Browse Source

Merge pull request #17 from godlikepanos/shader_program_resource

Reworking the material system. Introducing shader program resource
Panagiotis Christopoulos Charitos 8 years ago
parent
commit
514334cf36
91 changed files with 4153 additions and 6107 deletions
  1. 1 0
      .gitignore
  2. 1 1
      docs/doxyfile
  3. 68 0
      programs/FsParticles.ankiprog
  4. 184 0
      programs/MsGeneric.ankiprog
  5. 46 0
      samples/simple_scene/assets/ms_diffc_specc_roughc_metalc_normal0_emisc_subsc_par0.ankiprog
  6. 28 88
      samples/simple_scene/assets/room-material.ankimtl
  7. 28 117
      samples/sponza/assets/Material__57_001-material.ankimtl
  8. 28 117
      samples/sponza/assets/arch-material.ankimtl
  9. 28 117
      samples/sponza/assets/bricks-material.ankimtl
  10. 28 117
      samples/sponza/assets/ceiling-material.ankimtl
  11. 28 97
      samples/sponza/assets/chain-material.ankimtl
  12. 28 117
      samples/sponza/assets/column_a-material.ankimtl
  13. 28 117
      samples/sponza/assets/column_b-material.ankimtl
  14. 28 117
      samples/sponza/assets/column_c-material.ankimtl
  15. 28 117
      samples/sponza/assets/details-material.ankimtl
  16. 28 117
      samples/sponza/assets/fabric_a-material.ankimtl
  17. 28 117
      samples/sponza/assets/fabric_c-material.ankimtl
  18. 28 117
      samples/sponza/assets/fabric_d-material.ankimtl
  19. 28 117
      samples/sponza/assets/fabric_e-material.ankimtl
  20. 28 117
      samples/sponza/assets/fabric_f-material.ankimtl
  21. 15 66
      samples/sponza/assets/fire.ankimtl
  22. 28 117
      samples/sponza/assets/flagpole-material.ankimtl
  23. 28 117
      samples/sponza/assets/floor-material.ankimtl
  24. 28 117
      samples/sponza/assets/leaf-material.ankimtl
  25. 28 106
      samples/sponza/assets/lion-material.ankimtl
  26. 28 117
      samples/sponza/assets/lion_stand-material.ankimtl
  27. 28 117
      samples/sponza/assets/roof-material.ankimtl
  28. 2 2
      samples/sponza/assets/scene.lua
  29. 15 63
      samples/sponza/assets/smoke.ankimtl
  30. BIN
      samples/sponza/assets/sponza_05.ankimesh
  31. BIN
      samples/sponza/assets/sponza_06.ankimesh
  32. BIN
      samples/sponza/assets/sponza_34.ankimesh
  33. BIN
      samples/sponza/assets/sponza_36.ankimesh
  34. BIN
      samples/sponza/assets/sponza_379.ankimesh
  35. BIN
      samples/sponza/assets/sponza_381.ankimesh
  36. BIN
      samples/sponza/assets/sponza_68.ankimesh
  37. BIN
      samples/sponza/assets/sponza_69.ankimesh
  38. 28 117
      samples/sponza/assets/vase-material.ankimtl
  39. 28 117
      samples/sponza/assets/vase_hanging-material.ankimtl
  40. 28 117
      samples/sponza/assets/vase_round_001-material.ankimtl
  41. 28 97
      samples/sponza/assets/writings-material.ankimtl
  42. 2 2
      shaders/ClusterLightCommon.glsl
  43. 9 105
      shaders/FsCommonFrag.glsl
  44. 0 25
      shaders/FsCommonVert.glsl
  45. 15 163
      shaders/MsCommonFrag.glsl
  46. 10 73
      shaders/MsCommonVert.glsl
  47. 0 34
      shaders/MsFsCommon.glsl
  48. 13 0
      src/anki/Resource.h
  49. 8 8
      src/anki/gr/gl/ShaderImpl.cpp
  50. 23 0
      src/anki/gr/gl/ShaderProgramImpl.cpp
  51. 4 15
      src/anki/gr/gl/ShaderProgramImpl.h
  52. 8 8
      src/anki/gr/vulkan/ShaderImpl.cpp
  53. 1 1
      src/anki/input/InputSdl.cpp
  54. 2 2
      src/anki/misc/ConfigSet.cpp
  55. 68 82
      src/anki/misc/Xml.cpp
  56. 232 13
      src/anki/misc/Xml.h
  57. 183 204
      src/anki/renderer/Drawer.cpp
  58. 5 5
      src/anki/resource/Animation.cpp
  59. 1 1
      src/anki/resource/CollisionResource.cpp
  60. 2 1
      src/anki/resource/Common.h
  61. 376 282
      src/anki/resource/Material.cpp
  62. 105 210
      src/anki/resource/Material.h
  63. 0 985
      src/anki/resource/MaterialLoader.cpp
  64. 0 199
      src/anki/resource/MaterialLoader.h
  65. 1 1
      src/anki/resource/Model.cpp
  66. 1 1
      src/anki/resource/Model.h
  67. 4 4
      src/anki/resource/ParticleEmitterResource.cpp
  68. 12 9
      src/anki/resource/RenderingKey.h
  69. 2 0
      src/anki/resource/ResourceManager.cpp
  70. 2 1
      src/anki/resource/ResourceManager.h
  71. 1 1
      src/anki/resource/ResourceObject.h
  72. 21 16
      src/anki/resource/ShaderLoader.cpp
  73. 16 4
      src/anki/resource/ShaderLoader.h
  74. 957 0
      src/anki/resource/ShaderProgramResource.cpp
  75. 384 0
      src/anki/resource/ShaderProgramResource.h
  76. 1 1
      src/anki/resource/TextureAtlas.cpp
  77. 6 54
      src/anki/scene/RenderComponent.cpp
  78. 10 71
      src/anki/scene/RenderComponent.h
  79. 2 2
      src/anki/scene/Visibility.cpp
  80. 2 2
      src/anki/scene/VisibilityInternal.h
  81. 12 0
      src/anki/util/DynamicArray.h
  82. 8 0
      src/anki/util/Functions.h
  83. 96 15
      src/anki/util/String.cpp
  84. 59 9
      src/anki/util/String.h
  85. 5 0
      src/anki/util/StringList.h
  86. 0 142
      tests/resource/MaterialLoader.cpp
  87. 3 3
      tests/util/String.cpp
  88. 9 1
      tools/format_source.sh
  89. 163 372
      tools/scene/ExporterMaterial.cpp
  90. 4 3
      tools/scene/Main.cpp
  91. 273 0
      tools/shader/generate_program_permutations.py

+ 1 - 0
.gitignore

@@ -10,5 +10,6 @@
 !*.xml
 !*.md
 !*.txt
+!*.ankiprog
 !CMakeLists.txt
 build*/*

+ 1 - 1
docs/doxyfile

@@ -330,7 +330,7 @@ IDL_PROPERTY_SUPPORT   = YES
 # all members of a group must be documented explicitly.
 # The default value is: NO.
 
-DISTRIBUTE_GROUP_DOC   = NO
+DISTRIBUTE_GROUP_DOC   = YES
 
 # Set the SUBGROUPING tag to YES to allow class member groups of the same type
 # (for instance a group of public functions) to be put as a subgroup of that

+ 68 - 0
programs/FsParticles.ankiprog

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<shaderProgram>
+	<mutators>
+		<mutator name="ANIMATED_TEXTURE" values="0 1"/>
+		<mutator name="LIGHT" values="0 1"/>
+	</mutators>
+
+	<shaders>
+		<shader type="vert">
+			<inputs>
+				<input name="mvp" type="mat4"/>
+				<input name="cameraRotMat" type="mat3"/>
+				<input name="viewMat" type="mat4"/>
+			</inputs>
+
+			<source><![CDATA[#include "shaders/FsCommonVert.glsl"
+void main() 
+{
+	particle(mvp, cameraRotMat, viewMat);
+}
+			]]>
+			</source>
+		</shader>
+
+		<shader type="frag">
+			<inputs>
+				<input name="diffuseMap" type="sampler2D">
+					<mutators>
+						<mutator name="ANIMATED_TEXTURE" values="0"/>
+					</mutators>
+				</input>
+				<input name="diffuseMapArr" type="sampler2DArray">
+					<mutators>
+						<mutator name="ANIMATED_TEXTURE" values="1"/>
+					</mutators>
+				</input>
+				<input name="animationPeriod" type="float" const="1">
+					<mutators>
+						<mutator name="ANIMATED_TEXTURE" values="1"/>
+					</mutators>
+				</input>
+				<input name="colorScale" type="vec4" const="1"/>
+				<input name="colorBias" type="vec4" const="1"/>
+			</inputs>
+
+			<source><![CDATA[#include "shaders/FsCommonFrag.glsl"
+void main() 
+{
+#if ANIMATED_TEXTURE == 1
+	vec4 texCol = readAnimatedTextureRgba(diffuseMapArr, animationPeriod, in_uv, anki_u_time);
+#else
+	vec4 texCol = texture(diffuseMap, in_uv);
+#endif
+
+#if LIGHT
+	texCol.rgb = computeLightColor(texCol.rgb);
+#endif
+	
+	vec4 colScale = colorScale;
+	colScale.a *= in_alpha;
+	particleAlpha(texCol, colScale, colorBias);
+}
+			]]>
+			</source>
+		</shader>
+	</shaders>
+</shaderProgram>
+

+ 184 - 0
programs/MsGeneric.ankiprog

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<shaderProgram>
+	<mutators>
+		<mutator name="INSTANCE_COUNT" values="1 2 4 8 16 32 64"/>
+		<mutator name="LOD" values="0 1"/>
+		<mutator name="PASS" values="0 1"/>
+		<mutator name="DIFFUSE_TEX" values="0 1"/>
+		<mutator name="SPECULAR_TEX" values="0 1"/>
+		<mutator name="ROUGHNESS_TEX" values="0 1"/>
+		<mutator name="METAL_TEX" values="0 1"/>
+		<mutator name="NORMAL_TEX" values="0 1"/>
+		<mutator name="PARALLAX" values="0 1"/>
+		<mutator name="EMISSIVE_TEX" values="0 1"/>
+	</mutators>
+
+	<shaders>
+		<shader type="vert">
+			<inputs>
+				<input name="mvp" type="mat4" instanceCount="INSTANCE_COUNT"/>
+				<input name="normalMat" type="mat3" instanceCount="INSTANCE_COUNT">
+					<mutators>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="modelViewMat" type="mat4" instanceCount="INSTANCE_COUNT">
+					<mutators>
+						<mutator name="PASS" values="0"/>
+						<mutator name="PARALLAX" values="1"/>
+					</mutators>
+				</input>
+			</inputs>
+
+			<source><![CDATA[#include "shaders/MsCommonVert.glsl"
+void main() 
+{
+#if PASS == 0
+	positionUvNormalTangent(mvp, normalMat);
+	
+	#if PARALLAX
+		parallax(modelViewMat);
+	#endif
+#else
+	ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
+#endif
+}
+			]]>
+			</source>
+		</shader>
+
+		<shader type="frag">
+			<inputs>
+				<!-- Consts -->
+				<input name="diffColor" type="vec3" const="1">
+					<mutators>
+						<mutator name="DIFFUSE_TEX" values="0"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="specColor" type="vec3" const="1">
+					<mutators>
+						<mutator name="SPECULAR_TEX" values="0"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="roughness" type="float" const="1">
+					<mutators>
+						<mutator name="ROUGHNESS_TEX" values="0"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="metallic" type="float" const="1">
+					<mutators>
+						<mutator name="METAL_TEX" values="0"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="emission" type="vec3" const="1">
+					<mutators>
+						<mutator name="EMISSIVE_TEX" values="0"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="heightMapScale" type="float" const="1">
+					<mutators>
+						<mutator name="PARALLAX" values="1"/>
+						<mutator name="PASS" values="0"/>
+						<mutator name="LOD" values="0"/>
+					</mutators>
+				</input>
+				<input name="subsurface" type="float" const="1">
+					<mutators>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+
+				<!-- Textures -->
+				<input name="diffTex" type="sampler2D">
+					<mutators>
+						<mutator name="DIFFUSE_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="specTex" type="sampler2D">
+					<mutators>
+						<mutator name="SPECULAR_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="roughnessTex" type="sampler2D">
+					<mutators>
+						<mutator name="ROUGHNESS_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="metalTex" type="sampler2D">
+					<mutators>
+						<mutator name="METAL_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="normalTex" type="sampler2D">
+					<mutators>
+						<mutator name="NORMAL_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+				<input name="heightTex" type="sampler2D">
+					<mutators>
+						<mutator name="PARALLAX" values="1"/>
+						<mutator name="PASS" values="0"/>
+						<mutator name="LOD" values="0"/>
+					</mutators>
+				</input>
+				<input name="emissiveTex" type="sampler2D">
+					<mutators>
+						<mutator name="EMISSIVE_TEX" values="1"/>
+						<mutator name="PASS" values="0"/>
+					</mutators>
+				</input>
+			</inputs>
+
+			<source><![CDATA[#include "shaders/MsCommonFrag.glsl"
+void main()
+{
+#if PASS == 0
+	#if heightTex_DEFINED
+		vec2 uv =  computeTextureCoordParallax(heightTex, in_uv, heightMapScale);
+	#else
+		vec2 uv = in_uv;
+	#endif
+
+	#if diffTex_DEFINED
+		vec3 diffColor = texture(diffTex, uv).rgb;
+	#endif
+
+	#if specTex_DEFINED
+		vec3 specColor = texture(specTex, uv).rgb;
+	#endif
+
+	#if roughnessTex_DEFINED
+		float roughness = texture(roughnessTex, uv).r;
+	#endif
+
+	#if metalTex_DEFINED
+		float metallic = texture(metalTex, uv).r;
+	#endif
+
+	#if normalTex_DEFINED
+		vec3 normal = readNormalFromTexture(normalTex, uv);
+	#else
+		vec3 normal = normalize(in_normal);
+	#endif
+
+	#if emissiveTex_DEFINED
+		vec3 emission = texture(emissiveTex, uv).rgb;
+	#endif
+
+	writeRts(diffColor, normal, specColor, roughness, subsurface, emission, metallic);
+#endif
+}
+			]]></source>
+		</shader>
+	</shaders>
+</shaderProgram>

+ 46 - 0
samples/simple_scene/assets/ms_diffc_specc_roughc_metalc_normal0_emisc_subsc_par0.ankiprog

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by generate_program_permutations.py -->
+<shaderProgram>
+	<shaders>
+		<shader>
+			<type>vert</type>
+			<inputs>
+				<input><type>mat4</type><name>mvp</name></input>
+				<input><type>mat3</type><name>normalMat</name><depth>0</depth></input>
+
+			</inputs>
+			<source><![CDATA[#include "shaders/MsCommonVert.glsl"
+void main() {
+#if COLOR
+positionUvNormalTangent(mvp, normalMat);
+#else
+ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
+#endif
+
+}
+			]]></source>
+		</shader>
+		<shader>
+			<type>frag</type>
+			<inputs>
+				<input><type>vec3</type><name>diffColor</name><const>1</const><depth>0</depth></input>
+				<input><type>vec3</type><name>specColor</name><const>1</const><depth>0</depth></input>
+				<input><type>float</type><name>roughness</name><const>1</const><depth>0</depth></input>
+				<input><type>float</type><name>metallic</name><const>1</const><depth>0</depth></input>
+				<input><type>vec3</type><name>emission</name><const>1</const><depth>0</depth></input>
+				<input><type>float</type><name>subsurface</name><const>1</const><depth>0</depth></input>
+
+			</inputs>
+			<source><![CDATA[#include "shaders/MsCommonFrag.glsl"
+void main() {
+#if COLOR
+vec2 uv = in_uv;
+vec3 normal = in_normal;
+writeRts(diffColor, normal, specColor, roughness, subsurface, emission, metallic);
+#endif
+
+}
+			]]></source>
+		</shader>
+	</shaders>
+</shaderProgram>

+ 28 - 88
samples/simple_scene/assets/room-material.ankimtl

@@ -1,89 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.500000 0.500000 0.500000</value></input>
-				<input><type>float</type><name>uSpecularPower</name><value>0.097847</value></input>
-				<input><type>vec3</type><name>uDiffuseColor</name><value>0.799988 0.635542 0.640324</value></input>
-				 
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.0</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				 
-				
-				
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out0</argument>
-						<argument>uSpecularColor</argument>
-						<argument>uSpecularPower</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffColor" value="0.799988 0.635542 0.640324"/>
+		<input shaderInput="specColor" value="0.500000 0.500000 0.500000"/>
+		<input shaderInput="roughness" value="0.097847" />
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/Material__57_001-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.039993 0.039993</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/VasePlant_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/VasePlant_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/VasePlant_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.250000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/VasePlant_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.039993 0.039993"/>
+		<input shaderInput="roughnessTex" value="assets/VasePlant_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/VasePlant_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.250000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/arch-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Arch_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Arch_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Arch_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Arch_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Arch_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Arch_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/bricks-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Bricks_a_Roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Bricks_a_Albedo.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Bricks_a_Normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Bricks_a_Albedo.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Bricks_a_Roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Bricks_a_Normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/ceiling-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Ceiling_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Ceiling_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Ceiling_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Ceiling_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Ceiling_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Ceiling_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 97
samples/sponza/assets/chain-material.ankimtl

@@ -1,98 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.039993 0.039993</value></input>
-				<input><type>float</type><name>roughness</name><const>1</const><value>0.097847</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/ChainTexture_Albedo.ankitex</value></input>
-				 
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				 
-				
-				
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out0</argument>
-						<argument>uSpecularColor</argument>
-						<argument>roughness</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/ChainTexture_Albedo.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.039993 0.039993"/>
+		<input shaderInput="roughness" value="0.097847" />
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/column_a-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Column_a_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Column_a_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Column_a_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Column_a_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Column_a_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Column_a_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/column_b-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Column_b_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Column_b_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Column_b_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Column_b_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Column_b_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Column_b_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/column_c-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.037417 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Column_c_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Column_c_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Column_c_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Column_c_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.037417 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Column_c_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Column_c_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/details-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Details_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Details_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Details_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Details_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Details_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Details_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/fabric_a-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Fabric_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Fabric_Red_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Fabric_Red_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.100000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Fabric_Red_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Fabric_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Fabric_Red_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.100000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/fabric_c-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.000000 0.000000 0.000000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Curtain_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Curtain_Green_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Curtain_Red_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.250000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Curtain_Green_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Curtain_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Curtain_Red_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.250000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/fabric_d-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Fabric_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Fabric_Green_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Fabric_Red_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.100000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Fabric_Green_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Fabric_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Fabric_Red_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.100000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/fabric_e-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.006237 0.005484 0.005563</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Fabric_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Fabric_Blue_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Fabric_Red_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.100000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Fabric_Blue_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.006237 0.005484 0.005563"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Fabric_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Fabric_Red_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.100000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/fabric_f-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.039993 0.039993</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Curtain_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Curtain_Red_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Curtain_Red_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.250000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Curtain_Red_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.039993 0.039993"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Curtain_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Curtain_Red_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.250000"/>
+		
+	</inputs>
+</material>

+ 15 - 66
samples/sponza/assets/fire.ankimtl

@@ -1,69 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-
-<material>
-	<forwardShading>1</forwardShading>
-	<shadow>0</shadow>
-
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/FsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_cameraRotation</name></input>
-				<input><type>mat4</type><name>anki_viewMatrix</name></input>
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>particle</function>
-					<arguments>
-						<argument>anki_mvp</argument>
-						<argument>anki_cameraRotation</argument>
-						<argument>anki_viewMatrix</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/FsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>sampler2D</type><name>diffuseMap</name><value>assets/ember_mid.ankitex</value></input>
-				<input><type>vec4</type><name>mulCol</name><value>3. 3. 3. 1.</value><const>1</const></input>
-				<input><type>vec4</type><name>addCol</name><value>0 0 0 0</value><const>1</const></input>
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>1</id>
-					<returnType>float</returnType>
-					<function>getAlpha</function>
-				</operation>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>particleTextureAlpha</function>
-					<arguments>
-						<argument>diffuseMap</argument>
-						<argument>mulCol</argument>
-						<argument>addCol</argument>
-						<argument>out1</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>
+<material shaderProgram="programs/FsParticles.ankiprog" forwardShading="1" shadow="0">
+	<mutators>
+		<mutator name="ANIMATED_TEXTURE" value="0"/>
+		<mutator name="LIGHT" value="0"/>
+	</mutators>
+
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="cameraRotMat" builtin="CAMERA_ROTATION_MATRIX"/>
+		<input shaderInput="viewMat" builtin="VIEW_MATRIX"/>
+
+		<input shaderInput="diffuseMap" value="assets/ember_mid.ankitex"/>
+		<input shaderInput="colorScale" value="3 3 3 1.0"/>
+		<input shaderInput="colorBias" value="0 0 0 0"/>
+	</inputs>
 </material>
 
-

+ 28 - 117
samples/sponza/assets/flagpole-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_FlagPole_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_FlagPole_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_FlagPole_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_FlagPole_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_FlagPole_roughness.ankitex"/>
+		<input shaderInput="metallic" value="1.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_FlagPole_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/floor-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Column_b_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Floor_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Floor_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Floor_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Column_b_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Floor_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/leaf-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.037779 0.039480 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Thorn_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Thorn_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Thorn_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.100000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Thorn_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.037779 0.039480 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Thorn_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Thorn_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.100000"/>
+		
+	</inputs>
+</material>

+ 28 - 106
samples/sponza/assets/lion-material.ankimtl

@@ -1,107 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Lion_Roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Lion_Albedo.ankitex</value></input>
-				 
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				 
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out0</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Lion_Albedo.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Lion_Roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/lion_stand-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Background_Roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Background_Albedo.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Background_Normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Background_Albedo.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Background_Roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Background_Normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/roof-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.094086 0.094086 0.094086</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Sponza_Roof_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Roof_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Sponza_Roof_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Roof_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.094086 0.094086 0.094086"/>
+		<input shaderInput="roughnessTex" value="assets/Sponza_Roof_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Sponza_Roof_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 2 - 2
samples/sponza/assets/scene.lua

@@ -2689,8 +2689,8 @@ node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
 node = scene:newSpotLight("Lamp")
 lcomp = node:getSceneNodeBase():getLightComponent()
-lcomp:setDiffuseColor(Vec4.new(0.475606, 0.689217, 1.00085, 1))
-lcomp:setSpecularColor(Vec4.new(0.475606, 0.689217, 1.00085, 1))
+lcomp:setDiffuseColor(Vec4.new(0.951211, 1.37843, 2.00171, 1))
+lcomp:setSpecularColor(Vec4.new(0.951211, 1.37843, 2.00171, 1))
 lcomp:setInnerAngle(0.737402)
 lcomp:setOuterAngle(1.4748)
 lcomp:setDistance(79.5799)

+ 15 - 63
samples/sponza/assets/smoke.ankimtl

@@ -1,66 +1,18 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-
-<material>
-	<forwardShading>1</forwardShading>
-	<shadow>0</shadow>
-	<levelsOfDetail>2</levelsOfDetail>
-
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/FsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_cameraRotation</name></input>
-				<input><type>mat4</type><name>anki_viewMatrix</name></input>
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>particle</function>
-					<arguments>
-						<argument>anki_mvp</argument>
-						<argument>anki_cameraRotation</argument>
-						<argument>anki_viewMatrix</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/FsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>sampler2D</type><name>diffuseMap</name><value>assets/smoke.ankitex</value></input>
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>1</id>
-					<returnType>float</returnType>
-					<function>getAlpha</function>
-				</operation>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>particleTextureAlphaLight</function>
-					<arguments>
-						<argument>diffuseMap</argument>
-						<argument>out1</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>
+<material shaderProgram="programs/FsParticles.ankiprog" forwardShading="1" shadow="0">
+	<mutators>
+		<mutator name="ANIMATED_TEXTURE" value="0"/>
+		<mutator name="LIGHT" value="1"/>
+	</mutators>
+
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="cameraRotMat" builtin="CAMERA_ROTATION_MATRIX"/>
+		<input shaderInput="viewMat" builtin="VIEW_MATRIX"/>
+
+		<input shaderInput="diffuseMap" value="assets/smoke.ankitex"/>
+		<input shaderInput="colorScale" value="1.0 1.0 1.0 1.0"/>
+		<input shaderInput="colorBias" value="0 0 0 0"/>
+	</inputs>
 </material>
 
-

BIN
samples/sponza/assets/sponza_05.ankimesh


BIN
samples/sponza/assets/sponza_06.ankimesh


BIN
samples/sponza/assets/sponza_34.ankimesh


BIN
samples/sponza/assets/sponza_36.ankimesh


BIN
samples/sponza/assets/sponza_379.ankimesh


BIN
samples/sponza/assets/sponza_381.ankimesh


BIN
samples/sponza/assets/sponza_68.ankimesh


BIN
samples/sponza/assets/sponza_69.ankimesh


+ 28 - 117
samples/sponza/assets/vase-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/Vase_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Vase_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/Vase_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Vase_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughnessTex" value="assets/Vase_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/Vase_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/vase_hanging-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.039993 0.039993</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/VaseHanging_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/VaseHanging_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/VaseHanging_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/VaseHanging_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.039993 0.039993"/>
+		<input shaderInput="roughnessTex" value="assets/VaseHanging_roughness.ankitex"/>
+		<input shaderInput="metallic" value="1.000000"/>
+		<input shaderInput="normalTex" value="assets/VaseHanging_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 117
samples/sponza/assets/vase_round_001-material.ankimtl

@@ -1,118 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.039993 0.039993 0.039993</value></input>
-				<input><type>sampler2D</type><name>roughness</name><value>assets/VaseRound_roughness.ankitex</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/VaseRound_diffuse.ankitex</value></input>
-				<input><type>sampler2D</type><name>uNormal</name><value>assets/VaseRound_normal.ankitex</value></input>
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>60</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>roughness</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out20</argument>
-						<argument>uSpecularColor</argument>
-						<argument>out60</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/VaseRound_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.039993 0.039993 0.039993"/>
+		<input shaderInput="roughnessTex" value="assets/VaseRound_roughness.ankitex"/>
+		<input shaderInput="metallic" value="0.000000"/>
+		<input shaderInput="normalTex" value="assets/VaseRound_normal.ankitex"/>
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 28 - 97
samples/sponza/assets/writings-material.ankimtl

@@ -1,98 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<material>
-	<programs>
-		<program>
-			<type>vert</type>
-			<includes>
-				<include>shaders/MsCommonVert.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>mat4</type><name>anki_mvp</name></input>
-				<input><type>mat3</type><name>anki_n</name><inShadow>0</inShadow></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>void</returnType>
-					<function>writePositionAndUv</function>
-					<arguments><argument>anki_mvp</argument></arguments>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>void</returnType>
-					<function>writeNormalAndTangent</function>
-					<arguments><argument>anki_n</argument></arguments>
-				</operation>
-				 
-			</operations>
-		</program>
-		<program>
-			<type>frag</type>
-
-			<includes>
-				<include>shaders/MsCommonFrag.glsl</include>
-			</includes>
-
-			<inputs>
-				<input><type>vec3</type><name>uSpecularColor</name><value>0.040000 0.040000 0.040000</value></input>
-				<input><type>float</type><name>roughness</name><const>1</const><value>0.097847</value></input>
-				<input><type>sampler2D</type><name>uDiffuseColor</name><value>assets/Sponza_Ceiling_diffuse.ankitex</value></input>
-				 
-				<input><type>float</type><name>subsurface</name><const>1</const><value>0.000000</value></input>
-				<input><type>float</type><name>emission</name><value>0.000000</value><const>1</const></input>
-				<input><type>float</type><name>metallic</name><value>0.000000</value><const>1</const></input>
-				 
-			</inputs>
-
-			<operations>
-				<operation>
-					<id>0</id>
-					<returnType>vec3</returnType>
-					<function>getNormal</function>
-				</operation>
-				<operation>
-					<id>1</id>
-					<returnType>vec4</returnType>
-					<function>getTangent</function>
-				</operation>
-				<operation>
-					<id>2</id>
-					<returnType>vec2</returnType>
-					<function>getTextureCoord</function>
-				</operation>
-				 
-				
-				<operation>
-					<id>10</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>uDiffuseColor</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>
-				 
-				
-				
-				
-				
-				<operation>
-					<id>100</id>
-					<returnType>void</returnType>
-					<function>writeRts</function>
-					<arguments>
-						<argument>out10</argument>
-						<argument>out0</argument>
-						<argument>uSpecularColor</argument>
-						<argument>roughness</argument>
-						<argument>subsurface</argument>
-						<argument>emission</argument>
-						<argument>metallic</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>	</material>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		
+	
+		<input shaderInput="diffTex" value="assets/Sponza_Ceiling_diffuse.ankitex"/>
+		<input shaderInput="specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderInput="roughness" value="0.097847" />
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 2 - 2
shaders/ClusterLightCommon.glsl

@@ -95,7 +95,7 @@ layout(ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING), std140, row_major) unifor
 		readFirstInvocationARB(u_lightingUniforms.prevViewProjMat[2]),                                                 \
 		readFirstInvocationARB(u_lightingUniforms.prevViewProjMat[3]))
 
-#ifdef FRAGMENT_SHADER
+#ifdef ANKI_FRAGMENT_SHADER
 
 layout(ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING + 1), std140) uniform u1_
 {
@@ -140,6 +140,6 @@ layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 3)) uniform samplerCubeAr
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 4)) uniform sampler2D u_integrationLut;
 #endif
 
-#endif // FRAGMENT_SHADER
+#endif // ANKI_FRAGMENT_SHADER
 
 #endif

+ 9 - 105
shaders/FsCommonFrag.glsl

@@ -23,96 +23,25 @@ layout(ANKI_TEX_BINDING(1, 0)) uniform sampler2D anki_msDepthRt;
 #define anki_u_time u_time
 #define RENDERER_SIZE (u_lightingUniforms.rendererSizeTimePad1.xy * 0.5)
 
+// Varyings
 layout(location = 0) flat in float in_alpha;
 layout(location = 1) in vec2 in_uv;
 layout(location = 2) in vec3 in_posViewSpace;
 
 layout(location = 0) out vec4 out_color;
 
-#if PASS == COLOR
-#define texture_DEFINED
-#endif
-
-#define getAlpha_DEFINED
-float getAlpha()
-{
-	return in_alpha;
-}
-
-#define getPointCoord_DEFINED
-#define getPointCoord() gl_PointCoord
-
-#if PASS == COLOR
-#define writeGBuffer_DEFINED
 void writeGBuffer(in vec4 color)
 {
 	out_color = vec4(color.rgb, 1.0 - color.a);
 }
-#endif
-
-#if PASS == COLOR
-#define particleAlpha_DEFINED
-void particleAlpha(in sampler2D tex, in float alpha)
-{
-	vec4 color = texture(tex, gl_PointCoord);
-	color.a *= alpha;
-	writeGBuffer(color);
-}
-#endif
-
-#if PASS == COLOR
-#define particleSoftTextureAlpha_DEFINED
-void particleSoftTextureAlpha(in sampler2D depthMap, in sampler2D tex, in float alpha)
-{
-	vec2 screenSize = 1.0 / RENDERER_SIZE;
-
-	float depth = texture(depthMap, gl_FragCoord.xy * screenSize).r;
-
-	float delta = depth - gl_FragCoord.z;
-	float softalpha = clamp(delta * 50.0, 0.0, 1.0);
-
-	vec4 color = texture(tex, gl_PointCoord);
-	color.a *= alpha;
-	// color.a *= softalpha;
-
-	writeGBuffer(color);
-}
-#endif
-
-#if PASS == COLOR
-#define particleTextureAlpha_DEFINED
-void particleTextureAlpha(in sampler2D tex, vec4 mulColor, vec4 addColor, in float alpha)
-{
-	vec4 color = texture(tex, in_uv) * mulColor + addColor;
-	color.a *= alpha;
-
-	writeGBuffer(color);
-}
-#endif
 
-#if PASS == COLOR
-#define particleSoftColorAlpha_DEFINED
-void particleSoftColorAlpha(in sampler2D depthMap, in vec3 icolor, in float alpha)
+vec4 readAnimatedTextureRgba(sampler2DArray tex, float period, vec2 uv, float time)
 {
-	vec2 screenSize = 1.0 / RENDERER_SIZE;
-
-	float depth = texture(depthMap, gl_FragCoord.xy * screenSize).r;
-
-	float delta = depth - gl_FragCoord.z;
-	float softalpha = clamp(delta * 50.0, 0.0, 1.0);
-
-	vec2 pix = (1.0 - abs(gl_PointCoord * 2.0 - 1.0));
-	float roundFactor = pix.x * pix.y;
-
-	vec4 color;
-	color.rgb = icolor;
-	color.a = alpha * softalpha * roundFactor;
-	writeGBuffer(color);
+	float layerCount = float(textureSize(tex, 0).z);
+	float layer = mod(time * layerCount / period, layerCount);
+	return texture(tex, vec3(uv, layer));
 }
-#endif
 
-#if PASS == COLOR
-#define computeLightColor_DEFINED
 vec3 computeLightColor(vec3 diffCol)
 {
 	vec3 outColor = vec3(0.0);
@@ -187,42 +116,18 @@ vec3 computeLightColor(vec3 diffCol)
 
 	return outColor;
 }
-#endif
-
-#if PASS == COLOR
-#define particleTextureAlphaLight_DEFINED
-void particleTextureAlphaLight(in sampler2D tex, in float alpha)
-{
-	vec4 color = texture(tex, in_uv);
-	color.a *= alpha;
-
-	color.rgb = computeLightColor(color.rgb);
-
-	writeGBuffer(color);
-}
-#endif
 
-#if PASS == COLOR
-#define particleAnimatedTextureAlphaLight_DEFINED
-void particleAnimatedTextureAlphaLight(sampler2DArray tex, float alpha, float layerCount, float period)
+void particleAlpha(vec4 color, vec4 scaleColor, vec4 biasColor)
 {
-	vec4 color = readAnimatedTextureRgba(tex, layerCount, period, in_uv, anki_u_time);
-
-	color.rgb = computeLightColor(color.rgb);
-
-	color.a *= alpha;
-	writeGBuffer(color);
+	writeGBuffer(color * scaleColor + biasColor);
 }
-#endif
 
-#if PASS == COLOR
-#define fog_DEFINED
-void fog(in sampler2D depthMap, in vec3 color, in float fogScale)
+void fog(vec3 color, float fogScale)
 {
 	const vec2 screenSize = 1.0 / RENDERER_SIZE;
 
 	vec2 texCoords = gl_FragCoord.xy * screenSize;
-	float depth = texture(depthMap, texCoords).r;
+	float depth = texture(anki_msDepthRt, texCoords).r;
 	float diff;
 
 	if(depth < 1.0)
@@ -241,6 +146,5 @@ void fog(in sampler2D depthMap, in vec3 color, in float fogScale)
 
 	writeGBuffer(vec4(color, diff * fogScale));
 }
-#endif
 
 #endif

+ 0 - 25
shaders/FsCommonVert.glsl

@@ -34,25 +34,6 @@ out gl_PerVertex
 	vec4 gl_Position;
 };
 
-#define setPositionVec3_DEFINED
-void setPositionVec3(in vec3 pos)
-{
-	ANKI_WRITE_POSITION(vec4(pos, 1.0));
-}
-
-#define setPositionVec4_DEFINED
-void setPositionVec4(in vec4 pos)
-{
-	ANKI_WRITE_POSITION(pos);
-}
-
-#define writePositionMvp_DEFINED
-void writePositionMvp(in mat4 mvp)
-{
-	ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
-}
-
-#define particle_DEFINED
 void particle(in mat4 mvp, in mat3 camRot, in mat4 viewMat)
 {
 	const vec2 POSITIONS[4] = vec2[](vec2(-0.5, -0.5), vec2(0.5, -0.5), vec2(-0.5, 0.5), vec2(0.5, 0.5));
@@ -66,10 +47,4 @@ void particle(in mat4 mvp, in mat3 camRot, in mat4 viewMat)
 	out_uv = POSITIONS[gl_VertexID] + 0.5;
 }
 
-#define writeAlpha_DEFINED
-void writeAlpha(in float alpha)
-{
-	out_alpha = alpha;
-}
-
 #endif

+ 15 - 163
shaders/MsCommonFrag.glsl

@@ -3,19 +3,17 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
+#ifndef ANKI_SHADERS_MS_COMMON_FRAG_GLSL
+#define ANKI_SHADERS_MS_COMMON_FRAG_GLSL
+
 #include "shaders/Pack.glsl"
 #include "shaders/MsFsCommon.glsl"
 
 //
 // Input
 //
-#if NVIDIA_LINK_ERROR_WORKAROUND
-layout(location = 0) in highp vec4 in_uv;
-#else
 layout(location = 0) in highp vec2 in_uv;
-#endif
 
-#if PASS == COLOR
 layout(location = 1) in mediump vec3 in_normal;
 layout(location = 2) in mediump vec4 in_tangent;
 #if CALC_BITANGENT_IN_VERT
@@ -24,193 +22,49 @@ layout(location = 3) in mediump vec3 in_bitangent;
 layout(location = 4) in mediump vec3 in_vertPosViewSpace;
 layout(location = 5) in mediump vec3 in_eyeTangentSpace; // Parallax
 layout(location = 6) in mediump vec3 in_normalTangentSpace; // Parallax
-#endif
 
 //
 // Output
 //
-#if PASS == COLOR
+#if PASS == 0
 layout(location = 0) out vec4 out_msRt0;
 layout(location = 1) out vec4 out_msRt1;
 layout(location = 2) out vec4 out_msRt2;
-#define out_msRt0_DEFINED
-#define out_msRt1_DEFINED
-#define out_msRt2_DEFINED
-#endif
-
-// Getter
-#if PASS == COLOR
-#define getNormal_DEFINED
-vec3 getNormal()
-{
-	return normalize(in_normal);
-}
-#endif
-
-// Getter
-#if PASS == COLOR
-#define getTangent_DEFINED
-vec4 getTangent()
-{
-	return in_tangent;
-}
-#endif
-
-// Getter
-#define getTextureCoord_DEFINED
-vec2 getTextureCoord()
-{
-#if NVIDIA_LINK_ERROR_WORKAROUND
-	return in_uv.xy;
-#else
-	return in_uv;
-#endif
-}
-
-// Getter
-#if PASS == COLOR
-#define getPositionViewSpace_DEFINED
-vec3 getPositionViewSpace()
-{
-#if TESSELLATION
-	return vec3(0.0);
-#else
-	return in_vertPosViewSpace;
-#endif
-}
 #endif
 
 // Do normal mapping
-#if PASS == COLOR
-#define readNormalFromTexture_DEFINED
-vec3 readNormalFromTexture(in vec3 normal, in vec4 tangent, in sampler2D map, in highp vec2 texCoords)
+vec3 readNormalFromTexture(sampler2D map, highp vec2 texCoords)
 {
-#if LOD > 0
-	return normalize(normal);
-#else
 	// First read the texture
 	vec3 nAtTangentspace = normalize((texture(map, texCoords).rgb - 0.5) * 2.0);
 
-	vec3 n = normal; // Assume that getNormal() is called
-	vec3 t = normalize(tangent.xyz);
+	vec3 n = normalize(in_normal);
+	vec3 t = normalize(in_tangent.xyz);
 #if CALC_BITANGENT_IN_VERT
 	vec3 b = normalize(in_bitangent.xyz);
 #else
-	vec3 b = cross(n, t) * tangent.w;
+	vec3 b = cross(n, t) * in_tangent.w;
 #endif
 
 	mat3 tbnMat = mat3(t, b, n);
 
 	return tbnMat * nAtTangentspace;
-#endif
 }
-#endif
-
-// Do normal mapping by combining normal maps
-#if PASS == COLOR
-#define combineNormalFromTextures_DEFINED
-vec3 combineNormalFromTextures(in vec3 normal,
-	in vec4 tangent,
-	in sampler2D map,
-	in sampler2D map2,
-	in highp vec2 texCoords,
-	in float texCoords2Scale)
-{
-#if LOD > 0
-	return normalize(normal);
-#else
-	// First read the textures
-	vec3 nAtTangentspace0 = normalize((texture(map, texCoords).rgb - 0.5) * 2.0);
-	vec3 nAtTangentspace1 = normalize((texture(map2, texCoords * texCoords2Scale).rgb - 0.5) * 2.0);
-
-	vec3 nAtTangentspace = (nAtTangentspace0 + nAtTangentspace1) / 2.0;
-
-	vec3 n = normal; // Assume that getNormal() is called
-	vec3 t = normalize(tangent.xyz);
-	vec3 b = cross(n, t) * tangent.w;
-
-	mat3 tbnMat = mat3(t, b, n);
-
-	return tbnMat * nAtTangentspace;
-#endif
-}
-#endif
-
-// Do environment mapping
-#if PASS == COLOR
-#define readEnvironmentColor_DEFINED
-vec3 readEnvironmentColor(in vec3 vertPosViewSpace, in vec3 normal, in sampler2D map)
-{
-	// In case of normal mapping I could play with vertex's normal but this gives better results and its allready
-	// computed
-
-	vec3 u = normalize(vertPosViewSpace);
-	vec3 r = reflect(u, normal);
-	r.z += 1.0;
-	float m = 2.0 * length(r);
-	vec2 semTexCoords = r.xy / m + 0.5;
-
-	vec3 semCol = texture(map, semTexCoords).rgb;
-	return semCol;
-}
-#endif
 
 // Using a 4-channel texture and a tolerance discard the fragment if the texture's alpha is less than the tolerance
-#define readTextureRgbAlphaTesting_DEFINED
-vec3 readTextureRgbAlphaTesting(in sampler2D map, in highp vec2 texCoords, in float tolerance)
+vec3 readTextureRgbAlphaTesting(sampler2D map, in highp vec2 texCoords, float tolerance)
 {
-#if PASS == COLOR
 	vec4 col = vec4(texture(map, texCoords));
 	if(col.a < tolerance)
 	{
 		discard;
 	}
-	return col.rgb;
-#else // Depth
-#if LOD > 0
-	return vec3(0.0);
-#else
-	float a = float(texture(map, texCoords).a);
-	if(a < tolerance)
-	{
-		discard;
-	}
-	return vec3(0.0);
-#endif
-#endif
-}
-
-// Just read the RGB color from texture
-#if PASS == COLOR
-#define readRgbFromTexture_DEFINED
-vec3 readRgbFromTexture(in sampler2D tex, in highp vec2 texCoords)
-{
-	return vec3(texture(tex, texCoords)).rgb;
-}
-#endif
-
-// Just read the RGB color from cube texture
-#if PASS == COLOR
-#define readRgbFromCubeTexture_DEFINED
-vec3 readRgbFromCubeTexture(in samplerCube tex, in mediump vec3 texCoord)
-{
-	return texture(tex, texCoord).rgb;
-}
-#endif
 
-// Just read the R color from texture
-#if PASS == COLOR
-#define readRFromTexture_DEFINED
-float readRFromTexture(in sampler2D tex, in highp vec2 texCoords)
-{
-	return vec3(texture(tex, texCoords)).r;
+	return col.rgb;
 }
-#endif
 
-#define computeTextureCoordParallax_DEFINED
 vec2 computeTextureCoordParallax(in sampler2D heightMap, in vec2 uv, in float heightMapScale)
 {
-#if PASS == COLOR && LOD == 0
 	const uint MAX_SAMPLES = 25;
 	const uint MIN_SAMPLES = 1;
 	const float MAX_EFFECTIVE_DISTANCE = 32.0;
@@ -275,20 +129,16 @@ vec2 computeTextureCoordParallax(in sampler2D heightMap, in vec2 uv, in float he
 	}
 
 	return uv + crntOffset;
-#else
-	return uv;
-#endif
 }
 
 // Write the data to FAIs
-#if PASS == COLOR
-#define writeRts_DEFINED
+#if PASS == 0
 void writeRts(in vec3 diffColor, // from 0 to 1
 	in vec3 normal,
 	in vec3 specularColor,
 	in float roughness,
 	in float subsurface,
-	in float emission,
+	in vec3 emission,
 	in float metallic)
 {
 	GbufferInfo g;
@@ -297,8 +147,10 @@ void writeRts(in vec3 diffColor, // from 0 to 1
 	g.specular = specularColor;
 	g.roughness = roughness;
 	g.subsurface = subsurface;
-	g.emission = emission;
+	g.emission = (emission.r + emission.g + emission.b) / 3.0;
 	g.metallic = metallic;
 	writeGBuffer(g, out_msRt0, out_msRt1, out_msRt2);
 }
 #endif
+
+#endif

+ 10 - 73
shaders/MsCommonVert.glsl

@@ -13,14 +13,8 @@
 //
 layout(location = POSITION_LOCATION) in highp vec3 in_position;
 layout(location = TEXTURE_COORDINATE_LOCATION) in highp vec2 in_uv;
-
-#if PASS == COLOR || TESSELLATION
 layout(location = NORMAL_LOCATION) in mediump vec4 in_normal;
-#endif
-
-#if PASS == COLOR
 layout(location = TANGENT_LOCATION) in mediump vec4 in_tangent;
-#endif
 
 //
 // Output
@@ -30,17 +24,8 @@ out gl_PerVertex
 	vec4 gl_Position;
 };
 
-#if NVIDIA_LINK_ERROR_WORKAROUND
-layout(location = 0) out highp vec4 out_uv;
-#else
 layout(location = 0) out highp vec2 out_uv;
-#endif
-
-#if PASS == COLOR || TESSELLATION
 layout(location = 1) out mediump vec3 out_normal;
-#endif
-
-#if PASS == COLOR
 layout(location = 2) out mediump vec4 out_tangent;
 #if CALC_BITANGENT_IN_VERT
 layout(location = 3) out mediump vec3 out_bitangent;
@@ -50,51 +35,11 @@ layout(location = 4) out mediump vec3 out_vertPosViewSpace;
 layout(location = 5) out mediump vec3 out_eyeTangentSpace; // Parallax
 layout(location = 6) out mediump vec3 out_normalTangentSpace; // Parallax
 
-#endif
-
-#define writePositionAndUv_DEFINED
-void writePositionAndUv(in mat4 mvp)
+void positionUvNormalTangent(mat4 mvp, mat3 normalMat)
 {
-#if PASS == DEPTH && LOD > 0
-// No tex coords for you
-#else
-
-#if NVIDIA_LINK_ERROR_WORKAROUND
-	out_uv = vec4(in_uv, 0.0, 0.0);
-#else
 	out_uv = in_uv;
-#endif
-
-#endif
-
-#if TESSELLATION
-	gl_Position = vec4(in_position, 1.0);
-#else
 	ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
-#endif
-}
 
-#if PASS == COLOR
-#define writeNormalAndTangent_DEFINED
-void writeNormalAndTangent(in mat3 normalMat)
-{
-
-#if TESSELLATION
-
-	// Passthrough
-	out_normal = in_normal.xyz;
-#if PASS == COLOR
-	out_tangent = in_tangent;
-
-#if CALC_BITANGENT_IN_VERT
-#error TODO
-#endif
-
-#endif
-
-#else
-
-#if PASS == COLOR
 	out_normal = normalMat * in_normal.xyz;
 	out_tangent.xyz = normalMat * in_tangent.xyz;
 	out_tangent.w = in_tangent.w;
@@ -102,36 +47,28 @@ void writeNormalAndTangent(in mat3 normalMat)
 #if CALC_BITANGENT_IN_VERT
 	out_bitangent = cross(out_normal, out_tangent.xyz) * in_tangent.w;
 #endif
-
-#endif
-
-// #if TESSELLATION
-#endif
 }
-#endif
 
-#if PASS == COLOR
-#define writeVertPosViewSpace_DEFINED
-void writeVertPosViewSpace(in mat4 modelViewMat)
+void vertPosViewSpace(in mat4 modelViewMat)
 {
 	out_vertPosViewSpace = vec3(modelViewMat * vec4(in_position, 1.0));
 }
-#endif
 
-#if PASS == COLOR
-#define writeParallax_DEFINED
-void writeParallax(in mat3 normalMat, in mat4 modelViewMat)
+void parallax(in mat4 modelViewMat)
 {
-	vec3 n = normalMat * in_normal.xyz;
-	vec3 t = normalMat * in_tangent.xyz;
+	vec3 n = out_normal;
+	vec3 t = out_tangent.xyz;
+#if CALC_BITANGENT_IN_VERT
+	vec3 b = out_bitangent;
+#else
 	vec3 b = cross(n, t) * in_tangent.w;
+#endif
 	mat3 invTbn = transpose(mat3(t, b, n));
 
-	writeVertPosViewSpace(modelViewMat);
+	vertPosViewSpace(modelViewMat);
 
 	out_eyeTangentSpace = invTbn * out_vertPosViewSpace;
 	out_normalTangentSpace = invTbn * n;
 }
-#endif
 
 #endif

+ 0 - 34
shaders/MsFsCommon.glsl

@@ -8,40 +8,6 @@
 
 #include "shaders/Common.glsl"
 
-// Misc
-#define COLOR 0
-#define DEPTH 1
-
 #define CALC_BITANGENT_IN_VERT 1
 
-// Generic functions because materials cannot use operators
-#define add_DEFINED
-#define add(a, b) ((a) + (b))
-
-#define mul_DEFINED
-#define mul(a, b) ((a) * (b))
-
-#define assign_DEFINED
-#define assign(a, b) ((a) = (b))
-
-#define vec4ToVec3_DEFINED
-#define vec4ToVec3(a) ((a).xyz)
-
-#define vec3ToVec4_DEFINED
-#define vec3ToVec4(a, w) (vec4((a), (w)))
-
-#define getW_DEFINED
-#define getW(a) ((a).w)
-
-#define setW_DEFINED
-#define setW(a, b) ((a).w = (b))
-
-// Read from animated texture
-#define readAnimatedTextureRgba_DEFINED
-vec4 readAnimatedTextureRgba(sampler2DArray tex, float layerCount, float period, vec2 uv, float time)
-{
-	float layer = mod(time * layerCount / period, layerCount);
-	return texture(tex, vec3(uv, layer));
-}
-
 #endif

+ 13 - 0
src/anki/Resource.h

@@ -6,7 +6,20 @@
 #pragma once
 
 #include <anki/resource/ResourceManager.h>
+#include <anki/resource/ParticleEmitterResource.h>
 #include <anki/resource/Script.h>
+#include <anki/resource/Skeleton.h>
+#include <anki/resource/TextureAtlas.h>
+#include <anki/resource/Material.h>
+#include <anki/resource/TextureResource.h>
+#include <anki/resource/ShaderResource.h>
+#include <anki/resource/GenericResource.h>
+#include <anki/resource/Mesh.h>
+#include <anki/resource/DummyRsrc.h>
+#include <anki/resource/Animation.h>
+#include <anki/resource/Model.h>
+#include <anki/resource/ShaderProgramResource.h>
+#include <anki/resource/CollisionResource.h>
 
 /// @defgroup resource Collection of resources and management
 

+ 8 - 8
src/anki/gr/gl/ShaderImpl.cpp

@@ -38,11 +38,11 @@ static const char* SHADER_HEADER = R"(#version %u %s
 #define ANKI_IMAGE_BINDING(set_, binding_) binding = set_ * %u + binding_
 #define ANKI_TBO_BINDING(set_, binding_) binding = %u + set_ * %u + binding_
 
-#if defined(FRAGMENT_SHADER)
+#if defined(ANKI_FRAGMENT_SHADER)
 #define ANKI_USING_FRAG_COORD(height_) vec4 anki_fragCoord = gl_FragCoord;
 #endif
 
-#if defined(VERTEX_SHADER)
+#if defined(ANKI_VERTEX_SHADER)
 #define ANKI_WRITE_POSITION(x_) gl_Position = x_
 #endif
 
@@ -70,12 +70,12 @@ Error ShaderImpl::init(ShaderType type, const CString& source)
 		GL_FRAGMENT_SHADER,
 		GL_COMPUTE_SHADER}};
 
-	static const Array<const char*, 6> shaderName = {{"VERTEX_SHADER",
-		"TESSELATION_CONTROL_SHADER",
-		"TESSELATION_EVALUATION_SHADER",
-		"GEOMETRY_SHADER",
-		"FRAGMENT_SHADER",
-		"COMPUTE_SHADER"}};
+	static const Array<const char*, 6> shaderName = {{"ANKI_VERTEX_SHADER",
+		"ANKI_TESSELATION_CONTROL_SHADER",
+		"ANKI_TESSELATION_EVALUATION_SHADER",
+		"ANKI_GEOMETRY_SHADER",
+		"ANKI_FRAGMENT_SHADER",
+		"ANKI_COMPUTE_SHADER"}};
 
 	m_type = type;
 	m_glType = gltype[U(type)];

+ 23 - 0
src/anki/gr/gl/ShaderProgramImpl.cpp

@@ -10,29 +10,51 @@
 namespace anki
 {
 
+static void deletePrograms(GLsizei n, const GLuint* progs)
+{
+	ANKI_ASSERT(n == 1);
+	ANKI_ASSERT(progs);
+	glDeleteProgram(*progs);
+}
+
+ShaderProgramImpl::ShaderProgramImpl(GrManager* manager)
+	: GlObject(manager)
+{
+}
+
+ShaderProgramImpl::~ShaderProgramImpl()
+{
+	destroyDeferred(deletePrograms);
+}
+
 Error ShaderProgramImpl::initGraphics(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
 {
 	m_glName = glCreateProgram();
 	ANKI_ASSERT(m_glName != 0);
 
 	glAttachShader(m_glName, vert->m_impl->getGlName());
+	m_shaders[ShaderType::VERTEX] = vert;
 
 	if(tessc)
 	{
 		glAttachShader(m_glName, tessc->m_impl->getGlName());
+		m_shaders[ShaderType::TESSELLATION_CONTROL] = tessc;
 	}
 
 	if(tesse)
 	{
 		glAttachShader(m_glName, tesse->m_impl->getGlName());
+		m_shaders[ShaderType::TESSELLATION_EVALUATION] = tesse;
 	}
 
 	if(geom)
 	{
 		glAttachShader(m_glName, geom->m_impl->getGlName());
+		m_shaders[ShaderType::GEOMETRY] = geom;
 	}
 
 	glAttachShader(m_glName, frag->m_impl->getGlName());
+	m_shaders[ShaderType::FRAGMENT] = frag;
 
 	return link(vert->m_impl->getGlName(), frag->m_impl->getGlName());
 }
@@ -43,6 +65,7 @@ Error ShaderProgramImpl::initCompute(ShaderPtr comp)
 	ANKI_ASSERT(m_glName != 0);
 
 	glAttachShader(m_glName, comp->m_impl->getGlName());
+	m_shaders[ShaderType::COMPUTE] = comp;
 
 	return link(0, 0);
 }

+ 4 - 15
src/anki/gr/gl/ShaderProgramImpl.h

@@ -13,32 +13,21 @@ namespace anki
 /// @addtogroup opengl
 /// @{
 
-static inline void deletePrograms(GLsizei n, const GLuint* progs)
-{
-	ANKI_ASSERT(n == 1);
-	ANKI_ASSERT(progs);
-	glDeleteProgram(*progs);
-}
-
 /// Shader program implementation.
 class ShaderProgramImpl : public GlObject
 {
 public:
-	ShaderProgramImpl(GrManager* manager)
-		: GlObject(manager)
-	{
-	}
+	ShaderProgramImpl(GrManager* manager);
 
-	~ShaderProgramImpl()
-	{
-		destroyDeferred(deletePrograms);
-	}
+	~ShaderProgramImpl();
 
 	ANKI_USE_RESULT Error initGraphics(
 		ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag);
 	ANKI_USE_RESULT Error initCompute(ShaderPtr comp);
 
 private:
+	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders;
+
 	ANKI_USE_RESULT Error link(GLuint vert, GLuint frag);
 };
 /// @}

+ 8 - 8
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -165,12 +165,12 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #define ANKI_SS_BINDING(set_, binding_) set = set_, binding = %u + binding_
 #define ANKI_IMAGE_BINDING(set_, binding_) set = set_, binding = %u + binding_
 
-#if defined(FRAGMENT_SHADER)
+#if defined(ANKI_FRAGMENT_SHADER)
 #define ANKI_USING_FRAG_COORD(h_) vec4 anki_fragCoord = \
 	vec4(gl_FragCoord.x, h_ - gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);
 #endif
 
-#if defined(VERTEX_SHADER)
+#if defined(ANKI_VERTEX_SHADER)
 #define ANKI_WRITE_POSITION(x_) gl_Position = x_; gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5
 #endif
 
@@ -235,12 +235,12 @@ Error ShaderImpl::init(ShaderType shaderType, const CString& source)
 	auto alloc = getAllocator();
 	StringAuto fullSrc(alloc);
 
-	static const Array<const char*, 6> shaderName = {{"VERTEX_SHADER",
-		"TESSELATION_CONTROL_SHADER",
-		"TESSELATION_EVALUATION_SHADER",
-		"GEOMETRY_SHADER",
-		"FRAGMENT_SHADER",
-		"COMPUTE_SHADER"}};
+	static const Array<const char*, 6> shaderName = {{"ANKI_VERTEX_SHADER",
+		"ANKI_TESSELATION_CONTROL_SHADER",
+		"ANKI_TESSELATION_EVALUATION_SHADER",
+		"ANKI_GEOMETRY_SHADER",
+		"ANKI_FRAGMENT_SHADER",
+		"ANKI_COMPUTE_SHADER"}};
 
 	fullSrc.sprintf(SHADER_HEADER,
 		&GPU_VENDOR_STR[getGrManagerImpl().getGpuVendor()][0],

+ 1 - 1
src/anki/input/InputSdl.cpp

@@ -87,7 +87,7 @@ Error Input::init(NativeWindow* nativeWindow)
 	MAP(SDLK_q, Q);
 	MAP(SDLK_r, R);
 	MAP(SDLK_s, S);
-	MAP(SDLK_y, T);
+	MAP(SDLK_t, T);
 	MAP(SDLK_u, U);
 	MAP(SDLK_v, V);
 	MAP(SDLK_w, W);

+ 2 - 2
src/anki/misc/ConfigSet.cpp

@@ -151,7 +151,7 @@ Error ConfigSet::loadFromFile(CString filename)
 		{
 			if(option.m_type == 1)
 			{
-				ANKI_CHECK(el.getF64(option.m_fVal));
+				ANKI_CHECK(el.getNumber(option.m_fVal));
 			}
 			else
 			{
@@ -251,7 +251,7 @@ Error ConfigSet::setFromCommandLineArguments(U cmdLineArgsCount, char* cmdLineAr
 			else
 			{
 				CString val(arg);
-				ANKI_CHECK(val.toF64(option->m_fVal));
+				ANKI_CHECK(val.toNumber(option->m_fVal));
 			}
 		}
 	}

+ 68 - 82
src/anki/misc/Xml.cpp

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/misc/Xml.h>
-#include <anki/util/StringList.h>
 #include <anki/util/File.h>
 #include <anki/util/Logger.h>
 
@@ -37,106 +36,74 @@ Error XmlElement::getText(CString& out) const
 	return err;
 }
 
-Error XmlElement::getI64(I64& out) const
+Error XmlElement::getMat3(Mat3& out) const
 {
-	Error err = check();
+	DynamicArrayAuto<F32> arr(m_alloc);
+	Error err = getNumbers(arr);
 
-	if(!err)
+	if(!err && arr.getSize() != 9)
 	{
-		const char* txt = m_el->GetText();
-		if(txt != nullptr)
-		{
-			err = CString(txt).toI64(out);
-		}
-		else
-		{
-			ANKI_MISC_LOGE("Failed to return int. Element: %s", m_el->Value());
-			err = ErrorCode::USER_DATA;
-		}
+		ANKI_MISC_LOGE("Expecting 9 elements for Mat3");
+		err = ErrorCode::USER_DATA;
 	}
 
-	return err;
-}
-
-Error XmlElement::getF64(F64& out) const
-{
-	Error err = check();
-
 	if(!err)
 	{
-		const char* txt = m_el->GetText();
-		if(txt != nullptr)
-		{
-			err = CString(txt).toF64(out);
-		}
-		else
+		for(U i = 0; i < 9 && !err; i++)
 		{
-			ANKI_MISC_LOGE("Failed to return float. Element: %s", m_el->Value());
-			err = ErrorCode::USER_DATA;
+			out[i] = arr[i];
 		}
 	}
 
+	if(err)
+	{
+		ANKI_MISC_LOGE("Failed to return Mat3. Element: %s", m_el->Value());
+	}
+
 	return err;
 }
 
-Error XmlElement::getFloats(DynamicArrayAuto<F64>& out) const
+Error XmlElement::getMat4(Mat4& out) const
 {
-	Error err = check();
-
-	const char* txt;
-	if(!err)
-	{
-		txt = m_el->GetText();
-		if(txt == nullptr)
-		{
-			err = ErrorCode::USER_DATA;
-		}
-	}
+	DynamicArrayAuto<F32> arr(m_alloc);
+	Error err = getNumbers(arr);
 
-	StringList list;
-	if(!err)
+	if(!err && arr.getSize() != 16)
 	{
-		list.splitString(m_alloc, txt, ' ');
+		ANKI_MISC_LOGE("Expecting 16 elements for Mat4");
+		err = ErrorCode::USER_DATA;
 	}
 
-	out = DynamicArrayAuto<F64>(m_alloc);
-
 	if(!err)
 	{
-		out.create(list.getSize());
-	}
-
-	auto it = list.getBegin();
-	for(U i = 0; i < out.getSize() && !err; i++)
-	{
-		err = it->toF64(out[i]);
-		++it;
+		for(U i = 0; i < 16 && !err; i++)
+		{
+			out[i] = arr[i];
+		}
 	}
 
 	if(err)
 	{
-		ANKI_MISC_LOGE("Failed to return floats. Element: %s", m_el->Value());
+		ANKI_MISC_LOGE("Failed to return Mat4. Element: %s", m_el->Value());
 	}
 
-	list.destroy(m_alloc);
-
 	return err;
 }
 
-Error XmlElement::getMat4(Mat4& out) const
+Error XmlElement::getVec2(Vec2& out) const
 {
-	DynamicArrayAuto<F64> arr(m_alloc);
-	Error err = getFloats(arr);
+	DynamicArrayAuto<F32> arr(m_alloc);
+	Error err = getNumbers(arr);
 
-	if(!err && arr.getSize() != 16)
+	if(!err && arr.getSize() != 2)
 	{
-		ANKI_MISC_LOGE("Expecting 16 elements for Mat4");
+		ANKI_MISC_LOGE("Expecting 2 elements for Vec2");
 		err = ErrorCode::USER_DATA;
 	}
 
 	if(!err)
 	{
-		for(U i = 0; i < 16 && !err; i++)
+		for(U i = 0; i < 2; i++)
 		{
 			out[i] = arr[i];
 		}
@@ -144,7 +111,7 @@ Error XmlElement::getMat4(Mat4& out) const
 
 	if(err)
 	{
-		ANKI_MISC_LOGE("Failed to return Mat4. Element: %s", m_el->Value());
+		ANKI_MISC_LOGE("Failed to return Vec2. Element: %s", m_el->Value());
 	}
 
 	return err;
@@ -152,8 +119,8 @@ Error XmlElement::getMat4(Mat4& out) const
 
 Error XmlElement::getVec3(Vec3& out) const
 {
-	DynamicArrayAuto<F64> arr(m_alloc);
-	Error err = getFloats(arr);
+	DynamicArrayAuto<F32> arr(m_alloc);
+	Error err = getNumbers(arr);
 
 	if(!err && arr.getSize() != 3)
 	{
@@ -179,8 +146,8 @@ Error XmlElement::getVec3(Vec3& out) const
 
 Error XmlElement::getVec4(Vec4& out) const
 {
-	DynamicArrayAuto<F64> arr(m_alloc);
-	Error err = getFloats(arr);
+	DynamicArrayAuto<F32> arr(m_alloc);
+	Error err = getNumbers(arr);
 
 	if(!err && arr.getSize() != 4)
 	{
@@ -236,7 +203,7 @@ Error XmlElement::getChildElement(const CString& name, XmlElement& out) const
 
 	if(!out)
 	{
-		ANKI_MISC_LOGE("Cannot find tag %s", &name[0]);
+		ANKI_MISC_LOGE("Cannot find tag \"%s\"", &name[0]);
 		err = ErrorCode::USER_DATA;
 	}
 
@@ -260,26 +227,45 @@ Error XmlElement::getNextSiblingElement(const CString& name, XmlElement& out) co
 
 Error XmlElement::getSiblingElementsCount(U32& out) const
 {
-	Error err = check();
-	if(!err)
+	ANKI_CHECK(check());
+	const tinyxml2::XMLElement* el = m_el;
+
+	I count = -1;
+	do
 	{
-		const tinyxml2::XMLElement* el = m_el;
+		el = el->NextSiblingElement(m_el->Name());
+		++count;
+	} while(el);
 
-		I count = -1;
-		do
-		{
-			el = el->NextSiblingElement(m_el->Name());
-			++count;
-		} while(el);
+	out = count;
+
+	return ErrorCode::NONE;
+}
+
+Error XmlElement::getAttributeTextOptional(const CString& name, CString& out, Bool& attribPresent) const
+{
+	ANKI_CHECK(check());
 
-		out = count;
+	const tinyxml2::XMLAttribute* attrib = m_el->FindAttribute(&name[0]);
+	if(!attrib)
+	{
+		attribPresent = false;
+		return ErrorCode::NONE;
+	}
+
+	attribPresent = true;
+
+	const char* value = attrib->Value();
+	if(value)
+	{
+		out = value;
 	}
 	else
 	{
-		out = 0;
+		out = CString();
 	}
 
-	return err;
+	return ErrorCode::NONE;
 }
 
 CString XmlDocument::XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
@@ -319,7 +305,7 @@ ANKI_USE_RESULT Error XmlDocument::getChildElement(const CString& name, XmlEleme
 
 	if(!out)
 	{
-		ANKI_MISC_LOGE("Cannot find tag %s", &name[0]);
+		ANKI_MISC_LOGE("Cannot find tag \"%s\"", &name[0]);
 		err = ErrorCode::USER_DATA;
 	}
 

+ 232 - 13
src/anki/misc/Xml.h

@@ -8,6 +8,7 @@
 #include <anki/misc/Common.h>
 #include <anki/util/String.h>
 #include <anki/util/DynamicArray.h>
+#include <anki/util/StringList.h>
 #include <anki/Math.h>
 #include <tinyxml2.h>
 #if !ANKI_TINYXML2
@@ -57,30 +58,56 @@ public:
 		return *this;
 	}
 
-	/// Return the text inside a tag
+	/// Return the text inside a tag. May return empty string.
 	ANKI_USE_RESULT Error getText(CString& out) const;
 
-	/// Return the text inside as an int
-	ANKI_USE_RESULT Error getI64(I64& out) const;
+	/// Return the text inside as a number.
+	template<typename T>
+	ANKI_USE_RESULT Error getNumber(T& out) const
+	{
+		ANKI_CHECK(check());
 
-	/// Return the text inside as a float
-	ANKI_USE_RESULT Error getF64(F64& out) const;
+		const char* txt = m_el->GetText();
+		if(txt != nullptr)
+		{
+			ANKI_CHECK(CString(txt).toNumber(out));
+		}
+		else
+		{
+			ANKI_MISC_LOGE("Failed to return number. Element: %s", m_el->Value());
+			return ErrorCode::USER_DATA;
+		}
 
-	/// Return the text inside as a float
-	ANKI_USE_RESULT Error getF32(F32& out) const
-	{
-		F64 outd;
-		ANKI_CHECK(getF64(outd));
-		out = outd;
 		return ErrorCode::NONE;
 	}
 
-	/// Get a number of floats
-	ANKI_USE_RESULT Error getFloats(DynamicArrayAuto<F64>& out) const;
+	/// Get a number of numbers.
+	template<typename T>
+	ANKI_USE_RESULT Error getNumbers(DynamicArrayAuto<T>& out) const
+	{
+		CString txt;
+		ANKI_CHECK(getText(txt));
+
+		if(txt)
+		{
+			return parseNumbers(txt, out);
+		}
+		else
+		{
+			out = DynamicArrayAuto<T>(m_alloc);
+			return ErrorCode::NONE;
+		}
+	}
 
 	/// Return the text inside as a Mat4
 	ANKI_USE_RESULT Error getMat4(Mat4& out) const;
 
+	/// Return the text inside as a Mat3
+	ANKI_USE_RESULT Error getMat3(Mat3& out) const;
+
+	/// Return the text inside as a Vec2
+	ANKI_USE_RESULT Error getVec2(Vec2& out) const;
+
 	/// Return the text inside as a Vec3
 	ANKI_USE_RESULT Error getVec3(Vec3& out) const;
 
@@ -100,11 +127,203 @@ public:
 	/// @note The sibling elements share the same name.
 	ANKI_USE_RESULT Error getSiblingElementsCount(U32& out) const;
 
+	/// @name Get attributes optional
+	/// @{
+
+	/// Get value of a string attribute. May return empty string.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	ANKI_USE_RESULT Error getAttributeTextOptional(const CString& name, CString& out, Bool& attribPresent) const;
+
+	/// Get the attribute's value as a series of numbers.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumbersOptional(
+		const CString& name, DynamicArrayAuto<T>& out, Bool& attribPresent) const
+	{
+		CString txtVal;
+		ANKI_CHECK(getAttributeTextOptional(name, txtVal, attribPresent));
+
+		if(txtVal && attribPresent)
+		{
+			return parseNumbers(txtVal, out);
+		}
+		else
+		{
+			return ErrorCode::NONE;
+		}
+	}
+
+	/// Get the attribute's value as a number.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumberOptional(const CString& name, T& out, Bool& attribPresent) const
+	{
+		DynamicArrayAuto<T> arr(m_alloc);
+		ANKI_CHECK(getAttributeNumbersOptional(name, arr, attribPresent));
+
+		if(attribPresent)
+		{
+			if(arr.getSize() != 1)
+			{
+				ANKI_MISC_LOGE("Expecting one element for attrib %s", &name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			out = arr[0];
+		}
+
+		return ErrorCode::NONE;
+	}
+
+	/// Get the attribute's value as a vector.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeVectorOptional(const CString& name, T& out, Bool& attribPresent) const
+	{
+		DynamicArrayAuto<F32> arr(m_alloc);
+		ANKI_CHECK(getAttributeNumbersOptional(name, arr, attribPresent));
+
+		if(attribPresent)
+		{
+			if(arr.getSize() != sizeof(T) / sizeof(out[0]))
+			{
+				ANKI_MISC_LOGE("Expecting %u elements for attrib %s", sizeof(T) / sizeof(out[0]), &name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			U count = 0;
+			for(F32 v : arr)
+			{
+				out[count++] = v;
+			}
+		}
+
+		return ErrorCode::NONE;
+	}
+
+	/// Get the attribute's value as a matrix.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	/// @param attribPresent True if the attribute exists. If it doesn't the @a out is undefined.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeMatrixOptional(const CString& name, T& out, Bool& attribPresent) const
+	{
+		return getAttributeVectorOptional(name, out, attribPresent);
+	}
+	/// @}
+
+	/// @name Get attributes
+	/// @{
+
+	/// Get value of a string attribute. May return empty string.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	ANKI_USE_RESULT Error getAttributeText(const CString& name, CString& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeTextOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a series of numbers.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumbers(const CString& name, DynamicArrayAuto<T>& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeNumbersOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a number.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeNumber(const CString& name, T& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeNumberOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a vector.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeVector(const CString& name, T& out) const
+	{
+		Bool found;
+		ANKI_CHECK(getAttributeVectorOptional(name, out, found));
+		return throwAttribNotFoundError(name, found);
+	}
+
+	/// Get the attribute's value as a matrix.
+	/// @param name The name of the attribute.
+	/// @param out The value of the attribute.
+	template<typename T>
+	ANKI_USE_RESULT Error getAttributeMatrix(const CString& name, T& out) const
+	{
+		return getAttributeVector(name, out);
+	}
+	/// @}
+
 private:
 	const tinyxml2::XMLElement* m_el;
 	GenericMemoryPoolAllocator<U8> m_alloc;
 
 	ANKI_USE_RESULT Error check() const;
+
+	template<typename T>
+	ANKI_USE_RESULT Error parseNumbers(CString txt, DynamicArrayAuto<T>& out) const
+	{
+		ANKI_ASSERT(txt);
+		ANKI_ASSERT(m_el);
+
+		StringListAuto list(m_alloc);
+		list.splitString(txt, ' ');
+
+		out = DynamicArrayAuto<T>(m_alloc);
+		out.create(list.getSize());
+
+		Error err = ErrorCode::NONE;
+		auto it = list.getBegin();
+		auto end = list.getEnd();
+		U i = 0;
+		while(it != end && !err)
+		{
+			err = it->toNumber(out[i++]);
+			++it;
+		}
+
+		if(err)
+		{
+			ANKI_MISC_LOGE("Failed to parse floats. Element: %s", m_el->Value());
+		}
+
+		return err;
+	}
+
+	ANKI_USE_RESULT Error throwAttribNotFoundError(CString attrib, Bool found) const
+	{
+		if(!found)
+		{
+			ANKI_MISC_LOGE("Attribute not found \"%s\"", &attrib[0]);
+			return ErrorCode::USER_DATA;
+		}
+		else
+		{
+			return ErrorCode::NONE;
+		}
+	}
 };
 
 /// XML document

+ 183 - 204
src/anki/renderer/Drawer.cpp

@@ -18,9 +18,43 @@
 namespace anki
 {
 
+class CompleteRenderingBuildInfo
+{
+public:
+	F32 m_flod = 0.0;
+	RenderComponent* m_rc = nullptr;
+	RenderingBuildInfoIn m_in;
+	RenderingBuildInfoOut m_out;
+};
+
+/// Drawer's context
+class DrawContext
+{
+public:
+	Pass m_pass;
+	Mat4 m_viewMat;
+	Mat4 m_viewProjMat;
+	CommandBufferPtr m_cmdb;
+
+	const VisibleNode* m_visibleNode = nullptr;
+
+	Array<Mat4, MAX_INSTANCES> m_cachedTrfs;
+	U m_cachedTrfCount = 0;
+
+	StagingGpuMemoryToken m_uboToken;
+
+	U m_nodeProcessedCount = 0;
+
+	Array<CompleteRenderingBuildInfo, 2> m_buildInfo;
+	U m_crntBuildInfo = 0;
+};
+
 /// Check if the drawcalls can be merged.
-static Bool canMergeBuildInfo(const RenderingBuildInfoOut& a, const RenderingBuildInfoOut& b)
+static Bool canMergeBuildInfo(const CompleteRenderingBuildInfo& abi, const CompleteRenderingBuildInfo& bbi)
 {
+	const RenderingBuildInfoOut& a = abi.m_out;
+	const RenderingBuildInfoOut& b = bbi.m_out;
+
 	if(!a.m_hasTransform || !b.m_hasTransform)
 	{
 		// Cannot merge if there is no transform
@@ -29,6 +63,11 @@ static Bool canMergeBuildInfo(const RenderingBuildInfoOut& a, const RenderingBui
 
 	ANKI_ASSERT(a.m_hasTransform == b.m_hasTransform);
 
+	if(abi.m_rc->getMaterial().getUuid() != bbi.m_rc->getMaterial().getUuid())
+	{
+		return false;
+	}
+
 	if(a.m_program != b.m_program)
 	{
 		return false;
@@ -106,228 +145,171 @@ static void resetRenderingBuildInfoOut(RenderingBuildInfoOut& b)
 	b.m_topology = PrimitiveTopology::TRIANGLES;
 }
 
-class CompleteRenderingBuildInfo
-{
-public:
-	F32 m_flod = 0.0;
-	RenderComponent* m_rc = nullptr;
-	RenderingBuildInfoIn m_in;
-	RenderingBuildInfoOut m_out;
-};
-
-/// Drawer's context
-class DrawContext
-{
-public:
-	Pass m_pass;
-	Mat4 m_viewMat;
-	Mat4 m_viewProjMat;
-	CommandBufferPtr m_cmdb;
-
-	const VisibleNode* m_visibleNode = nullptr;
-
-	Array<Mat4, MAX_INSTANCES> m_cachedTrfs;
-	U m_cachedTrfCount = 0;
-
-	StagingGpuMemoryToken m_uboToken;
-
-	U m_nodeProcessedCount = 0;
-
-	Array<CompleteRenderingBuildInfo, 2> m_buildInfo;
-	U m_crntBuildInfo = 0;
-};
-
-/// Visitor that sets a uniform
-class SetupRenderableVariableVisitor
+RenderableDrawer::~RenderableDrawer()
 {
-public:
-	DrawContext* m_ctx ANKI_DBG_NULLIFY;
-	const RenderableDrawer* m_drawer ANKI_DBG_NULLIFY;
-	WeakArray<U8> m_uniformBuffer;
-	const MaterialVariant* m_variant ANKI_DBG_NULLIFY;
-	F32 m_flod;
-
-	/// Set a uniform in a client block
-	template<typename T>
-	void uniSet(const MaterialVariable& mtlVar, const T* value, U32 size)
-	{
-		mtlVar.writeShaderBlockMemory<T>(
-			*m_variant, value, size, &m_uniformBuffer[0], &m_uniformBuffer[0] + m_uniformBuffer.getSize());
-	}
-
-	template<typename TRenderableVariableTemplate>
-	Error visit(const TRenderableVariableTemplate& rvar);
-};
+}
 
-template<typename TRenderableVariableTemplate>
-Error SetupRenderableVariableVisitor::visit(const TRenderableVariableTemplate& rvar)
+void RenderableDrawer::setupUniforms(DrawContext& ctx, CompleteRenderingBuildInfo& build)
 {
-	using DataType = typename TRenderableVariableTemplate::Type;
-	const MaterialVariable& mvar = rvar.getMaterialVariable();
+	const Material& mtl = build.m_rc->getMaterial();
+	const MaterialVariant& variant = mtl.getOrCreateVariant(build.m_in.m_key);
+	const ShaderProgramResourceVariant& progVariant = variant.getShaderProgramResourceVariant();
 
-	// Array size
-	U cachedTrfs = m_ctx->m_cachedTrfCount;
+	const U cachedTrfs = ctx.m_cachedTrfCount;
 	ANKI_ASSERT(cachedTrfs <= MAX_INSTANCES);
 
-	// Set uniform
-	//
-	const Mat4& vp = m_ctx->m_viewProjMat;
-	const Mat4& v = m_ctx->m_viewMat;
+	// Get some memory for uniforms
+	U8* uniforms = static_cast<U8*>(m_r->getStagingGpuMemoryManager().allocateFrame(
+		variant.getUniformBlockSize(), StagingGpuMemoryType::UNIFORM, ctx.m_uboToken));
+	void* const uniformsBegin = uniforms;
+	const void* const uniformsEnd = uniforms + variant.getUniformBlockSize();
 
-	switch(mvar.getBuiltin())
-	{
-	case BuiltinMaterialVariableId::NONE:
-		uniSet<DataType>(mvar, &rvar.getValue(), 1);
-		break;
-	case BuiltinMaterialVariableId::MVP_MATRIX:
+	// Iterate variables
+	for(auto it = build.m_rc->getVariablesBegin(); it != build.m_rc->getVariablesEnd(); ++it)
 	{
-		ANKI_ASSERT(cachedTrfs > 0);
+		const RenderComponentVariable& var = *it;
+		const MaterialVariable& mvar = var.getMaterialVariable();
+		const ShaderProgramResourceInputVariable& progvar = mvar.getShaderProgramResourceInputVariable();
 
-		DynamicArrayAuto<Mat4> mvp(m_drawer->m_r->getFrameAllocator());
-		mvp.create(cachedTrfs);
-
-		for(U i = 0; i < cachedTrfs; i++)
+		if(!variant.variableActive(mvar))
 		{
-			mvp[i] = vp * m_ctx->m_cachedTrfs[i];
+			continue;
 		}
 
-		uniSet(mvar, &mvp[0], cachedTrfs);
-		break;
-	}
-	case BuiltinMaterialVariableId::MV_MATRIX:
-	{
-		ANKI_ASSERT(cachedTrfs > 0);
-
-		DynamicArrayAuto<Mat4> mv(m_drawer->m_r->getFrameAllocator());
-		mv.create(cachedTrfs);
-
-		for(U i = 0; i < cachedTrfs; i++)
+		switch(progvar.getShaderVariableDataType())
+		{
+		case ShaderVariableDataType::FLOAT:
 		{
-			mv[i] = v * m_ctx->m_cachedTrfs[i];
+			F32 val = mvar.getValue<F32>();
+			progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+			break;
 		}
-
-		uniSet(mvar, &mv[0], cachedTrfs);
-		break;
-	}
-	case BuiltinMaterialVariableId::VP_MATRIX:
-		ANKI_ASSERT(cachedTrfs == 0 && "Cannot have transform");
-		uniSet(mvar, &vp, 1);
-		break;
-	case BuiltinMaterialVariableId::V_MATRIX:
-		uniSet(mvar, &v, 1);
-		break;
-	case BuiltinMaterialVariableId::NORMAL_MATRIX:
-	{
-		ANKI_ASSERT(cachedTrfs > 0);
-
-		DynamicArrayAuto<Mat3> normMats(m_drawer->m_r->getFrameAllocator());
-		normMats.create(cachedTrfs);
-
-		for(U i = 0; i < cachedTrfs; i++)
+		case ShaderVariableDataType::VEC2:
 		{
-			Mat4 mv = v * m_ctx->m_cachedTrfs[i];
-			normMats[i] = mv.getRotationPart();
-			normMats[i].reorthogonalize();
+			Vec2 val = mvar.getValue<Vec2>();
+			progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+			break;
 		}
-
-		uniSet(mvar, &normMats[0], cachedTrfs);
-		break;
-	}
-	case BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX:
-	{
-		// Calc the billboard rotation matrix
-		Mat3 rot = v.getRotationPart().getTransposed();
-
-		DynamicArrayAuto<Mat4> bmvp(m_drawer->m_r->getFrameAllocator());
-		bmvp.create(cachedTrfs);
-
-		for(U i = 0; i < cachedTrfs; i++)
+		case ShaderVariableDataType::VEC3:
 		{
-			Mat4 trf = m_ctx->m_cachedTrfs[i];
-			trf.setRotationPart(rot);
-			bmvp[i] = vp * trf;
+			Vec3 val = mvar.getValue<Vec3>();
+			progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+			break;
 		}
-
-		uniSet(mvar, &bmvp[0], cachedTrfs);
-		break;
-	}
-	case BuiltinMaterialVariableId::CAMERA_ROT_MATRIX:
-	{
-		// Calc the billboard rotation matrix
-		Mat3 rot = v.getRotationPart().getTransposed();
-		uniSet(mvar, &rot, 1);
-		break;
-	}
-	case BuiltinMaterialVariableId::MAX_TESS_LEVEL:
-	{
-		const RenderComponentVariable& base = rvar;
-		F32 maxtess = base.getValue<F32>();
-		F32 tess = 0.0;
-
-		if(m_flod >= 1.0)
+		case ShaderVariableDataType::VEC4:
 		{
-			tess = 1.0;
+			Vec4 val = mvar.getValue<Vec4>();
+			progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+			break;
 		}
-		else
+		case ShaderVariableDataType::MAT3:
 		{
-			tess = maxtess - m_flod * maxtess;
-			tess = std::max(tess, 1.0f);
+			switch(mvar.getBuiltin())
+			{
+			case BuiltinMaterialVariableId::NONE:
+			{
+				Mat3 val = mvar.getValue<Mat3>();
+				progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::NORMAL_MATRIX:
+			{
+				ANKI_ASSERT(cachedTrfs > 0);
+
+				DynamicArrayAuto<Mat3> normMats(m_r->getFrameAllocator());
+				normMats.create(cachedTrfs);
+
+				for(U i = 0; i < cachedTrfs; i++)
+				{
+					Mat4 mv = ctx.m_viewMat * ctx.m_cachedTrfs[i];
+					normMats[i] = mv.getRotationPart();
+					normMats[i].reorthogonalize();
+				}
+
+				progVariant.writeShaderBlockMemory(progvar, &normMats[0], cachedTrfs, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::CAMERA_ROTATION_MATRIX:
+			{
+				Mat3 rot = ctx.m_viewMat.getRotationPart().getTransposed();
+				progVariant.writeShaderBlockMemory(progvar, &rot, 1, uniformsBegin, uniformsEnd);
+				break;
+			}
+			default:
+				ANKI_ASSERT(0);
+			}
+
+			break;
 		}
-
-		uniSet(mvar, &tess, 1);
-		break;
-	}
-	case BuiltinMaterialVariableId::MS_DEPTH_MAP:
-		// Do nothing
-		break;
-	default:
-		ANKI_ASSERT(0);
-		break;
-	}
-
-	return ErrorCode::NONE;
-}
-
-// Texture specialization
-template<>
-void SetupRenderableVariableVisitor::uniSet<TextureResourcePtr>(
-	const MaterialVariable& mtlvar, const TextureResourcePtr* values, U32 size)
-{
-	ANKI_ASSERT(size == 1);
-	ANKI_ASSERT(values);
-	m_ctx->m_cmdb->bindTexture(0, mtlvar.getTextureUnit(), (*values)->getGrTexture());
-}
-
-RenderableDrawer::~RenderableDrawer()
-{
-}
-
-void RenderableDrawer::setupUniforms(DrawContext& ctx, CompleteRenderingBuildInfo& build)
-{
-	const Material& mtl = build.m_rc->getMaterial();
-	const MaterialVariant& variant = mtl.getVariant(build.m_in.m_key);
-
-	// Get some memory for uniforms
-	U8* uniforms = static_cast<U8*>(m_r->getStagingGpuMemoryManager().allocateFrame(
-		variant.getDefaultBlockSize(), StagingGpuMemoryType::UNIFORM, ctx.m_uboToken));
-
-	// Call the visitor
-	SetupRenderableVariableVisitor visitor;
-	visitor.m_ctx = &ctx;
-	visitor.m_drawer = this;
-	visitor.m_uniformBuffer = WeakArray<U8>(uniforms, variant.getDefaultBlockSize());
-	visitor.m_variant = &variant;
-	visitor.m_flod = build.m_flod;
-
-	for(auto it = build.m_rc->getVariablesBegin(); it != build.m_rc->getVariablesEnd(); ++it)
-	{
-		RenderComponentVariable* rvar = *it;
-
-		if(variant.variableActive(rvar->getMaterialVariable()))
+		case ShaderVariableDataType::MAT4:
+		{
+			switch(mvar.getBuiltin())
+			{
+			case BuiltinMaterialVariableId::NONE:
+			{
+				Mat4 val = mvar.getValue<Mat4>();
+				progVariant.writeShaderBlockMemory(progvar, &val, 1, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::MODEL_VIEW_PROJECTION_MATRIX:
+			{
+				ANKI_ASSERT(cachedTrfs > 0);
+
+				DynamicArrayAuto<Mat4> mvp(m_r->getFrameAllocator());
+				mvp.create(cachedTrfs);
+
+				for(U i = 0; i < cachedTrfs; i++)
+				{
+					mvp[i] = ctx.m_viewProjMat * ctx.m_cachedTrfs[i];
+				}
+
+				progVariant.writeShaderBlockMemory(progvar, &mvp[0], cachedTrfs, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::MODEL_VIEW_MATRIX:
+			{
+				ANKI_ASSERT(cachedTrfs > 0);
+
+				DynamicArrayAuto<Mat4> mv(m_r->getFrameAllocator());
+				mv.create(cachedTrfs);
+
+				for(U i = 0; i < cachedTrfs; i++)
+				{
+					mv[i] = ctx.m_viewMat * ctx.m_cachedTrfs[i];
+				}
+
+				progVariant.writeShaderBlockMemory(progvar, &mv[0], cachedTrfs, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::VIEW_PROJECTION_MATRIX:
+			{
+				ANKI_ASSERT(cachedTrfs == 0 && "Cannot have transform");
+				progVariant.writeShaderBlockMemory(progvar, &ctx.m_viewProjMat, 1, uniformsBegin, uniformsEnd);
+				break;
+			}
+			case BuiltinMaterialVariableId::VIEW_MATRIX:
+			{
+				progVariant.writeShaderBlockMemory(progvar, &ctx.m_viewMat, 1, uniformsBegin, uniformsEnd);
+				break;
+			}
+			default:
+				ANKI_ASSERT(0);
+			}
+
+			break;
+		}
+		case ShaderVariableDataType::SAMPLER_2D:
+		case ShaderVariableDataType::SAMPLER_2D_ARRAY:
+		case ShaderVariableDataType::SAMPLER_3D:
+		case ShaderVariableDataType::SAMPLER_CUBE:
 		{
-			Error err = rvar->acceptVisitor(visitor);
-			(void)err;
+			ctx.m_cmdb->bindTexture(
+				0, progVariant.getTextureUnit(progvar), mvar.getValue<TextureResourcePtr>()->getGrTexture());
+			break;
 		}
+		default:
+			ANKI_ASSERT(0);
+		} // end switch
 	}
 }
 
@@ -448,7 +430,6 @@ Error RenderableDrawer::drawSingle(DrawContext& ctx)
 {
 	// Get components
 	RenderComponent& renderable = ctx.m_visibleNode->m_node->getComponent<RenderComponent>();
-	const Material& mtl = renderable.getMaterial();
 
 	// Get info of current
 	CompleteRenderingBuildInfo& crntBuild = ctx.m_buildInfo[ctx.m_crntBuildInfo];
@@ -457,15 +438,13 @@ Error RenderableDrawer::drawSingle(DrawContext& ctx)
 
 	// Fill the crntBuild
 	F32 flod = m_r->calculateLod(sqrt(ctx.m_visibleNode->m_frustumDistanceSquared));
-	flod = min<F32>(flod, MAX_LODS - 1);
+	flod = min<F32>(flod, MAX_LOD - 1);
 
 	crntBuild.m_rc = &renderable;
 	crntBuild.m_flod = flod;
 
 	crntBuild.m_in.m_key.m_lod = flod;
 	crntBuild.m_in.m_key.m_pass = ctx.m_pass;
-	crntBuild.m_in.m_key.m_tessellation = m_r->getTessellationEnabled() && mtl.getTessellationEnabled()
-		&& crntBuild.m_in.m_key.m_lod == 0 && ctx.m_pass != Pass::SM;
 	crntBuild.m_in.m_key.m_instanceCount = 1;
 	crntBuild.m_in.m_subMeshIndicesArray = &ctx.m_visibleNode->m_spatialIndices[0];
 	crntBuild.m_in.m_subMeshIndicesCount = ctx.m_visibleNode->m_spatialsCount;
@@ -482,7 +461,7 @@ Error RenderableDrawer::drawSingle(DrawContext& ctx)
 			ctx.m_cachedTrfs[ctx.m_cachedTrfCount++] = crntBuild.m_out.m_transform;
 		}
 	}
-	else if(crntBuild.m_out.m_hasTransform && canMergeBuildInfo(crntBuild.m_out, prevBuild.m_out)
+	else if(crntBuild.m_out.m_hasTransform && canMergeBuildInfo(crntBuild, prevBuild)
 		&& ctx.m_cachedTrfCount < MAX_INSTANCES - 1)
 	{
 		// Can merge, will cache the drawcall and skip the drawcall

+ 5 - 5
src/anki/resource/Animation.cpp

@@ -49,7 +49,7 @@ Error Animation::load(const ResourceFilename& filename)
 	ANKI_CHECK(rootel.getChildElementOptional("repeat", repel));
 	if(repel)
 	{
-		ANKI_CHECK(repel.getI64(tmp));
+		ANKI_CHECK(repel.getNumber(tmp));
 		m_repeat = tmp;
 	}
 	else
@@ -103,7 +103,7 @@ Error Animation::load(const ResourceFilename& filename)
 
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getF64(ftmp));
+				ANKI_CHECK(el.getNumber(ftmp));
 				key.m_time = ftmp;
 				m_startTime = std::min(m_startTime, key.m_time);
 				maxTime = std::max(maxTime, key.m_time);
@@ -140,7 +140,7 @@ Error Animation::load(const ResourceFilename& filename)
 
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getF64(ftmp));
+				ANKI_CHECK(el.getNumber(ftmp));
 				key.m_time = ftmp;
 				m_startTime = std::min(m_startTime, key.m_time);
 				maxTime = std::max(maxTime, key.m_time);
@@ -179,14 +179,14 @@ Error Animation::load(const ResourceFilename& filename)
 
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getF64(ftmp));
+				ANKI_CHECK(el.getNumber(ftmp));
 				key.m_time = ftmp;
 				m_startTime = std::min(m_startTime, key.m_time);
 				maxTime = std::max(maxTime, key.m_time);
 
 				// <value>
 				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getF64(ftmp));
+				ANKI_CHECK(el.getNumber(ftmp));
 				key.m_value = ftmp;
 
 				// Check ident

+ 1 - 1
src/anki/resource/CollisionResource.cpp

@@ -34,7 +34,7 @@ Error CollisionResource::load(const ResourceFilename& filename)
 	if(type == "sphere")
 	{
 		F64 tmp;
-		ANKI_CHECK(valEl.getF64(tmp));
+		ANKI_CHECK(valEl.getNumber(tmp));
 		m_physicsShape = physics.newInstance<PhysicsSphere>(csInit, tmp);
 	}
 	else if(type == "box")

+ 2 - 1
src/anki/resource/Common.h

@@ -31,7 +31,7 @@ class ResourcePointer;
 
 /// @name Constants
 /// @{
-const U MAX_LODS = 3;
+const U MAX_LOD = 3;
 const U MAX_INSTANCES = 64;
 const U MAX_SUB_DRAWCALLS = 64;
 
@@ -76,6 +76,7 @@ ANKI_FORWARD(DummyRsrc, DummyResourcePtr)
 ANKI_FORWARD(CollisionResource, CollisionResourcePtr)
 ANKI_FORWARD(GenericResource, GenericResourcePtr)
 ANKI_FORWARD(TextureAtlas, TextureAtlasResourcePtr)
+ANKI_FORWARD(ShaderProgramResource, ShaderProgramResourcePtr)
 
 #undef ANKI_FORWARD
 

+ 376 - 282
src/anki/resource/Material.cpp

@@ -4,391 +4,485 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/resource/Material.h>
-#include <anki/resource/MaterialLoader.h>
 #include <anki/resource/ResourceManager.h>
-#include <anki/core/App.h>
-#include <anki/util/Logger.h>
-#include <anki/resource/ShaderResource.h>
-#include <anki/resource/TextureResource.h>
-#include <anki/util/Hash.h>
-#include <anki/util/File.h>
-#include <anki/util/Filesystem.h>
 #include <anki/misc/Xml.h>
-#include <algorithm>
 
 namespace anki
 {
 
-template<typename T>
-Error MaterialVariableTemplate<T>::init(U idx, const MaterialLoader::Input& in, Material& mtl)
+struct BuiltinVarInfo
 {
-	m_idx = idx;
-	m_varType = in.m_type;
-	m_name.create(mtl.getAllocator(), in.m_name);
-	m_builtin = in.m_builtin;
-	m_instanced = in.m_flags.m_instanced;
-
-	// Set value
-	if(in.m_value.getSize() > 0)
-	{
-		ANKI_ASSERT(m_builtin == BuiltinMaterialVariableId::NONE);
+	const char* m_name;
+	ShaderVariableDataType m_type;
+	Bool m_instanced;
+};
+
+static const Array<BuiltinVarInfo, U(BuiltinMaterialVariableId::COUNT) - 1> BUILTIN_INFOS = {
+	{{"MODEL_VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, true},
+		{"MODEL_VIEW_MATRIX", ShaderVariableDataType::MAT4, true},
+		{"VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, false},
+		{"VIEW_MATRIX", ShaderVariableDataType::MAT4, false},
+		{"NORMAL_MATRIX", ShaderVariableDataType::MAT3, true},
+		{"CAMERA_ROTATION_MATRIX", ShaderVariableDataType::MAT3, false}}};
+
+MaterialVariable::MaterialVariable()
+{
+	m_mat4 = Mat4::getZero();
+}
 
-		// Has values
-		DynamicArrayAuto<F32> floats(mtl.getTempAllocator());
+MaterialVariable::~MaterialVariable()
+{
+}
 
-		U floatsNeeded = sizeof(T) / sizeof(F32);
+Material::Material(ResourceManager* manager)
+	: ResourceObject(manager)
+{
+}
 
-		if(in.m_value.getSize() != floatsNeeded)
-		{
-			ANKI_RESOURCE_LOGE("Incorrect number of values. Variable %s", &in.m_name[0]);
-			return ErrorCode::USER_DATA;
-		}
+Material::~Material()
+{
+	m_vars.destroy(getAllocator());
+	m_constValues.destroy(getAllocator());
+	m_mutations.destroy(getAllocator());
+}
 
-		floats.create(floatsNeeded);
+Error Material::load(const ResourceFilename& filename)
+{
+	XmlDocument doc;
+	XmlElement el;
+	Bool present = false;
+	ANKI_CHECK(openFileParseXml(filename, doc));
 
-		auto it = in.m_value.getBegin();
-		for(U i = 0; i < floatsNeeded; ++i)
-		{
-			F64 d;
-			ANKI_CHECK(it->toF64(d));
+	// <material>
+	XmlElement rootEl;
+	ANKI_CHECK(doc.getChildElement("material", rootEl));
 
-			floats[i] = d;
-			++it;
-		}
+	// shaderProgram
+	CString fname;
+	ANKI_CHECK(rootEl.getAttributeText("shaderProgram", fname));
+	ANKI_CHECK(getManager().loadResource(fname, m_prog));
 
-		memcpy(&m_value, &floats[0], sizeof(T));
-	}
-	else
-	{
-		ANKI_ASSERT(m_builtin != BuiltinMaterialVariableId::NONE);
-	}
+	// shadow
+	ANKI_CHECK(rootEl.getAttributeNumberOptional("shadow", m_shadow, present));
+	m_shadow = m_shadow != 0;
 
-	return ErrorCode::NONE;
-}
+	// forwardShading
+	ANKI_CHECK(rootEl.getAttributeNumberOptional("forwardShading", m_forwardShading, present));
+	m_forwardShading = m_forwardShading != 0;
 
-template<>
-Error MaterialVariableTemplate<TextureResourcePtr>::init(U idx, const MaterialLoader::Input& in, Material& mtl)
-{
-	m_idx = idx;
-	m_varType = in.m_type;
-	m_textureUnit = in.m_binding;
-	m_name.create(mtl.getAllocator(), in.m_name);
-	m_builtin = in.m_builtin;
-	m_instanced = false;
-
-	if(in.m_value.getSize() > 0)
+	// <mutators>
+	XmlElement mutatorsEl;
+	ANKI_CHECK(rootEl.getChildElementOptional("mutators", mutatorsEl));
+	if(mutatorsEl)
 	{
-		ANKI_ASSERT(m_builtin == BuiltinMaterialVariableId::NONE);
-
-		ANKI_CHECK(mtl.getManager().loadResource(in.m_value.getBegin()->toCString(), m_value));
+		ANKI_CHECK(parseMutators(mutatorsEl));
 	}
-	else
+
+	// <inputs>
+	ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
+	if(el)
 	{
-		ANKI_ASSERT(m_builtin != BuiltinMaterialVariableId::NONE);
+		ANKI_CHECK(parseInputs(el));
 	}
 
 	return ErrorCode::NONE;
 }
 
-MaterialVariant::MaterialVariant()
+Error Material::parseMutators(XmlElement mutatorsEl)
 {
-}
+	XmlElement mutatorEl;
+	ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
 
-MaterialVariant::~MaterialVariant()
-{
-}
+	U32 mutatorCount = 0;
+	ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
+	++mutatorCount;
+	ANKI_ASSERT(mutatorCount > 0);
+	m_mutations.create(getAllocator(), mutatorCount);
+	mutatorCount = 0;
 
-Error MaterialVariant::init(const RenderingKey& key2, Material& mtl, MaterialLoader& loader)
-{
-	RenderingKey key = key2;
-
-	m_varActive.create(mtl.getAllocator(), mtl.m_vars.getSize());
-	m_blockInfo.create(mtl.getAllocator(), mtl.m_vars.getSize());
-
-	// Disable tessellation under some keys
-	if(key.m_tessellation)
+	do
 	{
-		// tessellation is disabled on LODs > 0
-		if(key.m_lod > 0)
+		ShaderProgramResourceMutation& mutation = m_mutations[mutatorCount];
+
+		// name
+		CString mutatorName;
+		ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
+		if(mutatorName.isEmpty())
 		{
-			key.m_tessellation = false;
+			ANKI_RESOURCE_LOGE("Mutator name is empty");
+			return ErrorCode::USER_DATA;
 		}
 
-		// tesselation is disabled on shadow passes
-		if(key.m_pass == Pass::SM)
+		if(mutatorName == "INSTANCE_COUNT" || mutatorName == "PASS" || mutatorName == "LOD")
 		{
-			key.m_tessellation = false;
+			ANKI_RESOURCE_LOGE("Cannot list builtin mutator \"%s\"", &mutatorName[0]);
+			return ErrorCode::USER_DATA;
 		}
-	}
 
-	loader.mutate(key);
+		// value
+		ANKI_CHECK(mutatorEl.getAttributeNumber("value", mutation.m_value));
 
-	m_shaderBlockSize = loader.getUniformBlockSize();
+		// Find mutator
+		mutation.m_mutator = m_prog->tryFindMutator(mutatorName);
 
-	U count = 0;
-	Error err = loader.iterateAllInputVariables([&](const MaterialLoader::Input& in) -> Error {
-		m_varActive[count] = true;
-		if(!in.m_flags.m_inShadow && key.m_pass == Pass::SM)
+		if(!mutation.m_mutator)
 		{
-			m_varActive[count] = false;
+			ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
+			return ErrorCode::USER_DATA;
 		}
 
-		if(in.m_flags.m_inBlock && m_varActive[count])
+		if(!mutation.m_mutator->valueExists(mutation.m_value))
 		{
-			m_blockInfo[count] = in.m_blockInfo;
+			ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", mutation.m_value, &mutatorName[0]);
+			return ErrorCode::USER_DATA;
 		}
 
-		++count;
-
-		return ErrorCode::NONE;
-	});
-	(void)err;
+		// Advance
+		++mutatorCount;
+		ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
+	} while(mutatorEl);
 
-	//
-	// Shaders
-	//
-	Array<ShaderPtr, 5> shaders;
-	for(ShaderType stype = ShaderType::VERTEX; stype <= ShaderType::FRAGMENT; ++stype)
+	// Find the builtin mutators
+	U builtinMutatorCount = 0;
+	for(const ShaderProgramResourceMutator& m : m_prog->getMutators())
 	{
-		if(stype == ShaderType::GEOMETRY)
+		if(m.getName() == "INSTANCE_COUNT")
 		{
-			continue;
-		}
+			if(!m_prog->isInstanced())
+			{
+				ANKI_RESOURCE_LOGE("Program is not instanced but has the INSTANCE_COUNT mutator");
+				return ErrorCode::USER_DATA;
+			}
 
-		if((stype == ShaderType::TESSELLATION_CONTROL || stype == ShaderType::TESSELLATION_EVALUATION)
-			&& !key.m_tessellation)
-		{
-			continue;
-		}
+			if(m.getValues().getSize() != MAX_INSTANCE_GROUPS)
+			{
+				ANKI_RESOURCE_LOGE("Mutator INSTANCE_COUNT should have %u values in the program", MAX_INSTANCE_GROUPS);
+				return ErrorCode::USER_DATA;
+			}
 
-		const String& src = loader.getShaderSource(stype);
+			for(U i = 0; i < MAX_INSTANCE_GROUPS; ++i)
+			{
+				if(m.getValues()[i] != (1 << i))
+				{
+					ANKI_RESOURCE_LOGE("Values of the INSTANCE_COUNT mutator in the program are not the expected");
+					return ErrorCode::USER_DATA;
+				}
+			}
 
-		StringAuto filename(mtl.getTempAllocator());
-		ANKI_CHECK(mtl.createProgramSourceToCache(src, stype, filename));
+			m_instanceMutator = &m;
+			++builtinMutatorCount;
+		}
+		else if(m.getName() == "PASS")
+		{
+			if(m.getValues().getSize() != U(Pass::COUNT))
+			{
+				ANKI_RESOURCE_LOGE("Mutator PASS should have %u values in the program", U(Pass::COUNT));
+				return ErrorCode::USER_DATA;
+			}
 
-		ShaderResourcePtr& shader = m_shaders[stype];
+			for(U i = 0; i < U(Pass::COUNT); ++i)
+			{
+				if(m.getValues()[i] != I(i))
+				{
+					ANKI_RESOURCE_LOGE("Values of the PASS mutator in the program are not the expected");
+					return ErrorCode::USER_DATA;
+				}
+			}
 
-		ANKI_CHECK(mtl.getManager().loadResource(filename.toCString(), shader));
+			m_passMutator = &m;
+			++builtinMutatorCount;
+		}
+		else if(m.getName() == "LOD")
+		{
+			if(m.getValues().getSize() > MAX_LOD)
+			{
+				ANKI_RESOURCE_LOGE("Mutator LOD should have at least %u values in the program", U(MAX_LOD));
+				return ErrorCode::USER_DATA;
+			}
 
-		shaders[stype] = shader->getGrShader();
+			for(U i = 0; i < m.getValues().getSize(); ++i)
+			{
+				if(m.getValues()[i] != I(i))
+				{
+					ANKI_RESOURCE_LOGE("Values of the LOD mutator in the program are not the expected");
+					return ErrorCode::USER_DATA;
+				}
+			}
 
-		// Update the hash
-		mtl.m_hash ^= computeHash(&src[0], src.getLength());
+			m_lodMutator = &m;
+			m_lodCount = m.getValues().getSize();
+			++builtinMutatorCount;
+		}
 	}
 
-	m_prog = mtl.getManager().getGrManager().newInstance<ShaderProgram>(shaders[ShaderType::VERTEX],
-		shaders[ShaderType::TESSELLATION_CONTROL],
-		shaders[ShaderType::TESSELLATION_EVALUATION],
-		shaders[ShaderType::GEOMETRY],
-		shaders[ShaderType::FRAGMENT]);
-
-	return ErrorCode::NONE;
-}
-
-Material::Material(ResourceManager* manager)
-	: ResourceObject(manager)
-{
-	// Init m_variantMatrix
-	U16* arr = &m_variantMatrix[0][0][0][0];
-	U count = sizeof(m_variantMatrix) / sizeof(m_variantMatrix[0][0][0][0]);
-	for(U i = 0; i < count; ++i)
+	if(m_prog->isInstanced() && !m_instanceMutator)
 	{
-		arr[i] = MAX_U16;
+		ANKI_RESOURCE_LOGE("If program is instanced then the instance mutator sHould be the INSTANCE_COUNT");
+		return ErrorCode::USER_DATA;
 	}
-}
 
-Material::~Material()
-{
-	auto alloc = getAllocator();
-
-	for(MaterialVariant& var : m_variants)
+	if(!m_forwardShading && !m_passMutator)
 	{
-		var.destroy(alloc);
+		ANKI_RESOURCE_LOGE("PASS mutator is required");
+		return ErrorCode::USER_DATA;
 	}
-	m_variants.destroy(alloc);
 
-	for(MaterialVariable* var : m_vars)
+	if(m_mutations.getSize() + builtinMutatorCount != m_prog->getMutators().getSize())
 	{
-		if(var)
-		{
-			var->destroy(alloc);
-			alloc.deleteInstance(var);
-		}
+		ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
+		return ErrorCode::USER_DATA;
 	}
-	m_vars.destroy(alloc);
-}
-
-Error Material::load(const ResourceFilename& filename)
-{
-	XmlDocument doc;
-	ANKI_CHECK(openFileParseXml(filename, doc));
-
-	MaterialLoader loader(getTempAllocator());
-	ANKI_CHECK(loader.parseXmlDocument(doc));
-
-	m_lodCount = loader.getLodCount();
-	m_shadow = loader.getShadowEnabled();
-	m_forwardShading = loader.isForwardShading();
-	m_tessellation = loader.getTessellationEnabled();
-	m_instanced = loader.isInstanced();
-
-	// Start initializing
-	ANKI_CHECK(createVars(loader));
-	ANKI_CHECK(createVariants(loader));
 
 	return ErrorCode::NONE;
 }
 
-Error Material::createVars(const MaterialLoader& loader)
+Error Material::parseInputs(XmlElement inputsEl)
 {
-	// Count them and allocate
-	U count = 0;
-	Error err = loader.iterateAllInputVariables([&](const MaterialLoader::Input&) -> Error {
-		++count;
-		return ErrorCode::NONE;
-	});
-
-	auto alloc = getAllocator();
-	m_vars.create(alloc, count);
-	memset(&m_vars[0], 0, count * sizeof(m_vars[0]));
-
-	// Find the name
-	count = 0;
-	err = loader.iterateAllInputVariables([&](const MaterialLoader::Input& in) -> Error {
-
-#define ANKI_INIT_VAR(type_)                                                                                           \
-	{                                                                                                                  \
-		MaterialVariableTemplate<type_>* var = alloc.newInstance<MaterialVariableTemplate<type_>>();                   \
-		m_vars[count] = var;                                                                                           \
-		ANKI_CHECK(var->init(count, in, *this));                                                                       \
-		++count;                                                                                                       \
-	}
+	// Iterate the program's variables and get counts
+	U constInputCount = 0;
+	U nonConstInputCount = 0;
+	for(const ShaderProgramResourceInputVariable& in : m_prog->getInputVariables())
+	{
+		if(!in.acceptAllMutations(m_mutations))
+		{
+			// Will not be used by this material at all
+			continue;
+		}
 
-		switch(in.m_type)
+		if(in.isConstant())
+		{
+			++constInputCount;
+		}
+		else
 		{
-		case ShaderVariableDataType::SAMPLER_2D:
-		case ShaderVariableDataType::SAMPLER_2D_ARRAY:
-		case ShaderVariableDataType::SAMPLER_CUBE:
-			ANKI_INIT_VAR(TextureResourcePtr);
-			break;
-		case ShaderVariableDataType::FLOAT:
-			ANKI_INIT_VAR(F32);
-			break;
-		case ShaderVariableDataType::VEC2:
-			ANKI_INIT_VAR(Vec2);
-			break;
-		case ShaderVariableDataType::VEC3:
-			ANKI_INIT_VAR(Vec3);
-			break;
-		case ShaderVariableDataType::VEC4:
-			ANKI_INIT_VAR(Vec4);
-			break;
-		case ShaderVariableDataType::MAT3:
-			ANKI_INIT_VAR(Mat3);
-			break;
-		case ShaderVariableDataType::MAT4:
-			ANKI_INIT_VAR(Mat4);
-			break;
-		default:
-			ANKI_ASSERT(0);
+			++nonConstInputCount;
 		}
+	}
 
-#undef ANKI_INIT_VAR
+	m_vars.create(getAllocator(), nonConstInputCount);
+	m_constValues.create(getAllocator(), constInputCount);
 
-		return ErrorCode::NONE;
-	});
+	// Connect the input variables
+	XmlElement inputEl;
+	ANKI_CHECK(inputsEl.getChildElementOptional("input", inputEl));
+	while(inputEl)
+	{
+		// Get var name
+		CString varName;
+		ANKI_CHECK(inputEl.getAttributeText("shaderInput", varName));
 
-	return err;
-}
+		// Try find var
+		const ShaderProgramResourceInputVariable* foundVar = m_prog->tryFindInputVariable(varName);
+		if(foundVar == nullptr)
+		{
+			ANKI_RESOURCE_LOGE("Variable \"%s\" not found", &varName[0]);
+			return ErrorCode::USER_DATA;
+		}
 
-Error Material::createVariants(MaterialLoader& loader)
-{
-	U tessStates = m_tessellation ? 2 : 1;
-	U instStates = m_instanced ? MAX_INSTANCE_GROUPS : 1;
-	U passStates = m_shadow ? 2 : 1;
+		if(!foundVar->acceptAllMutations(m_mutations))
+		{
+			ANKI_RESOURCE_LOGE("Variable \"%s\" is not needed by the material's mutations. Don't need it");
+			return ErrorCode::USER_DATA;
+		}
 
-	// Init the variant matrix
-	U variantsCount = 0;
-	for(U p = 0; p < passStates; ++p)
-	{
-		for(U l = 0; l < m_lodCount; ++l)
+		// Process var
+		if(foundVar->isConstant())
 		{
-			for(U t = 0; t < tessStates; ++t)
+			// Const
+
+			ShaderProgramResourceConstantValue& constVal = m_constValues[--constInputCount];
+			constVal.m_variable = foundVar;
+
+			switch(foundVar->getShaderVariableDataType())
 			{
-				for(U i = 0; i < instStates; ++i)
-				{
-					// Enable variant
-					m_variantMatrix[p][l][t][i] = variantsCount++;
-				}
+			case ShaderVariableDataType::FLOAT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_float));
+				break;
+			case ShaderVariableDataType::VEC2:
+				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec2));
+				break;
+			case ShaderVariableDataType::VEC3:
+				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec3));
+				break;
+			case ShaderVariableDataType::VEC4:
+				ANKI_CHECK(inputEl.getAttributeVector("value", constVal.m_vec4));
+				break;
+			default:
+				ANKI_ASSERT(0);
+				break;
 			}
 		}
-	}
-
-	// Create the variants
-	m_variants.create(getAllocator(), variantsCount);
-
-	for(U p = 0; p < passStates; ++p)
-	{
-		for(U l = 0; l < m_lodCount; ++l)
+		else
 		{
-			for(U t = 0; t < tessStates; ++t)
+			// Builtin or not
+
+			Bool builtinPresent = false;
+			CString builtinStr;
+			ANKI_CHECK(inputEl.getAttributeTextOptional("builtin", builtinStr, builtinPresent));
+			if(builtinPresent)
 			{
-				for(U i = 0; i < instStates; ++i)
+				// Builtin
+
+				U i;
+				for(i = 0; i < BUILTIN_INFOS.getSize(); ++i)
 				{
-					U idx = m_variantMatrix[p][l][t][i];
-					if(idx == MAX_U16)
+					if(builtinStr == BUILTIN_INFOS[i].m_name)
 					{
-						continue;
+						break;
 					}
+				}
 
-					RenderingKey key;
-					key.m_pass = Pass(p);
-					key.m_lod = l;
-					key.m_tessellation = t;
-					key.m_instanceCount = 1 << i;
+				if(i == BUILTIN_INFOS.getSize())
+				{
+					ANKI_RESOURCE_LOGE("Incorrect builtin variable: %s", &builtinStr[0]);
+					return ErrorCode::USER_DATA;
+				}
 
-					ANKI_CHECK(m_variants[idx].init(key, *this, loader));
+				if(BUILTIN_INFOS[i].m_type != foundVar->getShaderVariableDataType())
+				{
+					ANKI_RESOURCE_LOGE(
+						"The type of the builtin variable in the shader program is not the correct one: %s",
+						&builtinStr[0]);
+					return ErrorCode::USER_DATA;
+				}
+
+				if(foundVar->isInstanced() && !BUILTIN_INFOS[i].m_instanced)
+				{
+					ANKI_RESOURCE_LOGE("Builtin variable %s cannot be instanced", BUILTIN_INFOS[i].m_name);
+					return ErrorCode::USER_DATA;
+				}
+
+				MaterialVariable& mtlVar = m_vars[--nonConstInputCount];
+				mtlVar.m_input = foundVar;
+				mtlVar.m_builtin = BuiltinMaterialVariableId(i + 1);
+			}
+			else
+			{
+				// Not built-in
+
+				if(foundVar->isInstanced())
+				{
+					ANKI_RESOURCE_LOGE("Only some builtin variables can be instanced: %s", &foundVar->getName()[0]);
+					return ErrorCode::USER_DATA;
+				}
+
+				MaterialVariable& mtlVar = m_vars[--nonConstInputCount];
+				mtlVar.m_input = foundVar;
+
+				switch(foundVar->getShaderVariableDataType())
+				{
+				case ShaderVariableDataType::FLOAT:
+					ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_float));
+					break;
+				case ShaderVariableDataType::VEC2:
+					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec2));
+					break;
+				case ShaderVariableDataType::VEC3:
+					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec3));
+					break;
+				case ShaderVariableDataType::VEC4:
+					ANKI_CHECK(inputEl.getAttributeVector("value", mtlVar.m_vec4));
+					break;
+				case ShaderVariableDataType::MAT3:
+					ANKI_CHECK(inputEl.getAttributeMatrix("value", mtlVar.m_mat3));
+					break;
+				case ShaderVariableDataType::MAT4:
+					ANKI_CHECK(inputEl.getAttributeMatrix("value", mtlVar.m_mat4));
+					break;
+				case ShaderVariableDataType::SAMPLER_2D:
+				case ShaderVariableDataType::SAMPLER_2D_ARRAY:
+				case ShaderVariableDataType::SAMPLER_3D:
+				case ShaderVariableDataType::SAMPLER_CUBE:
+				{
+					CString texfname;
+					ANKI_CHECK(inputEl.getAttributeText("value", texfname));
+					ANKI_CHECK(getManager().loadResource(texfname, mtlVar.m_tex));
+					break;
+				}
+
+				default:
+					ANKI_ASSERT(0);
+					break;
 				}
 			}
 		}
+
+		// Advance
+		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
+	}
+
+	if(nonConstInputCount != 0)
+	{
+		ANKI_RESOURCE_LOGE("Forgot to list %u shader program variables in this material", nonConstInputCount);
+		return ErrorCode::USER_DATA;
+	}
+
+	if(constInputCount != 0)
+	{
+		ANKI_RESOURCE_LOGE("Forgot to list %u constant shader program variables in this material", constInputCount);
+		return ErrorCode::USER_DATA;
 	}
 
 	return ErrorCode::NONE;
 }
 
-Error Material::createProgramSourceToCache(const String& source, ShaderType type, StringAuto& out)
+const MaterialVariant& Material::getOrCreateVariant(const RenderingKey& key_) const
 {
-	auto alloc = getTempAllocator();
+	RenderingKey key = key_;
+	key.m_lod = min<U>(m_lodCount - 1, key.m_lod);
 
-	// Create the hash
-	U64 h = computeHash(&source[0], source.getLength());
+	if(!isInstanced())
+	{
+		ANKI_ASSERT(key.m_instanceCount == 1);
+	}
 
-	// Create out
-	out.sprintf("mtl_%llu%s", h, &shaderTypeToFileExtension(type)[0]);
+	key.m_instanceCount = 1 << getInstanceGroupIdx(key.m_instanceCount);
 
-	// Create path
-	StringAuto fullFname(alloc);
-	fullFname.sprintf("%s/%s", &getManager()._getCacheDirectory()[0], &out[0]);
+	MaterialVariant& variant = m_variantMatrix[U(key.m_pass)][key.m_lod][getInstanceGroupIdx(key.m_instanceCount)];
+	LockGuard<SpinLock> lock(m_variantMatrixMtx);
 
-	// If file not exists write it
-	if(!fileExists(fullFname.toCString()))
+	if(variant.m_variant == nullptr)
 	{
-		// If not create it
-		File f;
-		ANKI_CHECK(f.open(fullFname.toCString(), FileOpenFlag::WRITE));
-		ANKI_CHECK(f.writeText("%s\n", &source[0]));
-	}
+		const U mutatorCount = m_mutations.getSize() + ((m_instanceMutator) ? 1 : 0) + ((m_passMutator) ? 1 : 0)
+			+ ((m_lodMutator) ? 1 : 0);
 
-	return ErrorCode::NONE;
-}
+		DynamicArrayAuto<ShaderProgramResourceMutation> mutations(getTempAllocator());
+		mutations.create(mutatorCount);
+		U count = m_mutations.getSize();
+		if(m_mutations.getSize())
+		{
+			memcpy(&mutations[0], &m_mutations[0], m_mutations.getSize() * sizeof(m_mutations[0]));
+		}
 
-const MaterialVariant& Material::getVariant(const RenderingKey& key) const
-{
-	U lod = min<U>(m_lodCount - 1, key.m_lod);
-	U16 idx = m_variantMatrix[U(key.m_pass)][lod][key.m_tessellation][getInstanceGroupIdx(key.m_instanceCount)];
+		if(m_instanceMutator)
+		{
+			mutations[count].m_mutator = m_instanceMutator;
+			mutations[count].m_value = key.m_instanceCount;
+			++count;
+		}
+
+		if(m_passMutator)
+		{
+			mutations[count].m_mutator = m_passMutator;
+			mutations[count].m_value = I(key.m_pass);
+			++count;
+		}
+
+		if(m_lodMutator)
+		{
+			mutations[count].m_mutator = m_lodMutator;
+			mutations[count].m_value = I(key.m_lod);
+			++count;
+		}
+
+		m_prog->getOrCreateVariant(
+			WeakArray<const ShaderProgramResourceMutation>(mutations.getSize() ? &mutations[0] : nullptr, count),
+			WeakArray<const ShaderProgramResourceConstantValue>(
+				(m_constValues.getSize()) ? &m_constValues[0] : nullptr, m_constValues.getSize()),
+			variant.m_variant);
+	}
 
-	ANKI_ASSERT(idx != MAX_U16);
-	return m_variants[idx];
+	return variant;
 }
 
 U Material::getInstanceGroupIdx(U instanceCount)

+ 105 - 210
src/anki/resource/Material.h

@@ -6,7 +6,7 @@
 #pragma once
 
 #include <anki/resource/ResourceObject.h>
-#include <anki/resource/ShaderResource.h>
+#include <anki/resource/ShaderProgramResource.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/RenderingKey.h>
 #include <anki/Math.h>
@@ -19,8 +19,6 @@ namespace anki
 // Forward
 class XmlElement;
 class Material;
-class MaterialLoader;
-class MaterialLoaderInputVariable;
 template<typename T>
 class MaterialVariableTemplate;
 class MaterialVariable;
@@ -33,50 +31,25 @@ class MaterialVariant;
 enum class BuiltinMaterialVariableId : U8
 {
 	NONE = 0,
-	MVP_MATRIX,
-	MV_MATRIX,
-	VP_MATRIX,
-	V_MATRIX,
+	MODEL_VIEW_PROJECTION_MATRIX,
+	MODEL_VIEW_MATRIX,
+	VIEW_PROJECTION_MATRIX,
+	VIEW_MATRIX,
 	NORMAL_MATRIX,
-	BILLBOARD_MVP_MATRIX,
-	CAMERA_ROT_MATRIX,
-	MAX_TESS_LEVEL,
-	MS_DEPTH_MAP,
+	CAMERA_ROTATION_MATRIX,
 	COUNT
 };
 
-/// Material variable base. It's a visitable.
-using MateriaVariableVisitable = VisitableCommonBase<MaterialVariable, // The base
-	MaterialVariableTemplate<F32>,
-	MaterialVariableTemplate<Vec2>,
-	MaterialVariableTemplate<Vec3>,
-	MaterialVariableTemplate<Vec4>,
-	MaterialVariableTemplate<Mat3>,
-	MaterialVariableTemplate<Mat4>,
-	MaterialVariableTemplate<TextureResourcePtr>>;
-
 /// Holds the shader variables. Its a container for shader program variables that share the same name
-class MaterialVariable : public MateriaVariableVisitable, public NonCopyable
+class MaterialVariable : public NonCopyable
 {
 	friend class Material;
 	friend class MaterialVariant;
 
 public:
-	using Base = MateriaVariableVisitable;
+	MaterialVariable();
 
-	MaterialVariable()
-	{
-	}
-
-	virtual ~MaterialVariable()
-	{
-	}
-
-	/// Get the name.
-	CString getName() const
-	{
-		return m_name.toCString();
-	}
+	~MaterialVariable();
 
 	/// Get the builtin info.
 	BuiltinMaterialVariableId getBuiltin() const
@@ -84,81 +57,62 @@ public:
 		return m_builtin;
 	}
 
-	U32 getTextureUnit() const
+	const ShaderProgramResourceInputVariable& getShaderProgramResourceInputVariable() const
 	{
-		ANKI_ASSERT(m_textureUnit != -1);
-		return static_cast<U32>(m_textureUnit);
-	}
-
-	ShaderVariableDataType getShaderVariableType() const
-	{
-		return m_varType;
-	}
-
-	Bool isInstanced() const
-	{
-		return m_instanced;
+		ANKI_ASSERT(m_input);
+		return *m_input;
 	}
 
 	template<typename T>
-	void writeShaderBlockMemory(const MaterialVariant& variant,
-		const T* elements,
-		U32 elementsCount,
-		void* buffBegin,
-		const void* buffEnd) const;
+	const T& getValue() const;
 
 protected:
-	U8 m_idx = MAX_U8; ///< Index in the Material::m_vars array.
-	ShaderVariableDataType m_varType = ShaderVariableDataType::NONE;
-	I16 m_textureUnit = -1;
-	String m_name;
-	BuiltinMaterialVariableId m_builtin = BuiltinMaterialVariableId::NONE;
-	Bool8 m_instanced = false;
+	const ShaderProgramResourceInputVariable* m_input = nullptr;
 
-	/// Deallocate stuff.
-	void destroy(ResourceAllocator<U8> alloc)
+	union
 	{
-		m_name.destroy(alloc);
-	}
-};
+		F32 m_float;
+		Vec2 m_vec2;
+		Vec3 m_vec3;
+		Vec4 m_vec4;
+		Mat3 m_mat3;
+		Mat4 m_mat4;
+	};
 
-/// Material variable with data. A complete type
-template<typename T>
-class MaterialVariableTemplate : public MaterialVariable
-{
-	friend class Material;
-	friend class MaterialVariant;
-
-public:
-	using Base = MaterialVariable;
-	using Type = T;
+	TextureResourcePtr m_tex;
 
-	MaterialVariableTemplate()
-	{
-		Base::setupVisitable(this);
-	}
-
-	~MaterialVariableTemplate()
-	{
-	}
+	BuiltinMaterialVariableId m_builtin = BuiltinMaterialVariableId::NONE;
+};
 
-	const T& getValue() const
-	{
-		checkGetValue();
-		return m_value;
+// Specialize the MaterialVariable::getValue
+#define ANKI_SPECIALIZE_GET_VALUE(t_, var_, shaderType_)                                                               \
+	template<>                                                                                                         \
+	inline const t_& MaterialVariable::getValue<t_>() const                                                            \
+	{                                                                                                                  \
+		ANKI_ASSERT(m_input);                                                                                          \
+		ANKI_ASSERT(m_input->getShaderVariableDataType() == ShaderVariableDataType::shaderType_);                      \
+		ANKI_ASSERT(m_builtin == BuiltinMaterialVariableId::NONE);                                                     \
+		return var_;                                                                                                   \
 	}
 
-private:
-	T m_value;
+ANKI_SPECIALIZE_GET_VALUE(F32, m_float, FLOAT)
+ANKI_SPECIALIZE_GET_VALUE(Vec2, m_vec2, VEC2)
+ANKI_SPECIALIZE_GET_VALUE(Vec3, m_vec3, VEC3)
+ANKI_SPECIALIZE_GET_VALUE(Vec4, m_vec4, VEC4)
+ANKI_SPECIALIZE_GET_VALUE(Mat3, m_mat3, MAT3)
+ANKI_SPECIALIZE_GET_VALUE(Mat4, m_mat4, MAT4)
 
-	void checkGetValue() const
-	{
-		ANKI_ASSERT(isTypeOf<MaterialVariableTemplate<T>>());
-		ANKI_ASSERT(MaterialVariable::m_builtin == BuiltinMaterialVariableId::NONE);
-	}
+template<>
+inline const TextureResourcePtr& MaterialVariable::getValue() const
+{
+	ANKI_ASSERT(m_input);
+	ANKI_ASSERT(m_input->getShaderVariableDataType() >= ShaderVariableDataType::SAMPLERS_FIRST
+		&& m_input->getShaderVariableDataType() <= ShaderVariableDataType::SAMPLERS_LAST);
+	ANKI_ASSERT(m_builtin == BuiltinMaterialVariableId::NONE);
+	return m_tex;
+}
 
-	ANKI_USE_RESULT Error init(U idx, const MaterialLoaderInputVariable& in, Material& mtl);
-};
+#undef ANKI_SPECIALIZE_GET_VALUE
 
 /// Material variant.
 class MaterialVariant : public NonCopyable
@@ -167,103 +121,64 @@ class MaterialVariant : public NonCopyable
 	friend class MaterialVariable;
 
 public:
-	MaterialVariant();
-
-	~MaterialVariant();
+	MaterialVariant()
+	{
+	}
 
-	ShaderProgramPtr getShaderProgram() const
+	~MaterialVariant()
 	{
-		return m_prog;
 	}
 
-	U getDefaultBlockSize() const
+	const ShaderProgramResourceVariant& getShaderProgramResourceVariant() const
 	{
-		ANKI_ASSERT(m_shaderBlockSize);
-		return m_shaderBlockSize;
+		ANKI_ASSERT(m_variant);
+		return *m_variant;
 	}
 
 	/// Return true of the the variable is active.
 	Bool variableActive(const MaterialVariable& var) const
 	{
-		return m_varActive[var.m_idx];
+		ANKI_ASSERT(var.m_input);
+		return m_variant->variableActive(*var.m_input);
 	}
 
-private:
-	/// All shaders except compute.
-	Array<ShaderResourcePtr, 5> m_shaders;
-	U32 m_shaderBlockSize = 0;
-	DynamicArray<ShaderVariableBlockInfo> m_blockInfo;
-	DynamicArray<Bool8> m_varActive;
-	ShaderProgramPtr m_prog;
-
-	ANKI_USE_RESULT Error init(const RenderingKey& key, Material& mtl, MaterialLoader& loader);
+	const ShaderProgramPtr& getShaderProgram() const
+	{
+		return m_variant->getProgram();
+	}
 
-	void destroy(ResourceAllocator<U8> alloc)
+	U getUniformBlockSize() const
 	{
-		m_blockInfo.destroy(alloc);
-		m_varActive.destroy(alloc);
+		return m_variant->getUniformBlockSize();
 	}
+
+private:
+	const ShaderProgramResourceVariant* m_variant = nullptr;
 };
 
 /// Material resource.
 ///
 /// Material XML file format:
 /// @code
-/// <material>
-/// 	[<levelsOfDetail>N</levelsOfDetail>]
-///
-/// 	[<shadow>0 | 1</shadow>]
-///
-/// 	[<forwardShading>0 | 1</forwardShading>]
-///
-/// 	[<depthTesting>0 | 1</depthTesting>]
+/// <material
+///		[shadow="0 | 1"]
+///		[forwardShading="0 | 1"]
+///		shaderProgram="path"/>
 ///
-/// 	[<wireframe>0 | 1</wireframe>]
+///		[<mutators>
+///			<mutator name="str" value="value"/>
+///		</mutators>]
 ///
-/// 	<programs>
-/// 		<program>
-/// 			<type>vert | tesc | tese | geom | frag</type>
-///
-///				[<inputs> (1)
-///					<input>
-///						<name>xx</name>
-///						<type>any glsl type</type>
-///						[<value> (2)
-///							[a_series_of_numbers | path/to/image.ankitex]
-///						</value>]
-///						[<const>0 | 1</const>] (3)
-/// 					[<inShadow>0 | 1</inShadow>] (4)
-///					</input>
-///				</inputs>]
-///
-/// 			<includes>
-/// 				<include>path/to/file.glsl</include>
-/// 				<include>path/to/file2.glsl</include>
-/// 			</includes>
-///
-/// 			<operations>
-/// 				<operation>
-/// 					<id>x</id>
-/// 					<returnType>any glsl type or void</returnType>
-/// 					<function>functionName</function>
-/// 					[<arguments>
-/// 						<argument>xx</argument>
-/// 						<argument>yy</argument>
-/// 					</arguments>]
-/// 				</operation>
-///
-/// 				<operation>...</operation>
-/// 			</operations>
-/// 		</program>
-///
-/// 		<program>...</program>
-/// 	</programs>
+///		[<inputs>
+///			<input
+///				shaderInput="name to shaderProg input"
+///				[value="values"] (1)
+///				[builtin="name"]/> (2)
+///		</inputs>]
 /// </material>
 /// @endcode
-/// (1): AKA uniforms
-/// (2): The \<value\> can be omitted for build-in variables
-/// (3): The \<const\> will mark a variable as constant and it cannot be changed at all. Default is 0
-/// (4): Optimization. Set to 1 if the var will be used in shadow passes as well
+/// (1): For non-builtins.
+/// (2): For builtins.
 class Material : public ResourceObject
 {
 	friend class MaterialVariable;
@@ -277,85 +192,65 @@ public:
 	/// Load a material file
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
 
-	/// For sorting
-	Bool operator<(const Material& b) const
-	{
-		ANKI_ASSERT(m_hash != 0 && b.m_hash != 0);
-		return m_hash < b.m_hash;
-	}
-
 	U getLodCount() const
 	{
 		return m_lodCount;
 	}
 
-	Bool getShadowEnabled() const
+	Bool castsShadow() const
 	{
 		return m_shadow;
 	}
 
-	Bool getTessellationEnabled() const
+	Bool hasTessellation() const
 	{
-		return m_tessellation;
+		return m_prog->hasTessellation();
 	}
 
-	Bool getForwardShading() const
+	Bool isForwardShading() const
 	{
 		return m_forwardShading;
 	}
 
 	Bool isInstanced() const
 	{
-		return m_instanced;
+		return m_prog->isInstanced();
 	}
 
-	const MaterialVariant& getVariant(const RenderingKey& key) const;
+	const MaterialVariant& getOrCreateVariant(const RenderingKey& key) const;
 
-	const DynamicArray<MaterialVariable*>& getVariables() const
+	const DynamicArray<MaterialVariable>& getVariables() const
 	{
 		return m_vars;
 	}
 
-	static U getInstanceGroupIdx(U instanceCount);
-
 private:
-	/// Used for sorting
-	U64 m_hash = 0;
+	ShaderProgramResourcePtr m_prog;
 
 	Bool8 m_shadow = true;
-	Bool8 m_tessellation = false;
 	Bool8 m_forwardShading = false;
 	U8 m_lodCount = 1;
-	Bool8 m_instanced = false;
-
-	DynamicArray<MaterialVariant> m_variants;
 
-	/// This is a matrix of variants. It holds indices to m_variants. If the idx is MAX_U16 then the variant is not
-	/// present.
-	Array4d<U16, U(Pass::COUNT), MAX_LODS, 2, MAX_INSTANCE_GROUPS> m_variantMatrix;
+	const ShaderProgramResourceMutator* m_lodMutator = nullptr;
+	const ShaderProgramResourceMutator* m_passMutator = nullptr;
+	const ShaderProgramResourceMutator* m_instanceMutator = nullptr;
 
-	DynamicArray<MaterialVariable*> m_vars;
+	DynamicArray<ShaderProgramResourceMutation> m_mutations;
 
-	/// Populate the m_varNames.
-	ANKI_USE_RESULT Error createVars(const MaterialLoader& loader);
+	/// Matrix of variants.
+	mutable Array3d<MaterialVariant, U(Pass::COUNT), MAX_LOD, MAX_INSTANCE_GROUPS> m_variantMatrix;
+	mutable SpinLock m_variantMatrixMtx;
 
-	/// Create the variants.
-	ANKI_USE_RESULT Error createVariants(MaterialLoader& loader);
+	DynamicArray<MaterialVariable> m_vars; ///< Non-const vars.
+	DynamicArray<ShaderProgramResourceConstantValue> m_constValues;
 
-	/// Create a unique shader source in chache. If already exists do nothing
-	ANKI_USE_RESULT Error createProgramSourceToCache(const String& source, ShaderType type, StringAuto& out);
-};
+	static U getInstanceGroupIdx(U instanceCount);
 
-template<typename T>
-inline void MaterialVariable::writeShaderBlockMemory(
-	const MaterialVariant& variant, const T* elements, U32 elementsCount, void* buffBegin, const void* buffEnd) const
-{
-	ANKI_ASSERT(Base::isTypeOf<MaterialVariableTemplate<T>>());
-	ANKI_ASSERT(m_varType == getShaderVariableTypeFromTypename<T>());
+	/// Parse whatever is inside the <inputs> tag.
+	ANKI_USE_RESULT Error parseInputs(XmlElement inputsEl);
 
-	const auto& blockInfo = variant.m_blockInfo[m_idx];
-	anki::writeShaderBlockMemory(m_varType, blockInfo, elements, elementsCount, buffBegin, buffEnd);
-}
+	ANKI_USE_RESULT Error parseMutators(XmlElement mutatorsEl);
+};
 /// @}
 
 } // end namespace anki

+ 0 - 985
src/anki/resource/MaterialLoader.cpp

@@ -1,985 +0,0 @@
-// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/resource/MaterialLoader.h>
-#include <anki/util/Assert.h>
-#include <anki/misc/Xml.h>
-#include <anki/util/Logger.h>
-#include <anki/util/Functions.h>
-#include <algorithm>
-
-namespace anki
-{
-
-/// Given a string return info about the shader
-static ANKI_USE_RESULT Error getShaderInfo(const CString& str, ShaderType& type, ShaderTypeBit& bit, U& idx)
-{
-	Error err = ErrorCode::NONE;
-
-	if(str == "vert")
-	{
-		type = ShaderType::VERTEX;
-		bit = ShaderTypeBit::VERTEX;
-		idx = 0;
-	}
-	else if(str == "tesc")
-	{
-		type = ShaderType::TESSELLATION_CONTROL;
-		bit = ShaderTypeBit::TESSELLATION_CONTROL;
-		idx = 1;
-	}
-	else if(str == "tese")
-	{
-		type = ShaderType::TESSELLATION_EVALUATION;
-		bit = ShaderTypeBit::TESSELLATION_EVALUATION;
-		idx = 2;
-	}
-	else if(str == "geom")
-	{
-		type = ShaderType::GEOMETRY;
-		bit = ShaderTypeBit::GEOMETRY;
-		idx = 3;
-	}
-	else if(str == "frag")
-	{
-		type = ShaderType::FRAGMENT;
-		bit = ShaderTypeBit::FRAGMENT;
-		idx = 4;
-	}
-	else
-	{
-		ANKI_RESOURCE_LOGE("Incorrect type %s", &str[0]);
-		err = ErrorCode::USER_DATA;
-	}
-
-	return err;
-}
-
-static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, ShaderVariableDataType& out)
-{
-	Error err = ErrorCode::NONE;
-
-	if(str == "float")
-	{
-		out = ShaderVariableDataType::FLOAT;
-	}
-	else if(str == "vec2")
-	{
-		out = ShaderVariableDataType::VEC2;
-	}
-	else if(str == "vec3")
-	{
-		out = ShaderVariableDataType::VEC3;
-	}
-	else if(str == "vec4")
-	{
-		out = ShaderVariableDataType::VEC4;
-	}
-	else if(str == "mat3")
-	{
-		out = ShaderVariableDataType::MAT3;
-	}
-	else if(str == "mat4")
-	{
-		out = ShaderVariableDataType::MAT4;
-	}
-	else if(str == "sampler2D")
-	{
-		out = ShaderVariableDataType::SAMPLER_2D;
-	}
-	else if(str == "sampler2DArray")
-	{
-		out = ShaderVariableDataType::SAMPLER_2D_ARRAY;
-	}
-	else if(str == "samplerCube")
-	{
-		out = ShaderVariableDataType::SAMPLER_CUBE;
-	}
-	else
-	{
-		ANKI_RESOURCE_LOGE("Incorrect variable type %s", &str[0]);
-		err = ErrorCode::USER_DATA;
-	}
-
-	return err;
-}
-
-static CString toString(ShaderVariableDataType in)
-{
-	CString out;
-
-	switch(in)
-	{
-	case ShaderVariableDataType::FLOAT:
-		out = "float";
-		break;
-	case ShaderVariableDataType::VEC2:
-		out = "vec2";
-		break;
-	case ShaderVariableDataType::VEC3:
-		out = "vec3";
-		break;
-	case ShaderVariableDataType::VEC4:
-		out = "vec4";
-		break;
-	case ShaderVariableDataType::MAT4:
-		out = "mat4";
-		break;
-	case ShaderVariableDataType::MAT3:
-		out = "mat3";
-		break;
-	case ShaderVariableDataType::SAMPLER_2D:
-		out = "sampler2D";
-		break;
-	case ShaderVariableDataType::SAMPLER_2D_ARRAY:
-		out = "sampler2DArray";
-		break;
-	case ShaderVariableDataType::SAMPLER_CUBE:
-		out = "samplerCube";
-		break;
-	default:
-		ANKI_ASSERT(0);
-	};
-
-	return out;
-}
-
-static ANKI_USE_RESULT Error computeBuiltin(const CString& name, BuiltinMaterialVariableId& out)
-{
-	if(name == "anki_mvp")
-	{
-		out = BuiltinMaterialVariableId::MVP_MATRIX;
-	}
-	else if(name == "anki_mv")
-	{
-		out = BuiltinMaterialVariableId::MV_MATRIX;
-	}
-	else if(name == "anki_vp")
-	{
-		out = BuiltinMaterialVariableId::VP_MATRIX;
-	}
-	else if(name == "anki_viewMatrix")
-	{
-		out = BuiltinMaterialVariableId::V_MATRIX;
-	}
-	else if(name == "anki_n")
-	{
-		out = BuiltinMaterialVariableId::NORMAL_MATRIX;
-	}
-	else if(name == "anki_billboardMvp")
-	{
-		out = BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX;
-	}
-	else if(name == "anki_cameraRotation")
-	{
-		out = BuiltinMaterialVariableId::CAMERA_ROT_MATRIX;
-	}
-	else if(name == "anki_tessLevel")
-	{
-		out = BuiltinMaterialVariableId::MAX_TESS_LEVEL;
-	}
-	else if(name == "anki_msDepthRt")
-	{
-		out = BuiltinMaterialVariableId::MS_DEPTH_MAP;
-	}
-	else if(name.find("anki_") == 0)
-	{
-		ANKI_RESOURCE_LOGE("Unknown builtin %s", &name[0]);
-		return ErrorCode::USER_DATA;
-	}
-	else
-	{
-		out = BuiltinMaterialVariableId::NONE;
-	}
-
-	return ErrorCode::NONE;
-}
-
-static ShaderVariableDataType getBuiltinType(BuiltinMaterialVariableId in)
-{
-	ShaderVariableDataType out = ShaderVariableDataType::SAMPLER_2D;
-
-	switch(in)
-	{
-	case BuiltinMaterialVariableId::MVP_MATRIX:
-		out = ShaderVariableDataType::MAT4;
-		break;
-	case BuiltinMaterialVariableId::MV_MATRIX:
-		out = ShaderVariableDataType::MAT4;
-		break;
-	case BuiltinMaterialVariableId::VP_MATRIX:
-		out = ShaderVariableDataType::MAT4;
-		break;
-	case BuiltinMaterialVariableId::V_MATRIX:
-		out = ShaderVariableDataType::MAT4;
-		break;
-	case BuiltinMaterialVariableId::NORMAL_MATRIX:
-		out = ShaderVariableDataType::MAT3;
-		break;
-	case BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX:
-		out = ShaderVariableDataType::MAT4;
-		break;
-	case BuiltinMaterialVariableId::CAMERA_ROT_MATRIX:
-		out = ShaderVariableDataType::MAT3;
-		break;
-	case BuiltinMaterialVariableId::MAX_TESS_LEVEL:
-		out = ShaderVariableDataType::FLOAT;
-		break;
-	case BuiltinMaterialVariableId::MS_DEPTH_MAP:
-		out = ShaderVariableDataType::SAMPLER_2D;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		break;
-	}
-
-	return out;
-}
-
-void MaterialLoaderInputVariable::move(MaterialLoaderInputVariable& b)
-{
-	m_type = b.m_type;
-	m_builtin = b.m_builtin;
-	m_flags = b.m_flags;
-
-	m_binding = b.m_binding;
-	m_index = b.m_index;
-
-	m_shaderDefinedMask = b.m_shaderDefinedMask;
-	m_shaderReferencedMask = b.m_shaderReferencedMask;
-}
-
-CString MaterialLoaderInputVariable::typeStr() const
-{
-	return toString(m_type);
-}
-
-MaterialLoader::MaterialLoader(GenericMemoryPoolAllocator<U8> alloc)
-	: m_alloc(alloc)
-	, m_source{{{alloc}, {alloc}, {alloc}, {alloc}, {alloc}}}
-	, m_sourceBaked{{{alloc}, {alloc}, {alloc}, {alloc}, {alloc}}}
-	, m_inputs{alloc}
-{
-}
-
-MaterialLoader::~MaterialLoader()
-{
-}
-
-Error MaterialLoader::parseXmlDocument(const XmlDocument& doc)
-{
-	XmlElement materialEl, el;
-
-	// <levelsOfDetail>
-	ANKI_CHECK(doc.getChildElement("material", materialEl));
-
-	// <levelsOfDetail>
-	ANKI_CHECK(materialEl.getChildElementOptional("levelsOfDetail", el));
-	if(el)
-	{
-		I64 tmp;
-		ANKI_CHECK(el.getI64(tmp));
-		m_lodCount = (tmp < 1) ? 1 : tmp;
-	}
-	else
-	{
-		m_lodCount = 1;
-	}
-
-	if(m_lodCount > MAX_LODS)
-	{
-		ANKI_RESOURCE_LOGW("Too many <levelsOfDetail>");
-		m_lodCount = MAX_LODS;
-	}
-
-	// <shadow>
-	ANKI_CHECK(materialEl.getChildElementOptional("shadow", el));
-	if(el)
-	{
-		I64 tmp;
-		ANKI_CHECK(el.getI64(tmp));
-		m_shadow = tmp;
-	}
-
-	// <forwardShading>
-	ANKI_CHECK(materialEl.getChildElementOptional("forwardShading", el));
-	if(el)
-	{
-		I64 tmp;
-		ANKI_CHECK(el.getI64(tmp));
-		m_forwardShading = tmp;
-	}
-
-	// <programs>
-	ANKI_CHECK(materialEl.getChildElement("programs", el));
-	ANKI_CHECK(parseProgramsTag(el));
-
-	return ErrorCode::NONE;
-}
-
-Error MaterialLoader::parseProgramsTag(const XmlElement& el)
-{
-	//
-	// First gather all the inputs
-	//
-	XmlElement programEl;
-	ANKI_CHECK(el.getChildElement("program", programEl));
-	do
-	{
-		ANKI_CHECK(parseInputsTag(programEl));
-
-		ANKI_CHECK(programEl.getNextSiblingElement("program", programEl));
-	} while(programEl);
-
-	processInputs();
-
-	//
-	// Then parse the includes, operations and other parts of the program
-	//
-	ANKI_CHECK(el.getChildElement("program", programEl));
-	do
-	{
-		ANKI_CHECK(parseProgramTag(programEl));
-
-		ANKI_CHECK(programEl.getNextSiblingElement("program", programEl));
-	} while(programEl);
-
-	//
-	// Sanity checks
-	//
-
-	// Check that all inputs are referenced
-	for(auto& in : m_inputs)
-	{
-		if(in.m_shaderDefinedMask != in.m_shaderReferencedMask)
-		{
-			ANKI_RESOURCE_LOGE("Variable not referenced or not defined %s", &in.m_name[0]);
-			return ErrorCode::USER_DATA;
-		}
-	}
-
-	return ErrorCode::NONE;
-}
-
-Error MaterialLoader::parseProgramTag(const XmlElement& programEl)
-{
-	XmlElement el;
-
-	// <type>
-	ANKI_CHECK(programEl.getChildElement("type", el));
-	CString type;
-	ANKI_CHECK(el.getText(type));
-	ShaderTypeBit glshaderbit;
-	ShaderType glshader;
-	U shaderidx;
-	ANKI_CHECK(getShaderInfo(type, glshader, glshaderbit, shaderidx));
-
-	auto& lines = m_source[shaderidx];
-
-	if(glshader == ShaderType::TESSELLATION_CONTROL || glshader == ShaderType::TESSELLATION_EVALUATION)
-	{
-		m_tessellation = true;
-	}
-
-	// Some instancing crap
-	lines.pushBackSprintf("\n#if INSTANCE_COUNT > 1\n"
-						  "#define INSTANCE_ID [%s]\n"
-						  "#define INSTANCED [INSTANCE_COUNT]\n"
-						  "#else\n"
-						  "#define INSTANCE_ID\n"
-						  "#define INSTANCED\n"
-						  "#endif\n",
-		glshader == ShaderType::VERTEX ? "gl_InstanceID" : "in_instanceId");
-
-	// <includes></includes>
-	XmlElement includesEl;
-	ANKI_CHECK(programEl.getChildElement("includes", includesEl));
-	XmlElement includeEl;
-	ANKI_CHECK(includesEl.getChildElement("include", includeEl));
-
-	do
-	{
-		CString tmp;
-		ANKI_CHECK(includeEl.getText(tmp));
-		lines.pushBackSprintf("#include \"%s\"", &tmp[0]);
-
-		ANKI_CHECK(includeEl.getNextSiblingElement("include", includeEl));
-	} while(includeEl);
-
-	// Inputs
-
-	// Block
-	if((m_uniformBlockReferencedMask & glshaderbit) != ShaderTypeBit::NONE)
-	{
-		lines.pushBack("\nlayout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform u00_\n{");
-
-		for(Input& in : m_inputs)
-		{
-			if(in.m_flags.m_inBlock)
-			{
-				lines.pushBack(&in.m_line[0]);
-			}
-		}
-
-		lines.pushBack("};");
-	}
-
-	// Other variables
-	for(Input& in : m_inputs)
-	{
-		if(!in.m_flags.m_inBlock && (in.m_shaderDefinedMask & glshaderbit) != ShaderTypeBit::NONE
-			&& !in.m_flags.m_specialBuiltin)
-		{
-			lines.pushBack(&in.m_line[0]);
-		}
-	}
-
-	// <operations></operations>
-	lines.pushBack("\nvoid main()\n{");
-
-	if(!m_forwardShading && glshader == ShaderType::FRAGMENT)
-	{
-		// Write default values
-		lines.pushBack("#if defined(writeRts_DEFINED)");
-		lines.pushBack("writeRts(vec3(0.0), vec3(1.0), vec3(0.04), 0.5, 0.0, 0.0, 0.0);");
-		lines.pushBack("#endif");
-	}
-
-	XmlElement opsEl;
-	ANKI_CHECK(programEl.getChildElement("operations", opsEl));
-	XmlElement opEl;
-	ANKI_CHECK(opsEl.getChildElement("operation", opEl));
-	do
-	{
-		String out;
-		ANKI_CHECK(parseOperationTag(opEl, glshader, glshaderbit, out));
-		lines.pushBack(&out[0]);
-		out.destroy(m_alloc);
-
-		// Advance
-		ANKI_CHECK(opEl.getNextSiblingElement("operation", opEl));
-	} while(opEl);
-
-	lines.pushBack("}\n");
-	return ErrorCode::NONE;
-}
-
-Error MaterialLoader::parseInputsTag(const XmlElement& programEl)
-{
-	XmlElement el;
-	CString cstr;
-	XmlElement inputsEl;
-	ANKI_CHECK(programEl.getChildElementOptional("inputs", inputsEl));
-	if(!inputsEl)
-	{
-		return ErrorCode::NONE;
-	}
-
-	// Get shader type
-	ShaderTypeBit glshaderbit;
-	ShaderType glshader;
-	U shaderidx;
-	ANKI_CHECK(programEl.getChildElement("type", el));
-	ANKI_CHECK(el.getText(cstr));
-	ANKI_CHECK(getShaderInfo(cstr, glshader, glshaderbit, shaderidx));
-
-	XmlElement inputEl;
-	ANKI_CHECK(inputsEl.getChildElement("input", inputEl));
-	do
-	{
-		Input in(m_alloc);
-
-		// <name>
-		ANKI_CHECK(inputEl.getChildElement("name", el));
-		ANKI_CHECK(el.getText(cstr));
-		in.m_name.create(cstr);
-
-		ANKI_CHECK(computeBuiltin(in.m_name.toCString(), in.m_builtin));
-
-		if(in.m_builtin != BuiltinMaterialVariableId::NONE)
-		{
-			in.m_flags.m_builtin = true;
-			if(in.m_builtin == BuiltinMaterialVariableId::MS_DEPTH_MAP)
-			{
-				in.m_flags.m_specialBuiltin = true;
-			}
-		}
-
-		// Check if instanced
-		if(in.m_builtin == BuiltinMaterialVariableId::MVP_MATRIX || in.m_builtin == BuiltinMaterialVariableId::MV_MATRIX
-			|| in.m_builtin == BuiltinMaterialVariableId::NORMAL_MATRIX
-			|| in.m_builtin == BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX)
-		{
-			in.m_flags.m_instanced = true;
-			m_instanced = true;
-		}
-
-		// <type>
-		ANKI_CHECK(inputEl.getChildElement("type", el));
-		ANKI_CHECK(el.getText(cstr));
-		ANKI_CHECK(computeShaderVariableDataType(cstr, in.m_type));
-
-		if(in.m_flags.m_builtin)
-		{
-			if(getBuiltinType(in.m_builtin) != in.m_type)
-			{
-				ANKI_RESOURCE_LOGE("Builtin variable %s cannot be of type %s", &in.m_name[0], &cstr[0]);
-				return ErrorCode::USER_DATA;
-			}
-		}
-
-		// <const>
-		ANKI_CHECK(inputEl.getChildElementOptional("const", el));
-		if(el)
-		{
-			I64 tmp;
-			ANKI_CHECK(el.getI64(tmp));
-			in.m_flags.m_const = tmp;
-		}
-
-		if(in.m_flags.m_builtin && in.m_flags.m_const)
-		{
-			ANKI_RESOURCE_LOGE("Builtins cannot be consts: %s", &in.m_name[0]);
-			return ErrorCode::USER_DATA;
-		}
-
-		// <value>
-		ANKI_CHECK(inputEl.getChildElementOptional("value", el));
-		if(el)
-		{
-			ANKI_CHECK(el.getText(cstr));
-
-			if(!cstr)
-			{
-				ANKI_RESOURCE_LOGE("Value tag is empty for: %s", &in.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-
-			if(in.m_flags.m_builtin)
-			{
-				ANKI_RESOURCE_LOGE("Builtins cannot have value: %s", &in.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-
-			in.m_value.splitString(cstr, ' ');
-		}
-		else
-		{
-			if(!in.m_flags.m_builtin)
-			{
-				ANKI_RESOURCE_LOGE("Non-builtins should have a value: %s", &in.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-
-			if(in.m_flags.m_const)
-			{
-				ANKI_RESOURCE_LOGE("Consts should have a value: %s", &in.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-		}
-
-		// <inShadow>
-		ANKI_CHECK(inputEl.getChildElementOptional("inShadow", el));
-		if(el)
-		{
-			I64 tmp;
-			ANKI_CHECK(el.getI64(tmp));
-			in.m_flags.m_inShadow = tmp;
-		}
-		else
-		{
-			in.m_flags.m_inShadow = true;
-		}
-
-		// Set some stuff
-		if(in.m_type >= ShaderVariableDataType::SAMPLERS_FIRST && in.m_type <= ShaderVariableDataType::SAMPLERS_LAST)
-		{
-			in.m_flags.m_texture = true;
-		}
-		else if(!in.m_flags.m_const)
-		{
-			in.m_flags.m_inBlock = true;
-			m_uniformBlockReferencedMask |= glshaderbit;
-		}
-
-		// Now you have the info to check if duplicate
-		Input* duplicateInp = nullptr;
-		Error err = m_inputs.iterateForward([&duplicateInp, &in](Input& inp) -> Error {
-			if(in.m_name == inp.m_name)
-			{
-				duplicateInp = &in;
-				return ErrorCode::NONE;
-			}
-
-			return ErrorCode::NONE;
-		});
-		(void)err;
-
-		if(duplicateInp != nullptr)
-		{
-			// Duplicate. Make sure it's the same as the other shader
-			Bool same = duplicateInp->duplicate(in);
-
-			if(!same)
-			{
-				ANKI_RESOURCE_LOGE("Variable defined differently between shaders: %s", &in.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-
-			duplicateInp->m_shaderDefinedMask |= glshaderbit;
-
-			if(duplicateInp->m_flags.m_inBlock)
-			{
-				m_uniformBlockReferencedMask |= glshaderbit;
-			}
-		}
-		else
-		{
-			in.m_shaderDefinedMask = glshaderbit;
-
-			m_inputs.emplaceBack(std::move(in));
-		}
-
-		// Advance
-		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
-	} while(inputEl);
-
-	return ErrorCode::NONE;
-}
-
-void MaterialLoader::processInputs()
-{
-	// Sort the variables to decrease the change of creating unique shaders
-	m_inputs.sort([](const Input& a, const Input& b) { return a.m_name < b.m_name; });
-
-	for(auto& in : m_inputs)
-	{
-		if(!in.m_flags.m_const)
-		{
-			// Handle NON-consts
-
-			if(in.m_flags.m_texture)
-			{
-				// Not in block
-
-				if(!in.m_flags.m_specialBuiltin)
-				{
-					in.m_binding = m_texBinding++;
-
-					in.m_line.sprintf("layout(ANKI_TEX_BINDING(0, %u)) uniform %s tex%u;",
-						in.m_binding,
-						&in.typeStr()[0],
-						in.m_binding);
-				}
-			}
-			else if(in.m_flags.m_inBlock)
-			{
-				// In block
-
-				in.m_index = m_nextIndex++;
-				const Bool isMatrix = in.m_type >= ShaderVariableDataType::MATRIX_FIRST
-					&& in.m_type <= ShaderVariableDataType::MATRIX_LAST;
-
-				in.m_line.sprintf("#if %s\n"
-								  "%s var%u_u%s;\n"
-								  "#define var%u %s(var%u_u)\n"
-								  "#endif",
-					!in.m_flags.m_inShadow ? "PASS == COLOR" : "1",
-					&in.typeStr()[0],
-					in.m_index,
-					in.m_flags.m_instanced ? " INSTANCED" : "",
-					in.m_index,
-					(isMatrix) ? "" : "readFirstInvocationARB",
-					in.m_index);
-			}
-			else
-			{
-				ANKI_ASSERT(0);
-			}
-		}
-		else
-		{
-			// Handle consts
-
-			in.m_index = m_nextIndex++;
-
-			String initList;
-			in.m_value.join(m_alloc, ", ", initList);
-
-			in.m_line.sprintf("const %s var%u = %s(%s);", &in.typeStr()[0], in.m_index, &in.typeStr()[0], &initList[0]);
-
-			initList.destroy(m_alloc);
-		}
-	}
-}
-
-Error MaterialLoader::parseOperationTag(
-	const XmlElement& operationTag, ShaderType glshader, ShaderTypeBit glshaderbit, String& out)
-{
-	CString cstr;
-	XmlElement el;
-
-	static const char* OUT = "out";
-
-	CString funcName;
-	StringListAuto argsList(m_alloc);
-
-	// <id>
-	I64 id;
-	ANKI_CHECK(operationTag.getChildElement("id", el));
-	ANKI_CHECK(el.getI64(id));
-
-	// <returnType>
-	XmlElement retTypeEl;
-	ANKI_CHECK(operationTag.getChildElement("returnType", retTypeEl));
-	ANKI_CHECK(retTypeEl.getText(cstr));
-	Bool retTypeVoid = cstr == "void";
-
-	// <function>
-	ANKI_CHECK(operationTag.getChildElement("function", el));
-	ANKI_CHECK(el.getText(funcName));
-
-	// <arguments>
-	XmlElement argsEl;
-	ANKI_CHECK(operationTag.getChildElementOptional("arguments", argsEl));
-
-	if(argsEl)
-	{
-		// Get all arguments
-		XmlElement argEl;
-		ANKI_CHECK(argsEl.getChildElement("argument", argEl));
-		do
-		{
-			CString arg;
-			ANKI_CHECK(argEl.getText(arg));
-
-			// Search for all the inputs and mark the appropriate
-			Input* input = nullptr;
-			for(Input& in : m_inputs)
-			{
-				// Check that the first part of the string is equal to the variable and the following char is '['
-				if(in.m_name == arg)
-				{
-					input = &in;
-					in.m_shaderReferencedMask |= glshaderbit;
-					break;
-				}
-			}
-
-			// The argument should be an input variable or an outXX or global
-			if(input == nullptr)
-			{
-				if(arg.find(OUT) != 0)
-				{
-					ANKI_RESOURCE_LOGE("Incorrect argument: %s", &arg[0]);
-					return ErrorCode::USER_DATA;
-				}
-			}
-
-			// Add to a list and do something special if instanced
-			if(input)
-			{
-				if(!input->m_flags.m_specialBuiltin)
-				{
-					argsList.pushBackSprintf("%s%d%s",
-						input->m_flags.m_texture ? "tex" : "var",
-						!input->m_flags.m_texture ? input->m_index : input->m_binding,
-						input->m_flags.m_instanced ? " INSTANCE_ID" : "");
-				}
-				else
-				{
-					ANKI_CHECK(argEl.getText(cstr));
-					argsList.pushBackSprintf(&cstr[0]);
-				}
-
-				m_instanceIdMask |= glshaderbit;
-			}
-			else
-			{
-				ANKI_CHECK(argEl.getText(cstr));
-				argsList.pushBackSprintf(&cstr[0]);
-			}
-
-			// Advance
-			ANKI_CHECK(argEl.getNextSiblingElement("argument", argEl));
-		} while(argEl);
-	}
-
-	// Now write everything
-	StringListAuto lines(m_alloc);
-	lines.pushBackSprintf("#if defined(%s_DEFINED)", &funcName[0]);
-
-	// Write the defines for the operationOuts
-	for(const String& arg : argsList)
-	{
-		if(arg.find(OUT) == 0)
-		{
-			lines.pushBackSprintf(" && defined(%s_DEFINED)", &arg[0]);
-		}
-	}
-	lines.pushBackSprintf("\n");
-
-	if(!retTypeVoid)
-	{
-		ANKI_CHECK(retTypeEl.getText(cstr));
-		lines.pushBackSprintf("#define out%u_DEFINED\n"
-							  "%s out%u = ",
-			id,
-			&cstr[0],
-			id);
-	}
-
-	// write the "func(args...)" of "blah = func(args...)"
-	StringAuto argsStr(m_alloc);
-
-	if(!argsList.isEmpty())
-	{
-		argsList.join(m_alloc, ", ", argsStr);
-	}
-
-	lines.pushBackSprintf("%s(%s);\n#endif", &funcName[0], (argsStr.isEmpty()) ? "" : &argsStr[0]);
-
-	// Bake final string
-	lines.join(m_alloc, "", out);
-
-	return ErrorCode::NONE;
-}
-
-void MaterialLoader::mutate(const RenderingKey& key)
-{
-	U instanceCount = key.m_instanceCount;
-	Bool tessellation = key.m_tessellation;
-	U lod = key.m_lod;
-	Pass pass = key.m_pass;
-
-	// Preconditions
-	if(tessellation)
-	{
-		ANKI_ASSERT(m_tessellation);
-	}
-
-	if(instanceCount > 1)
-	{
-		ANKI_ASSERT(m_instanced);
-	}
-
-	// Compute the block info for each var
-	m_blockSize = 0;
-	for(Input& in : m_inputs)
-	{
-		// Invalidate the var's block info
-		in.m_blockInfo = ShaderVariableBlockInfo();
-
-		if(!in.m_flags.m_inBlock)
-		{
-			continue;
-		}
-
-		if(pass == Pass::SM && !in.m_flags.m_inShadow)
-		{
-			continue;
-		}
-
-		// std140 rules
-		in.m_blockInfo.m_offset = m_blockSize;
-		in.m_blockInfo.m_arraySize = in.m_flags.m_instanced ? instanceCount : 1;
-
-		if(in.m_type == ShaderVariableDataType::FLOAT)
-		{
-			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(in.m_blockInfo.m_arraySize == 1)
-			{
-				// No need to align the in.m_offset
-				m_blockSize += sizeof(F32);
-			}
-			else
-			{
-				alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-				m_blockSize += sizeof(Vec4) * in.m_blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_type == ShaderVariableDataType::VEC2)
-		{
-			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(in.m_blockInfo.m_arraySize == 1)
-			{
-				alignRoundUp(sizeof(Vec2), in.m_blockInfo.m_offset);
-				m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec2);
-			}
-			else
-			{
-				alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-				m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec4) * in.m_blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_type == ShaderVariableDataType::VEC3)
-		{
-			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(in.m_blockInfo.m_arraySize == 1)
-			{
-				m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec3);
-			}
-			else
-			{
-				m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec4) * in.m_blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_type == ShaderVariableDataType::VEC4)
-		{
-			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
-			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-			m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec4) * in.m_blockInfo.m_arraySize;
-		}
-		else if(in.m_type == ShaderVariableDataType::MAT3)
-		{
-			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-			in.m_blockInfo.m_arrayStride = sizeof(Vec4) * 3;
-			m_blockSize = in.m_blockInfo.m_offset + sizeof(Vec4) * 3 * in.m_blockInfo.m_arraySize;
-			in.m_blockInfo.m_matrixStride = sizeof(Vec4);
-		}
-		else if(in.m_type == ShaderVariableDataType::MAT4)
-		{
-			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
-			in.m_blockInfo.m_arrayStride = sizeof(Mat4);
-			m_blockSize = in.m_blockInfo.m_offset + sizeof(Mat4) * in.m_blockInfo.m_arraySize;
-			in.m_blockInfo.m_matrixStride = sizeof(Vec4);
-		}
-		else
-		{
-			ANKI_ASSERT(0);
-		}
-	}
-
-	// Update the defines
-	StringAuto defines(m_alloc);
-	defines.sprintf("#define INSTANCE_COUNT %u\n"
-					"#define TESSELATION %u\n"
-					"#define LOD %u\n"
-					"#define PASS %s\n",
-		instanceCount,
-		tessellation,
-		lod,
-		(pass == Pass::SM) ? "DEPTH" : "COLOR");
-
-	// Merge strings
-	for(U i = 0; i < m_sourceBaked.getSize(); i++)
-	{
-		if(!m_source[i].isEmpty())
-		{
-			m_sourceBaked[i].destroy();
-
-			StringAuto tmp(m_alloc);
-			m_source[i].join("\n", tmp);
-
-			m_sourceBaked[i].sprintf("%s%s", &defines[0], &tmp[0]);
-		}
-	}
-}
-
-} // end namespace anki

+ 0 - 199
src/anki/resource/MaterialLoader.h

@@ -1,199 +0,0 @@
-// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/util/StringList.h>
-#include <anki/Gr.h>
-#include <anki/resource/Common.h>
-#include <anki/resource/Material.h>
-
-namespace anki
-{
-
-// Forward
-class XmlElement;
-
-/// Material loader variable. It's the information on whatever is inside \<input\>
-class MaterialLoaderInputVariable : public NonCopyable
-{
-public:
-	StringAuto m_name;
-	StringListAuto m_value;
-	StringAuto m_line;
-
-	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
-	BuiltinMaterialVariableId m_builtin = BuiltinMaterialVariableId::NONE;
-
-	// Flags
-	class Flags
-	{
-	public:
-		Bool8 m_inBlock = false;
-		Bool8 m_texture = false;
-		Bool8 m_builtin = false;
-		Bool8 m_const = false;
-		Bool8 m_instanced = false;
-		Bool8 m_inShadow = false;
-		Bool8 m_specialBuiltin = false;
-
-		Bool operator==(const Flags& b) const
-		{
-			return memcmp(this, &b, sizeof(*this)) == 0;
-		}
-	} m_flags;
-
-	I16 m_binding = -1; ///< Texture unit
-	I16 m_index = -1;
-	ShaderVariableBlockInfo m_blockInfo;
-
-	ShaderTypeBit m_shaderDefinedMask = ShaderTypeBit::NONE; ///< Defined in
-	ShaderTypeBit m_shaderReferencedMask = ShaderTypeBit::NONE; ///< Referenced
-
-	MaterialLoaderInputVariable(GenericMemoryPoolAllocator<U8> alloc)
-		: m_name(alloc)
-		, m_value(alloc)
-		, m_line(alloc)
-	{
-	}
-
-	MaterialLoaderInputVariable(MaterialLoaderInputVariable&& b)
-		: m_name(std::move(b.m_name))
-		, m_value(std::move(b.m_value))
-		, m_line(std::move(b.m_line))
-	{
-		move(b);
-	}
-
-	~MaterialLoaderInputVariable()
-	{
-	}
-
-	MaterialLoaderInputVariable& operator=(MaterialLoaderInputVariable&& b)
-	{
-		m_name = std::move(b.m_name);
-		m_value = std::move(b.m_value);
-		m_line = std::move(b.m_line);
-		move(b);
-		return *this;
-	}
-
-	Bool duplicate(const MaterialLoaderInputVariable& b) const
-	{
-		return b.m_name == m_name && b.m_value == m_value && b.m_type == m_type && b.m_builtin == m_builtin
-			&& b.m_flags == m_flags;
-	}
-
-	CString typeStr() const;
-
-private:
-	void move(MaterialLoaderInputVariable& b);
-};
-
-/// Creator of shader programs. This class parses between
-/// @code <material></material> @endcode and creates the source of a custom program.
-///
-/// @note Be carefull when you change the methods. Some change may create more unique shaders and this is never good.
-class MaterialLoader
-{
-public:
-	using Input = MaterialLoaderInputVariable;
-
-	explicit MaterialLoader(GenericMemoryPoolAllocator<U8> alloc);
-
-	~MaterialLoader();
-
-	ANKI_USE_RESULT Error parseXmlDocument(const XmlDocument& doc);
-
-	/// Get the shader source code
-	const String& getShaderSource(ShaderType shaderType_) const
-	{
-		U shaderType = enumToType(shaderType_);
-		ANKI_ASSERT(!m_sourceBaked[shaderType].isEmpty());
-		return m_sourceBaked[shaderType];
-	}
-
-	/// After parsing the program mutate the sources for a specific use case.
-	void mutate(const RenderingKey& key);
-
-	/// Iterate all the variables.
-	template<typename TFunc>
-	ANKI_USE_RESULT Error iterateAllInputVariables(TFunc func) const
-	{
-		for(const Input& in : m_inputs)
-		{
-			if(!in.m_flags.m_const && !in.m_flags.m_specialBuiltin)
-			{
-				ANKI_CHECK(func(in));
-			}
-		}
-		return ErrorCode::NONE;
-	}
-
-	Bool getTessellationEnabled() const
-	{
-		return m_tessellation;
-	}
-
-	U32 getUniformBlockSize() const
-	{
-		return m_blockSize;
-	}
-
-	Bool isInstanced() const
-	{
-		return m_instanced;
-	}
-
-	U getLodCount() const
-	{
-		return m_lodCount;
-	}
-
-	Bool getShadowEnabled() const
-	{
-		return m_shadow;
-	}
-
-	Bool isForwardShading() const
-	{
-		return m_forwardShading;
-	}
-
-private:
-	GenericMemoryPoolAllocator<char> m_alloc;
-	Array<StringListAuto, 5> m_source; ///< Shader program final source
-	Array<StringAuto, 5> m_sourceBaked; ///< Final source baked
-	ListAuto<Input> m_inputs;
-	ShaderTypeBit m_uniformBlockReferencedMask = ShaderTypeBit::NONE;
-	U32 m_blockSize = 0;
-	Bool8 m_instanced = false;
-	U32 m_texBinding = 0;
-	ShaderTypeBit m_instanceIdMask = ShaderTypeBit::NONE;
-	Bool8 m_tessellation = false;
-	U8 m_lodCount = 0;
-	Bool8 m_shadow = true;
-	Bool8 m_forwardShading = false;
-	U32 m_nextIndex = 0;
-
-	/// Parse what is within the
-	/// @code <programs></programs> @endcode
-	ANKI_USE_RESULT Error parseProgramsTag(const XmlElement& el);
-
-	/// Parse what is within the
-	/// @code <program></program> @endcode
-	ANKI_USE_RESULT Error parseProgramTag(const XmlElement& el);
-
-	/// Parse what is within the @code <inputs></inputs> @endcode
-	ANKI_USE_RESULT Error parseInputsTag(const XmlElement& programEl);
-
-	void processInputs();
-
-	/// Parse what is within the @code <operation></operation> @endcode
-	ANKI_USE_RESULT Error parseOperationTag(
-		const XmlElement& el, ShaderType glshader, ShaderTypeBit glshaderbit, String& out);
-};
-
-} // end namespace anki

+ 1 - 1
src/anki/resource/Model.cpp

@@ -37,7 +37,7 @@ void ModelPatch::getRenderingDataSub(
 		RenderingKey mtlKey = key;
 		mtlKey.m_lod = min<U>(key.m_lod, m_mtl->getLodCount() - 1);
 
-		const MaterialVariant& variant = m_mtl->getVariant(mtlKey);
+		const MaterialVariant& variant = m_mtl->getOrCreateVariant(mtlKey);
 
 		inf.m_program = variant.getShaderProgram();
 	}

+ 1 - 1
src/anki/resource/Model.h

@@ -124,7 +124,7 @@ public:
 private:
 	Model* m_model ANKI_DBG_NULLIFY;
 
-	Array<MeshResourcePtr, MAX_LODS> m_meshes; ///< One for each LOD
+	Array<MeshResourcePtr, MAX_LOD> m_meshes; ///< One for each LOD
 	U8 m_meshCount = 0;
 	MaterialResourcePtr m_mtl;
 

+ 4 - 4
src/anki/resource/ParticleEmitterResource.cpp

@@ -40,7 +40,7 @@ static ANKI_USE_RESULT Error xmlF32(const XmlElement& el_, const CString& str, F
 	}
 
 	F64 tmp;
-	err = el.getF64(tmp);
+	err = el.getNumber(tmp);
 	if(!err)
 	{
 		out = tmp;
@@ -61,7 +61,7 @@ static ANKI_USE_RESULT Error xmlU32(const XmlElement& el_, const CString& str, U
 	}
 
 	I64 tmp;
-	err = el.getI64(tmp);
+	err = el.getNumber(tmp);
 	if(!err)
 	{
 		out = static_cast<U32>(tmp);
@@ -200,8 +200,8 @@ void ParticleEmitterResource::getRenderingInfo(U lod, ShaderProgramPtr& prog) co
 {
 	lod = min<U>(lod, m_lodCount - 1);
 
-	RenderingKey key(Pass::MS_FS, lod, false, 1);
-	const MaterialVariant& variant = m_material->getVariant(key);
+	RenderingKey key(Pass::MS_FS, lod, 1);
+	const MaterialVariant& variant = m_material->getOrCreateVariant(key);
 	prog = variant.getShaderProgram();
 }
 

+ 12 - 9
src/anki/resource/RenderingKey.h

@@ -24,37 +24,41 @@ class RenderingKey
 public:
 	Pass m_pass;
 	U8 m_lod;
-	Bool8 m_tessellation;
 	U8 m_instanceCount;
 
-	RenderingKey(Pass pass, U8 lod, Bool tessellation, U instanceCount)
+	RenderingKey(Pass pass, U8 lod, U instanceCount)
 		: m_pass(pass)
 		, m_lod(lod)
-		, m_tessellation(tessellation)
 		, m_instanceCount(instanceCount)
 	{
 		ANKI_ASSERT(m_instanceCount <= MAX_INSTANCES && m_instanceCount != 0);
-		ANKI_ASSERT(m_lod <= MAX_LODS);
+		ANKI_ASSERT(m_lod <= MAX_LOD);
 	}
 
 	RenderingKey()
-		: RenderingKey(Pass::MS_FS, 0, false, 1)
+		: RenderingKey(Pass::MS_FS, 0, 1)
 	{
 	}
 
 	RenderingKey(const RenderingKey& b)
-		: RenderingKey(b.m_pass, b.m_lod, b.m_tessellation, b.m_instanceCount)
+		: RenderingKey(b.m_pass, b.m_lod, b.m_instanceCount)
 	{
 	}
 };
 
+template<>
+constexpr Bool isPacked<RenderingKey>()
+{
+	return sizeof(RenderingKey) == 3;
+}
+
 /// The hash function
 class RenderingKeyHasher
 {
 public:
 	PtrSize operator()(const RenderingKey& key) const
 	{
-		return U8(key.m_pass) | (key.m_lod << 8) | (key.m_tessellation << 16) | (key.m_instanceCount << 24);
+		return U8(key.m_pass) | (key.m_lod << 8) | (key.m_instanceCount << 16);
 	}
 };
 
@@ -64,8 +68,7 @@ class RenderingKeyEqual
 public:
 	Bool operator()(const RenderingKey& a, const RenderingKey& b) const
 	{
-		return a.m_pass == b.m_pass && a.m_lod == b.m_lod && a.m_tessellation == b.m_tessellation
-			&& a.m_instanceCount == b.m_instanceCount;
+		return a.m_pass == b.m_pass && a.m_lod == b.m_lod && a.m_instanceCount == b.m_instanceCount;
 	}
 };
 

+ 2 - 0
src/anki/resource/ResourceManager.cpp

@@ -16,6 +16,7 @@
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/GenericResource.h>
 #include <anki/resource/TextureAtlas.h>
+#include <anki/resource/ShaderProgramResource.h>
 #include <anki/util/Logger.h>
 #include <anki/misc/ConfigSet.h>
 
@@ -67,6 +68,7 @@ Error ResourceManager::create(ResourceManagerInitInfo& init)
 	ANKI_RESOURCE(CollisionResource)
 	ANKI_RESOURCE(GenericResource)
 	ANKI_RESOURCE(TextureAtlas)
+	ANKI_RESOURCE(ShaderProgramResource)
 
 #undef ANKI_RESOURCE
 

+ 2 - 1
src/anki/resource/ResourceManager.h

@@ -115,7 +115,8 @@ class ResourceManager : public TypeResourceManager<Animation>,
 						public TypeResourceManager<DummyRsrc>,
 						public TypeResourceManager<CollisionResource>,
 						public TypeResourceManager<GenericResource>,
-						public TypeResourceManager<TextureAtlas>
+						public TypeResourceManager<TextureAtlas>,
+						public TypeResourceManager<ShaderProgramResource>
 {
 	template<typename T>
 	friend class ResourcePtrDeleter;

+ 1 - 1
src/anki/resource/ResourceObject.h

@@ -30,7 +30,7 @@ public:
 	virtual ~ResourceObject();
 
 	/// @privatesection
-	ResourceManager& getManager()
+	ResourceManager& getManager() const
 	{
 		return *m_manager;
 	}

+ 21 - 16
src/anki/resource/ShaderLoader.cpp

@@ -34,37 +34,42 @@ Error ShaderLoader::parseFile(const ResourceFilename& filename)
 	ANKI_CHECK(fileExtensionToShaderType(filename, m_type));
 
 	// Parse files recursively
-	Error err = parseFileIncludes(filename, 0);
+	ANKI_CHECK(parseFileIncludes(filename, 0));
 
-	if(!err)
-	{
-		m_sourceLines.join(m_alloc, "\n", m_shaderSource);
-	}
+	m_sourceLines.join(m_alloc, "\n", m_shaderSource);
 
-	return err;
+	return ErrorCode::NONE;
 }
 
 Error ShaderLoader::parseFileIncludes(ResourceFilename filename, U32 depth)
+{
+	// load file in lines
+	ResourceFilePtr file;
+	ANKI_CHECK(m_manager->getFilesystem().openFile(filename, file));
+
+	StringAuto txt(m_alloc);
+	ANKI_CHECK(file->readAllText(m_alloc, txt));
+
+	ANKI_CHECK(parseSource(txt.toCString(), depth));
+
+	return ErrorCode::NONE;
+}
+
+Error ShaderLoader::parseSource(CString src, U depth)
 {
 	// first check the depth
 	if(depth > MAX_INCLUDE_DEPTH)
 	{
-		ANKI_RESOURCE_LOGE("The include depth is too high. "
-						   "Probably circular includance");
+		ANKI_RESOURCE_LOGE("The include depth is too high. Probably circular includance");
 		return ErrorCode::USER_DATA;
 	}
 
-	// load file in lines
-	StringAuto txt(m_alloc);
+	// Split the file to lines
 	StringListAuto lines(m_alloc);
-
-	ResourceFilePtr file;
-	ANKI_CHECK(m_manager->getFilesystem().openFile(filename, file));
-	ANKI_CHECK(file->readAllText(TempResourceAllocator<char>(m_alloc), txt));
-	lines.splitString(txt.toCString(), '\n');
+	lines.splitString(src, '\n');
 	if(lines.getSize() < 1)
 	{
-		ANKI_RESOURCE_LOGE("File is empty: %s", &filename[0]);
+		ANKI_RESOURCE_LOGE("Source is empty");
 		return ErrorCode::USER_DATA;
 	}
 

+ 16 - 4
src/anki/resource/ShaderLoader.h

@@ -31,14 +31,23 @@ public:
 		m_sourceLines.destroy(m_alloc);
 	}
 
-	/// Parse a PrePreprocessor formated GLSL file. Use the accessors to get the output
-	/// @param filename The file to parse
+	/// Run an include preprocessor in a GLSL file.
+	/// @param filename The file to parse.
 	ANKI_USE_RESULT Error parseFile(const ResourceFilename& filename);
 
-	const String& getShaderSource() const
+	/// Run an include preprocessor in some GLSL source.
+	/// @param src The source to parse.
+	ANKI_USE_RESULT Error parseSourceString(CString src)
+	{
+		ANKI_CHECK(parseSource(src, 0));
+		m_sourceLines.join(m_alloc, "\n", m_shaderSource);
+		return ErrorCode::NONE;
+	}
+
+	CString getShaderSource() const
 	{
 		ANKI_ASSERT(!m_shaderSource.isEmpty());
-		return m_shaderSource;
+		return m_shaderSource.toCString();
 	}
 
 	ShaderType getShaderType() const
@@ -68,6 +77,9 @@ protected:
 	///        includance depth
 	ANKI_USE_RESULT Error parseFileIncludes(ResourceFilename filename, U32 depth);
 
+	/// Parse some source text.
+	ANKI_USE_RESULT Error parseSource(CString src, U depth);
+
 	void printSourceLines() const; ///< For debugging
 };
 

+ 957 - 0
src/anki/resource/ShaderProgramResource.cpp

@@ -0,0 +1,957 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/resource/ShaderProgramResource.h>
+#include <anki/misc/Xml.h>
+#include <anki/resource/ShaderLoader.h>
+#include <anki/resource/RenderingKey.h>
+
+namespace anki
+{
+
+static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, ShaderVariableDataType& out)
+{
+	Error err = ErrorCode::NONE;
+
+	if(str == "float")
+	{
+		out = ShaderVariableDataType::FLOAT;
+	}
+	else if(str == "vec2")
+	{
+		out = ShaderVariableDataType::VEC2;
+	}
+	else if(str == "vec3")
+	{
+		out = ShaderVariableDataType::VEC3;
+	}
+	else if(str == "vec4")
+	{
+		out = ShaderVariableDataType::VEC4;
+	}
+	else if(str == "mat3")
+	{
+		out = ShaderVariableDataType::MAT3;
+	}
+	else if(str == "mat4")
+	{
+		out = ShaderVariableDataType::MAT4;
+	}
+	else if(str == "sampler2D")
+	{
+		out = ShaderVariableDataType::SAMPLER_2D;
+	}
+	else if(str == "sampler2DArray")
+	{
+		out = ShaderVariableDataType::SAMPLER_2D_ARRAY;
+	}
+	else if(str == "samplerCube")
+	{
+		out = ShaderVariableDataType::SAMPLER_CUBE;
+	}
+	else
+	{
+		ANKI_RESOURCE_LOGE("Incorrect variable type %s", &str[0]);
+		err = ErrorCode::USER_DATA;
+	}
+
+	return err;
+}
+
+/// Given a string return info about the shader.
+static ANKI_USE_RESULT Error getShaderInfo(const CString& str, ShaderType& type, ShaderTypeBit& bit, U& idx)
+{
+	Error err = ErrorCode::NONE;
+
+	if(str == "vert")
+	{
+		type = ShaderType::VERTEX;
+		bit = ShaderTypeBit::VERTEX;
+		idx = 0;
+	}
+	else if(str == "tesc")
+	{
+		type = ShaderType::TESSELLATION_CONTROL;
+		bit = ShaderTypeBit::TESSELLATION_CONTROL;
+		idx = 1;
+	}
+	else if(str == "tese")
+	{
+		type = ShaderType::TESSELLATION_EVALUATION;
+		bit = ShaderTypeBit::TESSELLATION_EVALUATION;
+		idx = 2;
+	}
+	else if(str == "geom")
+	{
+		type = ShaderType::GEOMETRY;
+		bit = ShaderTypeBit::GEOMETRY;
+		idx = 3;
+	}
+	else if(str == "frag")
+	{
+		type = ShaderType::FRAGMENT;
+		bit = ShaderTypeBit::FRAGMENT;
+		idx = 4;
+	}
+	else
+	{
+		ANKI_RESOURCE_LOGE("Incorrect type %s", (str) ? &str[0] : "<empty string>");
+		err = ErrorCode::USER_DATA;
+	}
+
+	return err;
+}
+
+ShaderProgramResource::ShaderProgramResource(ResourceManager* manager)
+	: ResourceObject(manager)
+{
+}
+
+ShaderProgramResource::~ShaderProgramResource()
+{
+	auto alloc = getAllocator();
+
+	while(!m_variants.isEmpty())
+	{
+		ShaderProgramResourceVariant* variant = &(*m_variants.getBegin());
+		m_variants.erase(variant);
+
+		variant->m_blockInfos.destroy(alloc);
+		variant->m_texUnits.destroy(alloc);
+		alloc.deleteInstance(variant);
+	}
+
+	for(ShaderProgramResourceInputVariable& var : m_inputVars)
+	{
+		for(auto& m : var.m_mutators)
+		{
+			m.m_values.destroy(alloc);
+		}
+
+		var.m_mutators.destroy(alloc);
+		var.m_name.destroy(alloc);
+	}
+	m_inputVars.destroy(alloc);
+
+	for(ShaderProgramResourceMutator& m : m_mutators)
+	{
+		m.m_name.destroy(alloc);
+		m.m_values.destroy(alloc);
+	}
+	m_mutators.destroy(alloc);
+
+	for(String& s : m_sources)
+	{
+		s.destroy(alloc);
+	}
+}
+
+Error ShaderProgramResource::load(const ResourceFilename& filename)
+{
+	XmlElement el;
+	XmlDocument doc;
+	ANKI_CHECK(openFileParseXml(filename, doc));
+
+	// <shaderProgram>
+	XmlElement rootEl;
+	ANKI_CHECK(doc.getChildElement("shaderProgram", rootEl));
+
+	// <mutators>
+	XmlElement mutatorsEl;
+	ANKI_CHECK(rootEl.getChildElementOptional("mutators", mutatorsEl));
+	if(mutatorsEl)
+	{
+		XmlElement mutatorEl;
+		ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
+		U32 count = 0;
+		ANKI_CHECK(mutatorEl.getSiblingElementsCount(count));
+		++count;
+
+		m_mutators.create(getAllocator(), count);
+		count = 0;
+
+		do
+		{
+			// Get name
+			CString name;
+			ANKI_CHECK(mutatorEl.getAttributeText("name", name));
+			if(name.isEmpty())
+			{
+				ANKI_RESOURCE_LOGE("Mutator name is empty");
+				return ErrorCode::USER_DATA;
+			}
+
+			// Check if already there
+			for(U i = 0; i < count; ++i)
+			{
+				if(m_mutators[i].m_name == name)
+				{
+					ANKI_RESOURCE_LOGE("Mutator aleady present %s", &name[0]);
+					return ErrorCode::USER_DATA;
+				}
+			}
+
+			// Get values
+			DynamicArrayAuto<ShaderProgramResourceMutatorValue> values(getTempAllocator());
+			ANKI_CHECK(mutatorEl.getAttributeNumbers("values", values));
+			if(values.getSize() < 1)
+			{
+				ANKI_RESOURCE_LOGE("Mutator doesn't have any values %s", &name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			std::sort(values.getBegin(), values.getEnd());
+			for(U i = 1; i < values.getSize(); ++i)
+			{
+				if(values[i - 1] == values[i])
+				{
+					ANKI_RESOURCE_LOGE("Mutator %s has duplicate values %d", &name[0], values[i]);
+					return ErrorCode::USER_DATA;
+				}
+			}
+
+			// Init mutator
+			m_mutators[count].m_name.create(getAllocator(), name);
+			m_mutators[count].m_values.create(getAllocator(), values.getSize());
+			for(U i = 0; i < values.getSize(); ++i)
+			{
+				m_mutators[count].m_values[i] = values[i];
+			}
+
+			++count;
+			ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
+		} while(mutatorEl);
+	}
+
+	// <shaders>
+	XmlElement shadersEl;
+	ANKI_CHECK(rootEl.getChildElement("shaders", shadersEl));
+
+	// <shader>
+	// Count the input variables
+	U inputVarCount = 0;
+	XmlElement shaderEl;
+	ANKI_CHECK(shadersEl.getChildElement("shader", shaderEl));
+	do
+	{
+		XmlElement inputsEl;
+		ANKI_CHECK(shaderEl.getChildElementOptional("inputs", inputsEl));
+
+		if(inputsEl)
+		{
+			XmlElement inputEl;
+			ANKI_CHECK(inputsEl.getChildElement("input", inputEl));
+
+			U32 count;
+			ANKI_CHECK(inputEl.getSiblingElementsCount(count));
+			++count;
+
+			inputVarCount += count;
+		}
+
+		ANKI_CHECK(shaderEl.getNextSiblingElement("shader", shaderEl));
+	} while(shaderEl);
+
+	if(inputVarCount)
+	{
+		m_inputVars.create(getAllocator(), inputVarCount);
+	}
+
+	// <shader> again
+	inputVarCount = 0;
+	StringListAuto constSrcList(getTempAllocator());
+	StringListAuto blockSrcList(getTempAllocator());
+	StringListAuto globalsSrcList(getTempAllocator());
+	StringListAuto definesSrcList(getTempAllocator());
+	ShaderTypeBit presentShaders = ShaderTypeBit::NONE;
+	Array<CString, 5> shaderSources;
+	ANKI_CHECK(shadersEl.getChildElement("shader", shaderEl));
+	do
+	{
+		// type
+		CString shaderTypeTxt;
+		ANKI_CHECK(shaderEl.getAttributeText("type", shaderTypeTxt));
+		ShaderType shaderType;
+		ShaderTypeBit shaderTypeBit;
+		U shaderIdx;
+		ANKI_CHECK(getShaderInfo(shaderTypeTxt, shaderType, shaderTypeBit, shaderIdx));
+
+		if(!!(presentShaders & shaderTypeBit))
+		{
+			ANKI_RESOURCE_LOGE("Shader is present more than once: %s", &shaderTypeTxt[0]);
+			return ErrorCode::USER_DATA;
+		}
+		presentShaders |= shaderTypeBit;
+
+		if(shaderType == ShaderType::TESSELLATION_CONTROL || shaderType == ShaderType::TESSELLATION_EVALUATION)
+		{
+			m_tessellation = true;
+		}
+
+		// <inputs>
+		XmlElement inputsEl;
+		ANKI_CHECK(shaderEl.getChildElementOptional("inputs", inputsEl));
+
+		if(inputsEl)
+		{
+			ANKI_CHECK(
+				parseInputs(inputsEl, inputVarCount, constSrcList, blockSrcList, globalsSrcList, definesSrcList));
+		}
+
+		// <source>
+		ANKI_CHECK(shaderEl.getChildElement("source", el));
+		ANKI_CHECK(el.getText(shaderSources[shaderIdx]));
+
+		// Advance
+		ANKI_CHECK(shaderEl.getNextSiblingElement("shader", shaderEl));
+	} while(shaderEl);
+
+	ANKI_ASSERT(inputVarCount == m_inputVars.getSize());
+
+	// Sanity checks
+	if(!(presentShaders & ShaderTypeBit::VERTEX))
+	{
+		ANKI_RESOURCE_LOGE("Missing vertex shader");
+		return ErrorCode::USER_DATA;
+	}
+
+	if(!(presentShaders & ShaderTypeBit::FRAGMENT))
+	{
+		ANKI_RESOURCE_LOGE("Missing fragment shader");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Create sources
+	StringAuto backedConstSrc(getTempAllocator());
+	if(!constSrcList.isEmpty())
+	{
+		constSrcList.join("", backedConstSrc);
+	}
+
+	StringAuto backedUboSrc(getTempAllocator());
+	if(!blockSrcList.isEmpty())
+	{
+		blockSrcList.pushBack("};\n");
+		blockSrcList.join("", backedUboSrc);
+	}
+
+	StringAuto backedGlobalsSrc(getTempAllocator());
+	if(!globalsSrcList.isEmpty())
+	{
+		globalsSrcList.join("", backedGlobalsSrc);
+	}
+
+	StringAuto backedDefinesSrc(getTempAllocator());
+	if(!definesSrcList.isEmpty())
+	{
+		definesSrcList.join("", backedDefinesSrc);
+	}
+
+	for(U s = 0; s < 5; ++s)
+	{
+		if(shaderSources[s])
+		{
+			ShaderLoader loader(&getManager());
+			ANKI_CHECK(loader.parseSourceString(shaderSources[s]));
+
+			if(!backedDefinesSrc.isEmpty())
+			{
+				m_sources[s].append(getAllocator(), backedDefinesSrc);
+			}
+
+			if(!backedConstSrc.isEmpty())
+			{
+				m_sources[s].append(getAllocator(), backedConstSrc);
+			}
+
+			if(!backedUboSrc.isEmpty())
+			{
+				m_sources[s].append(getAllocator(), backedUboSrc);
+			}
+
+			if(!backedGlobalsSrc.isEmpty())
+			{
+				m_sources[s].append(getAllocator(), backedGlobalsSrc);
+			}
+
+			m_sources[s].append(getAllocator(), loader.getShaderSource());
+		}
+	}
+
+	return ErrorCode::NONE;
+}
+
+Error ShaderProgramResource::parseInputs(XmlElement& inputsEl,
+	U& inputVarCount,
+	StringListAuto& constsSrc,
+	StringListAuto& blockSrc,
+	StringListAuto& globalsSrc,
+	StringListAuto& definesSrc)
+{
+	XmlElement inputEl;
+	ANKI_CHECK(inputsEl.getChildElement("input", inputEl));
+	do
+	{
+		ShaderProgramResourceInputVariable& var = m_inputVars[inputVarCount];
+		var.m_idx = inputVarCount;
+
+		// name
+		CString name;
+		ANKI_CHECK(inputEl.getAttributeText("name", name));
+		if(name.isEmpty())
+		{
+			ANKI_RESOURCE_LOGE("Input variable name is empty");
+			return ErrorCode::USER_DATA;
+		}
+		var.m_name.create(getAllocator(), name);
+
+		// type
+		CString typeTxt;
+		ANKI_CHECK(inputEl.getAttributeText("type", typeTxt));
+		ANKI_CHECK(computeShaderVariableDataType(typeTxt, var.m_dataType));
+
+		// const
+		Bool present;
+		U32 constant = 0;
+		ANKI_CHECK(inputEl.getAttributeNumberOptional("const", constant, present));
+		var.m_const = constant != 0;
+
+		// <mutators>
+		XmlElement mutatorsEl;
+		ANKI_CHECK(inputEl.getChildElementOptional("mutators", mutatorsEl));
+		if(mutatorsEl)
+		{
+			XmlElement mutatorEl;
+			ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
+
+			U32 mutatorCount;
+			ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
+			++mutatorCount;
+			ANKI_ASSERT(mutatorCount > 0);
+			var.m_mutators.create(getAllocator(), mutatorCount);
+			mutatorCount = 0;
+
+			do
+			{
+				// name
+				CString mutatorName;
+				ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
+				if(mutatorName.isEmpty())
+				{
+					ANKI_RESOURCE_LOGE("Empty mutator name in input variable %s", &name[0]);
+					return ErrorCode::USER_DATA;
+				}
+
+				// Find the mutator
+				const ShaderProgramResourceMutator* foundMutator = tryFindMutator(mutatorName);
+				if(!foundMutator)
+				{
+					ANKI_RESOURCE_LOGE("Input variable %s can't link to unknown mutator %s", &name[0], &mutatorName[0]);
+					return ErrorCode::USER_DATA;
+				}
+
+				// Get values
+				DynamicArrayAuto<ShaderProgramResourceMutatorValue> vals(getTempAllocator());
+				ANKI_CHECK(mutatorEl.getAttributeNumbers("values", vals));
+				if(vals.getSize() < 1)
+				{
+					ANKI_RESOURCE_LOGE(
+						"Mutator %s doesn't have any values for input variable %s", &mutatorName[0], &name[0]);
+					return ErrorCode::USER_DATA;
+				}
+
+				// Sanity check values
+				std::sort(vals.getBegin(), vals.getEnd());
+
+				for(U i = 0; i < vals.getSize(); ++i)
+				{
+					auto val = vals[i];
+					if(i > 0 && vals[i - 1] == val)
+					{
+						ANKI_RESOURCE_LOGE(
+							"Mutator %s for input var %s has duplicate values", &mutatorName[0], &name[0]);
+						return ErrorCode::USER_DATA;
+					}
+
+					if(!foundMutator->valueExists(val))
+					{
+						ANKI_RESOURCE_LOGE(
+							"Mutator %s for input variable %s has a value that is not part of the mutator",
+							&mutatorName[0],
+							&name[0]);
+						return ErrorCode::USER_DATA;
+					}
+				}
+
+				// Set var
+				var.m_mutators[mutatorCount].m_mutator = foundMutator;
+				var.m_mutators[mutatorCount].m_values.create(getAllocator(), vals.getSize());
+				for(U i = 0; i < vals.getSize(); ++i)
+				{
+					var.m_mutators[mutatorCount].m_values[i] = vals[i];
+				}
+				++mutatorCount;
+
+				// Advance
+				ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
+			} while(mutatorEl);
+
+			ANKI_ASSERT(mutatorCount == var.m_mutators.getSize());
+		} // if(mutatorsEl)
+
+		// instanceCount
+		CString instanceCountTxt;
+		Bool found;
+		ANKI_CHECK(inputEl.getAttributeTextOptional("instanceCount", instanceCountTxt, found));
+		if(found)
+		{
+			if(instanceCountTxt.isEmpty())
+			{
+				ANKI_RESOURCE_LOGE("instanceCount tag is empty for input variable %s", &name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			var.m_instanced = true;
+			const ShaderProgramResourceMutator* instancingMutator = tryFindMutator(instanceCountTxt);
+			if(!instancingMutator)
+			{
+				ANKI_RESOURCE_LOGE("Failed to find mutator %s, used as instanceCount attribute for input variable %s",
+					&instanceCountTxt[0],
+					&name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			if(!m_instancingMutator)
+			{
+				m_instancingMutator = instancingMutator;
+			}
+			else if(instancingMutator != m_instancingMutator)
+			{
+				ANKI_RESOURCE_LOGE("All input variables should have the same instancing mutator");
+				return ErrorCode::USER_DATA;
+			}
+		}
+
+		// Sanity checks
+		if(var.isTexture() && var.m_const)
+		{
+			ANKI_RESOURCE_LOGE("Can't have a texture to be const: %s", &var.m_name[0]);
+			return ErrorCode::USER_DATA;
+		}
+
+		if(var.m_const && var.isInstanced())
+		{
+			ANKI_RESOURCE_LOGE("Can't have input variables that are instanced and const: %s", &var.m_name[0]);
+			return ErrorCode::USER_DATA;
+		}
+
+		if(var.isTexture() && var.isInstanced())
+		{
+			ANKI_RESOURCE_LOGE("Can't have texture that is instanced: %s", &var.m_name[0]);
+			return ErrorCode::USER_DATA;
+		}
+
+		for(U i = 0; i < inputVarCount; ++i)
+		{
+			const ShaderProgramResourceInputVariable& b = m_inputVars[i];
+			if(b.m_name == var.m_name)
+			{
+				ANKI_RESOURCE_LOGE("Duplicate input variable found: %s", &var.m_name[0]);
+				return ErrorCode::USER_DATA;
+			}
+		}
+
+		if(var.m_dataType >= ShaderVariableDataType::MATRIX_FIRST
+			&& var.m_dataType <= ShaderVariableDataType::MATRIX_LAST
+			&& var.m_const)
+		{
+			ANKI_RESOURCE_LOGE("Matrix input variable %s cannot be constant", &var.m_name[0]);
+			return ErrorCode::USER_DATA;
+		}
+
+		// Write the _DEFINED
+		if(var.m_mutators.getSize())
+		{
+			definesSrc.pushBackSprintf("#define %s_DEFINED (", &name[0]);
+			compInputVarDefineString(var, definesSrc);
+			definesSrc.pushBack(")\n");
+		}
+		else
+		{
+			definesSrc.pushBackSprintf("#define %s_DEFINED 1\n", &name[0]);
+		}
+
+		// Append to consts source
+		if(var.m_const)
+		{
+			constsSrc.pushBackSprintf("#if %s_DEFINED == 1\n", &name[0]);
+			constsSrc.pushBackSprintf("const %s %s = %s(%s_CONSTVAL);\n", &typeTxt[0], &name[0], &typeTxt[0], &name[0]);
+			constsSrc.pushBack("#endif\n");
+		}
+
+		// Append to ubo source
+		if(var.inBlock())
+		{
+			if(blockSrc.isEmpty())
+			{
+				blockSrc.pushBack("layout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform sprubo00_ {\n");
+			}
+
+			blockSrc.pushBackSprintf("#if %s_DEFINED == 1\n", &name[0]);
+
+			if(var.m_instanced)
+			{
+				blockSrc.pushBackSprintf("#if %s > 1\n", &m_instancingMutator->getName()[0]);
+				blockSrc.pushBackSprintf(
+					"%s %s_INSTARR[%s];\n", &typeTxt[0], &name[0], &m_instancingMutator->getName()[0]);
+				blockSrc.pushBack("#else\n");
+				blockSrc.pushBackSprintf("%s %s;\n", &typeTxt[0], &name[0]);
+				blockSrc.pushBack("#endif\n");
+
+				globalsSrc.pushBackSprintf("#if defined(ANKI_VERTEX_SHADER) && %s_DEFINED == 1 && %s > 1\n",
+					&name[0],
+					&m_instancingMutator->getName()[0]);
+				globalsSrc.pushBackSprintf("%s %s = %s_INSTARR[gl_InstanceID];\n", &typeTxt[0], &name[0], &name[0]);
+				globalsSrc.pushBack("#else\n// TODO\n#endif\n");
+			}
+			else
+			{
+				blockSrc.pushBackSprintf("%s %s;\n", &typeTxt[0], &name[0]);
+			}
+
+			blockSrc.pushBack("#endif\n");
+		}
+
+		// Append the textures to global area
+		if(var.isTexture())
+		{
+			globalsSrc.pushBackSprintf("#if %s_DEFINED == 1\n", &name[0]);
+			globalsSrc.pushBackSprintf(
+				"layout(ANKI_TEX_BINDING(0, %s_TEXUNIT)) uniform %s %s;\n", &name[0], &typeTxt[0], &name[0]);
+			globalsSrc.pushBack("#endif\n");
+		}
+
+		// Advance
+		++inputVarCount;
+		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
+	} while(inputEl);
+
+	return ErrorCode::NONE;
+}
+
+void ShaderProgramResource::compInputVarDefineString(
+	const ShaderProgramResourceInputVariable& var, StringListAuto& list)
+{
+	if(var.m_mutators.getSize() > 0)
+	{
+		for(U mi = 0; mi < var.m_mutators.getSize(); ++mi)
+		{
+			const ShaderProgramResourceInputVariable::Mutator& mutator = var.m_mutators[mi];
+			list.pushBack("(");
+
+			for(U vi = 0; vi < mutator.m_values.getSize(); ++vi)
+			{
+				ShaderProgramResourceMutatorValue val = mutator.m_values[vi];
+
+				if(vi != mutator.m_values.getSize() - 1)
+				{
+					list.pushBackSprintf("%s == %d || ", &mutator.m_mutator->m_name[0], int(val));
+				}
+				else
+				{
+					list.pushBackSprintf("%s == %d)", &mutator.m_mutator->m_name[0], int(val));
+				}
+			}
+
+			if(mi != var.m_mutators.getSize() - 1)
+			{
+				list.pushBack(" && ");
+			}
+		}
+	}
+}
+
+U64 ShaderProgramResource::computeVariantHash(WeakArray<const ShaderProgramResourceMutation> mutations,
+	WeakArray<const ShaderProgramResourceConstantValue> constants) const
+{
+	U hash = 1;
+
+	if(mutations.getSize())
+	{
+		hash = computeHash(&mutations[0], sizeof(mutations[0]) * mutations.getSize());
+	}
+
+	if(constants.getSize())
+	{
+		hash = appendHash(&constants[0], sizeof(constants[0]) * constants.getSize(), hash);
+	}
+
+	return hash;
+}
+
+void ShaderProgramResource::getOrCreateVariant(WeakArray<const ShaderProgramResourceMutation> mutation,
+	WeakArray<const ShaderProgramResourceConstantValue> constants,
+	const ShaderProgramResourceVariant*& variant) const
+{
+	// Sanity checks
+	ANKI_ASSERT(mutation.getSize() == m_mutators.getSize());
+	ANKI_ASSERT(mutation.getSize() <= 128 && "Wrong assumption");
+	BitSet<128> mutatorPresent = {false};
+	for(const ShaderProgramResourceMutation& m : mutation)
+	{
+		(void)m;
+		ANKI_ASSERT(m.m_mutator);
+		ANKI_ASSERT(m.m_mutator->valueExists(m.m_value));
+
+		ANKI_ASSERT(&m_mutators[0] <= m.m_mutator);
+		const U idx = m.m_mutator - &m_mutators[0];
+		ANKI_ASSERT(idx < m_mutators.getSize());
+		ANKI_ASSERT(!mutatorPresent.get(idx) && "Appeared 2 times in 'mutation'");
+		mutatorPresent.set(idx);
+
+#if ANKI_EXTRA_CHECKS
+		if(m_instancingMutator == m.m_mutator)
+		{
+			ANKI_ASSERT(m.m_value > 0 && "Instancing value can't be negative");
+		}
+#endif
+	}
+
+	// Compute hash
+	U64 hash = computeVariantHash(mutation, constants);
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	auto it = m_variants.find(hash);
+	if(it != m_variants.getEnd())
+	{
+		variant = &(*it);
+	}
+	else
+	{
+		// Create one
+		ShaderProgramResourceVariant* v = getAllocator().newInstance<ShaderProgramResourceVariant>();
+		initVariant(mutation, constants, *v);
+
+		m_variants.pushBack(hash, v);
+		variant = v;
+	}
+}
+
+void ShaderProgramResource::initVariant(WeakArray<const ShaderProgramResourceMutation> mutations,
+	WeakArray<const ShaderProgramResourceConstantValue> constants,
+	ShaderProgramResourceVariant& variant) const
+{
+	variant.m_activeInputVars.unsetAll();
+	variant.m_blockInfos.create(getAllocator(), m_inputVars.getSize());
+	variant.m_texUnits.create(getAllocator(), m_inputVars.getSize());
+	if(m_inputVars.getSize() > 0)
+	{
+		memorySet<I16>(&variant.m_texUnits[0], -1, variant.m_texUnits.getSize());
+	}
+
+	// Get instance count, one mutation has it
+	U instanceCount = 1;
+	if(m_instancingMutator)
+	{
+		for(const ShaderProgramResourceMutation& m : mutations)
+		{
+			if(m.m_mutator == m_instancingMutator)
+			{
+				ANKI_ASSERT(m.m_value > 0);
+				instanceCount = m.m_value;
+				break;
+			}
+		}
+	}
+
+	// - Compute the block info for each var
+	// - Activate vars
+	// - Compute varius strings
+	StringListAuto headerSrc(getTempAllocator());
+	U texUnit = 0;
+
+	for(const ShaderProgramResourceInputVariable& in : m_inputVars)
+	{
+		if(!in.acceptAllMutations(mutations))
+		{
+			continue;
+		}
+
+		variant.m_activeInputVars.set(in.m_idx);
+
+		// Init block info
+		if(in.inBlock())
+		{
+			ShaderVariableBlockInfo& blockInfo = variant.m_blockInfos[in.m_idx];
+
+			// std140 rules
+			blockInfo.m_offset = variant.m_uniBlockSize;
+			blockInfo.m_arraySize = (in.m_instanced) ? instanceCount : 1;
+
+			if(in.m_dataType == ShaderVariableDataType::FLOAT)
+			{
+				blockInfo.m_arrayStride = sizeof(Vec4);
+
+				if(blockInfo.m_arraySize == 1)
+				{
+					// No need to align the in.m_offset
+					variant.m_uniBlockSize += sizeof(F32);
+				}
+				else
+				{
+					alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+					variant.m_uniBlockSize += sizeof(Vec4) * blockInfo.m_arraySize;
+				}
+			}
+			else if(in.m_dataType == ShaderVariableDataType::VEC2)
+			{
+				blockInfo.m_arrayStride = sizeof(Vec4);
+
+				if(blockInfo.m_arraySize == 1)
+				{
+					alignRoundUp(sizeof(Vec2), blockInfo.m_offset);
+					variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec2);
+				}
+				else
+				{
+					alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+					variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+				}
+			}
+			else if(in.m_dataType == ShaderVariableDataType::VEC3)
+			{
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				blockInfo.m_arrayStride = sizeof(Vec4);
+
+				if(blockInfo.m_arraySize == 1)
+				{
+					variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec3);
+				}
+				else
+				{
+					variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+				}
+			}
+			else if(in.m_dataType == ShaderVariableDataType::VEC4)
+			{
+				blockInfo.m_arrayStride = sizeof(Vec4);
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+			}
+			else if(in.m_dataType == ShaderVariableDataType::MAT3)
+			{
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				blockInfo.m_arrayStride = sizeof(Vec4) * 3;
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * 3 * blockInfo.m_arraySize;
+				blockInfo.m_matrixStride = sizeof(Vec4);
+			}
+			else if(in.m_dataType == ShaderVariableDataType::MAT4)
+			{
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				blockInfo.m_arrayStride = sizeof(Mat4);
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Mat4) * blockInfo.m_arraySize;
+				blockInfo.m_matrixStride = sizeof(Vec4);
+			}
+			else
+			{
+				ANKI_ASSERT(0);
+			}
+		} // if(in.inBlock())
+
+		if(in.m_const)
+		{
+			// Find the const value
+			const ShaderProgramResourceConstantValue* constVal = nullptr;
+			for(const ShaderProgramResourceConstantValue& c : constants)
+			{
+				if(c.m_variable == &in)
+				{
+					constVal = &c;
+					break;
+				}
+			}
+
+			ANKI_ASSERT(constVal);
+
+			switch(in.m_dataType)
+			{
+			case ShaderVariableDataType::FLOAT:
+				headerSrc.pushBackSprintf("#define %s_CONSTVAL %f\n", &in.m_name[0], constVal->m_float);
+				break;
+			case ShaderVariableDataType::VEC2:
+				headerSrc.pushBackSprintf(
+					"#define %s_CONSTVAL %f, %f\n", &in.m_name[0], constVal->m_vec2.x(), constVal->m_vec2.y());
+				break;
+			case ShaderVariableDataType::VEC3:
+				headerSrc.pushBackSprintf("#define %s_CONSTVAL %f, %f, %f\n",
+					&in.m_name[0],
+					constVal->m_vec3.x(),
+					constVal->m_vec3.y(),
+					constVal->m_vec3.z());
+				break;
+			case ShaderVariableDataType::VEC4:
+				headerSrc.pushBackSprintf("#define %s_CONSTVAL %f, %f, %f, %f\n",
+					&in.m_name[0],
+					constVal->m_vec4.x(),
+					constVal->m_vec4.y(),
+					constVal->m_vec4.z(),
+					constVal->m_vec4.w());
+				break;
+			default:
+				ANKI_ASSERT(0);
+			}
+		}
+
+		if(in.isTexture())
+		{
+			headerSrc.pushBackSprintf("#define %s_TEXUNIT %u\n", &in.m_name[0], texUnit);
+			variant.m_texUnits[in.m_idx] = texUnit;
+			++texUnit;
+		}
+	}
+
+	// Write the source header
+	StringListAuto shaderHeaderSrc(getTempAllocator());
+
+	for(const ShaderProgramResourceMutation& m : mutations)
+	{
+		shaderHeaderSrc.pushBackSprintf("#define %s %d\n", &m.m_mutator->getName()[0], m.m_value);
+	}
+
+	if(!headerSrc.isEmpty())
+	{
+		StringAuto str(getTempAllocator());
+		headerSrc.join("", str);
+		shaderHeaderSrc.pushBack(str.toCString());
+	}
+
+	StringAuto shaderHeader(getTempAllocator());
+	shaderHeaderSrc.join("", shaderHeader);
+
+	// Create the shaders and the program
+	Array<ShaderPtr, 5> shaders;
+	for(ShaderType i = ShaderType::FIRST_GRAPHICS; i <= ShaderType::LAST_GRAPHICS; ++i)
+	{
+		if(!m_sources[i])
+		{
+			continue;
+		}
+
+		StringAuto src(getTempAllocator());
+		src.append(shaderHeader);
+		src.append(m_sources[i]);
+
+		shaders[i] = getManager().getGrManager().newInstance<Shader>(i, src.toCString());
+	}
+
+	variant.m_prog = getManager().getGrManager().newInstance<ShaderProgram>(shaders[ShaderType::VERTEX],
+		shaders[ShaderType::TESSELLATION_CONTROL],
+		shaders[ShaderType::TESSELLATION_EVALUATION],
+		shaders[ShaderType::GEOMETRY],
+		shaders[ShaderType::FRAGMENT]);
+}
+
+} // end namespace anki

+ 384 - 0
src/anki/resource/ShaderProgramResource.h

@@ -0,0 +1,384 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/resource/ResourceObject.h>
+#include <anki/Gr.h>
+#include <anki/util/BitSet.h>
+
+namespace anki
+{
+
+// Forward
+class RenderingKey;
+class XmlElement;
+
+/// @addtogroup resource
+/// @{
+
+using ShaderProgramResourceMutatorValue = I32;
+
+/// The means to mutate a shader program.
+class ShaderProgramResourceMutator : public NonCopyable
+{
+	friend class ShaderProgramResource;
+
+public:
+	CString getName() const
+	{
+		ANKI_ASSERT(m_name);
+		return m_name.toCString();
+	}
+
+	const DynamicArray<ShaderProgramResourceMutatorValue>& getValues() const
+	{
+		ANKI_ASSERT(m_values.getSize() > 0);
+		return m_values;
+	}
+
+	Bool valueExists(ShaderProgramResourceMutatorValue v) const
+	{
+		for(ShaderProgramResourceMutatorValue val : m_values)
+		{
+			if(v == val)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+
+private:
+	String m_name;
+	DynamicArray<ShaderProgramResourceMutatorValue> m_values;
+};
+
+class ShaderProgramResourceMutation
+{
+public:
+	const ShaderProgramResourceMutator* m_mutator;
+	ShaderProgramResourceMutatorValue m_value;
+	U8 _padding[sizeof(void*) - sizeof(m_value)];
+
+	ShaderProgramResourceMutation()
+	{
+		memset(this, 0, sizeof(*this));
+	}
+};
+
+static_assert(sizeof(ShaderProgramResourceMutation) == sizeof(void*) * 2, "Need it to be packed");
+
+/// A wrapper over the uniforms of a shader or constants.
+class ShaderProgramResourceInputVariable : public NonCopyable
+{
+	friend class ShaderProgramResourceVariant;
+	friend class ShaderProgramResource;
+
+public:
+	CString getName() const
+	{
+		return m_name.toCString();
+	}
+
+	ShaderVariableDataType getShaderVariableDataType() const
+	{
+		ANKI_ASSERT(m_dataType);
+		return m_dataType;
+	}
+
+	Bool isInstanced() const
+	{
+		return m_instanced;
+	}
+
+	Bool isConstant() const
+	{
+		return m_const;
+	}
+
+	Bool acceptAllMutations(const WeakArray<const ShaderProgramResourceMutation>& mutations) const
+	{
+		for(const ShaderProgramResourceMutation& m : mutations)
+		{
+			if(!acceptMutation(m))
+			{
+				return false;
+			}
+		}
+		return true;
+	}
+
+private:
+	/// Information on how this variable will be used by this mutator.
+	class Mutator
+	{
+	public:
+		const ShaderProgramResourceMutator* m_mutator = nullptr;
+		DynamicArray<ShaderProgramResourceMutatorValue> m_values;
+	};
+
+	String m_name;
+	DynamicArray<Mutator> m_mutators;
+	U32 m_idx;
+	ShaderVariableDataType m_dataType = ShaderVariableDataType::NONE;
+	Bool8 m_const = false;
+	Bool8 m_instanced = false;
+
+	Bool isTexture() const
+	{
+		return m_dataType >= ShaderVariableDataType::SAMPLERS_FIRST
+			&& m_dataType <= ShaderVariableDataType::SAMPLERS_LAST;
+	}
+
+	Bool inBlock() const
+	{
+		return !m_const && !isTexture();
+	}
+
+	const Mutator* tryFindMutator(const ShaderProgramResourceMutator& mutator) const
+	{
+		for(const Mutator& m : m_mutators)
+		{
+			if(m.m_mutator == &mutator)
+			{
+				return &m;
+			}
+		}
+
+		return nullptr;
+	}
+
+	Bool acceptMutation(const ShaderProgramResourceMutation& mutation) const
+	{
+		ANKI_ASSERT(mutation.m_mutator->valueExists(mutation.m_value));
+		const Mutator* m = tryFindMutator(*mutation.m_mutator);
+		if(m)
+		{
+			for(ShaderProgramResourceMutatorValue v : m->m_values)
+			{
+				if(mutation.m_value == v)
+				{
+					return true;
+				}
+			}
+			return false;
+		}
+		else
+		{
+			return true;
+		}
+	}
+};
+
+/// Shader program resource variant.
+class ShaderProgramResourceVariant : public IntrusiveHashMapEnabled<ShaderProgramResourceVariant>
+{
+	friend class ShaderProgramResource;
+
+public:
+	ShaderProgramResourceVariant()
+	{
+	}
+
+	~ShaderProgramResourceVariant()
+	{
+	}
+
+	/// Return true of the the variable is active.
+	Bool variableActive(const ShaderProgramResourceInputVariable& var) const
+	{
+		return m_activeInputVars.get(var.m_idx);
+	}
+
+	U getUniformBlockSize() const
+	{
+		return m_uniBlockSize;
+	}
+
+	const ShaderVariableBlockInfo& getVariableBlockInfo(const ShaderProgramResourceInputVariable& var) const
+	{
+		ANKI_ASSERT(!var.isTexture() && variableActive(var));
+		return m_blockInfos[var.m_idx];
+	}
+
+	template<typename T>
+	void writeShaderBlockMemory(const ShaderProgramResourceInputVariable& var,
+		const T* elements,
+		U32 elementsCount,
+		void* buffBegin,
+		const void* buffEnd) const
+	{
+		ANKI_ASSERT(getShaderVariableTypeFromTypename<T>() == var.m_dataType);
+		const ShaderVariableBlockInfo& blockInfo = m_blockInfos[var.m_idx];
+		anki::writeShaderBlockMemory(var.m_dataType, blockInfo, elements, elementsCount, buffBegin, buffEnd);
+	}
+
+	const ShaderProgramPtr& getProgram() const
+	{
+		return m_prog;
+	}
+
+	U getTextureUnit(const ShaderProgramResourceInputVariable& var) const
+	{
+		ANKI_ASSERT(m_texUnits[var.m_idx] >= 0);
+		return U(m_texUnits[var.m_idx]);
+	}
+
+private:
+	ShaderProgramPtr m_prog;
+
+	BitSet<128> m_activeInputVars = {false};
+	DynamicArray<ShaderVariableBlockInfo> m_blockInfos;
+	U32 m_uniBlockSize = 0;
+	DynamicArray<I16> m_texUnits;
+};
+
+/// The value of a constant.
+class ShaderProgramResourceConstantValue
+{
+public:
+	union
+	{
+		F32 m_float;
+		Vec2 m_vec2;
+		Vec3 m_vec3;
+		Vec4 m_vec4;
+	};
+
+	const ShaderProgramResourceInputVariable* m_variable;
+	U8 _padding[sizeof(Vec4) - sizeof(void*)];
+
+	ShaderProgramResourceConstantValue()
+	{
+		memset(this, 0, sizeof(*this));
+	}
+};
+
+static_assert(sizeof(ShaderProgramResourceConstantValue) == sizeof(Vec4) * 2, "Need it to be packed");
+
+/// Shader program resource. It defines a custom format for shader programs.
+///
+/// XML file format:
+/// @code
+/// <shaderProgram>
+/// 	[<mutators> (1)
+/// 		<mutator name="str" values="array of ints"/>
+/// 	</mutators>]
+///
+///		<shaders>
+///			<shader type="vert | frag | tese | tesc"/>
+///
+///				[<inputs>
+///					<input name="str" type="float | vec2 | vec3 | vec4 | mat3 | mat4 | samplerXXX"
+/// 					[instanceCount="mutator name"] [const="0 | 1"]>
+/// 					[<mutators> (2)
+/// 						<mutator name="variant_name" values="array of ints"/>
+/// 					</mutators>]
+///					</input>
+///				</inputs>]
+///
+///				<source>
+///					src
+///				</source>
+///			</shader>
+///		</shaders>
+/// </shaderProgram>
+/// @endcode
+/// (1): This is a variant list. It contains the means to permutate the program.
+/// (2): This lists a subset of mutators and out of these variants a subset of their values. The input variable will
+///      become active only on those mutators. Mutators not listed are implicitly added with all their values.
+class ShaderProgramResource : public ResourceObject
+{
+public:
+	ShaderProgramResource(ResourceManager* manager);
+
+	~ShaderProgramResource();
+
+	/// Load the resource.
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+
+	const DynamicArray<ShaderProgramResourceInputVariable>& getInputVariables() const
+	{
+		return m_inputVars;
+	}
+
+	const ShaderProgramResourceInputVariable* tryFindInputVariable(CString name) const
+	{
+		for(const ShaderProgramResourceInputVariable& m : m_inputVars)
+		{
+			if(m.getName() == name)
+			{
+				return &m;
+			}
+		}
+		return nullptr;
+	}
+
+	const DynamicArray<ShaderProgramResourceMutator>& getMutators() const
+	{
+		return m_mutators;
+	}
+
+	const ShaderProgramResourceMutator* tryFindMutator(CString name) const
+	{
+		for(const ShaderProgramResourceMutator& m : m_mutators)
+		{
+			if(m.getName() == name)
+			{
+				return &m;
+			}
+		}
+		return nullptr;
+	}
+
+	/// Get or create a graphics shader program variant.
+	/// @note It's thread-safe.
+	void getOrCreateVariant(WeakArray<const ShaderProgramResourceMutation> mutation,
+		WeakArray<const ShaderProgramResourceConstantValue> constants,
+		const ShaderProgramResourceVariant*& variant) const;
+
+	Bool hasTessellation() const
+	{
+		return m_tessellation;
+	}
+
+	Bool isInstanced() const
+	{
+		return m_instancingMutator != nullptr;
+	}
+
+private:
+	DynamicArray<ShaderProgramResourceInputVariable> m_inputVars;
+	DynamicArray<ShaderProgramResourceMutator> m_mutators;
+
+	Array<String, 5> m_sources;
+
+	mutable IntrusiveHashMap<U64, ShaderProgramResourceVariant> m_variants;
+	mutable Mutex m_mtx;
+
+	Bool8 m_tessellation = false;
+	const ShaderProgramResourceMutator* m_instancingMutator = nullptr;
+
+	/// Parse whatever is inside <inputs>
+	ANKI_USE_RESULT Error parseInputs(XmlElement& inputsEl,
+		U& inputVarCount,
+		StringListAuto& constsSrc,
+		StringListAuto& blockSrc,
+		StringListAuto& globalsSrc,
+		StringListAuto& definesSrc);
+
+	U64 computeVariantHash(WeakArray<const ShaderProgramResourceMutation> mutations,
+		WeakArray<const ShaderProgramResourceConstantValue> constants) const;
+
+	void initVariant(WeakArray<const ShaderProgramResourceMutation> mutations,
+		WeakArray<const ShaderProgramResourceConstantValue> constants,
+		ShaderProgramResourceVariant& v) const;
+
+	void compInputVarDefineString(const ShaderProgramResourceInputVariable& var, StringListAuto& list);
+};
+/// @}
+
+} // end namespace anki

+ 1 - 1
src/anki/resource/TextureAtlas.cpp

@@ -49,7 +49,7 @@ Error TextureAtlas::load(const ResourceFilename& filename)
 	//
 	ANKI_CHECK(rootel.getChildElement("subTextureMargin", el));
 	I64 margin = 0;
-	ANKI_CHECK(el.getI64(margin));
+	ANKI_CHECK(el.getNumber(margin));
 	if(margin >= I(m_tex->getWidth()) || margin >= I(m_tex->getHeight()) || margin < 0)
 	{
 		ANKI_RESOURCE_LOGE("Too big margin %d", U(margin));

+ 6 - 54
src/anki/scene/RenderComponent.cpp

@@ -12,42 +12,6 @@
 namespace anki
 {
 
-/// Create a new RenderComponentVariable given a MaterialVariable
-struct CreateNewRenderComponentVariableVisitor
-{
-	const MaterialVariable* m_mvar = nullptr;
-	mutable RenderComponent::Variables* m_vars = nullptr;
-	mutable U32 m_count = 0;
-	mutable SceneAllocator<U8> m_alloc;
-
-	template<typename TMaterialVariableTemplate>
-	Error visit(const TMaterialVariableTemplate& mvart) const
-	{
-		using Type = typename TMaterialVariableTemplate::Type;
-
-		RenderComponentVariableTemplate<Type>* rvar =
-			m_alloc.newInstance<RenderComponentVariableTemplate<Type>>(m_mvar);
-
-		if(mvart.getBuiltin() == BuiltinMaterialVariableId::NONE)
-		{
-			rvar->setValue(mvart.getValue());
-		}
-
-		(*m_vars)[m_count++] = rvar;
-		return ErrorCode::NONE;
-	}
-};
-
-RenderComponentVariable::RenderComponentVariable(const MaterialVariable* mvar)
-	: m_mvar(mvar)
-{
-	ANKI_ASSERT(m_mvar);
-}
-
-RenderComponentVariable::~RenderComponentVariable()
-{
-}
-
 RenderComponent::RenderComponent(SceneNode* node, const Material* mtl)
 	: SceneComponent(SceneComponentType::RENDER, node)
 	, m_mtl(mtl)
@@ -56,31 +20,19 @@ RenderComponent::RenderComponent(SceneNode* node, const Material* mtl)
 
 RenderComponent::~RenderComponent()
 {
-	auto alloc = m_node->getSceneAllocator();
-	for(RenderComponentVariable* var : m_vars)
-	{
-		alloc.deleteInstance(var);
-	}
-
-	m_vars.destroy(alloc);
+	m_vars.destroy(getAllocator());
 }
 
 Error RenderComponent::init()
 {
 	const Material& mtl = getMaterial();
-	auto alloc = m_node->getSceneAllocator();
-
-	// Create the material variables using a visitor
-	m_vars.create(alloc, mtl.getVariables().getSize());
-
-	CreateNewRenderComponentVariableVisitor vis;
-	vis.m_vars = &m_vars;
-	vis.m_alloc = alloc;
 
-	for(const MaterialVariable* mv : mtl.getVariables())
+	// Create the material variables
+	m_vars.create(getAllocator(), mtl.getVariables().getSize());
+	U count = 0;
+	for(const MaterialVariable& mv : mtl.getVariables())
 	{
-		vis.m_mvar = mv;
-		ANKI_CHECK(mv->acceptVisitor(vis));
+		m_vars[count++].m_mvar = &mv;
 	}
 
 	return ErrorCode::NONE;

+ 10 - 71
src/anki/scene/RenderComponent.h

@@ -18,46 +18,19 @@ namespace anki
 /// @addtogroup scene
 /// @{
 
-// Forward
-class RenderComponentVariable;
-
-template<typename T>
-class RenderComponentVariableTemplate;
-
-/// RenderComponent variable base. It's a visitable.
-using RenderComponentVariableVisitable = VisitableCommonBase<RenderComponentVariable, // The base
-	RenderComponentVariableTemplate<F32>,
-	RenderComponentVariableTemplate<Vec2>,
-	RenderComponentVariableTemplate<Vec3>,
-	RenderComponentVariableTemplate<Vec4>,
-	RenderComponentVariableTemplate<Mat3>,
-	RenderComponentVariableTemplate<Mat4>,
-	RenderComponentVariableTemplate<TextureResourcePtr>>;
-
 /// A wrapper on top of MaterialVariable
-class RenderComponentVariable : public RenderComponentVariableVisitable
+class RenderComponentVariable
 {
-public:
-	using Base = RenderComponentVariableVisitable;
+	friend class RenderComponent;
 
-	RenderComponentVariable(const MaterialVariable* mvar);
-	virtual ~RenderComponentVariable();
-
-	/// This will trigger copy on write
-	template<typename T>
-	void setValue(const T& value)
-	{
-		ANKI_ASSERT(Base::isTypeOf<RenderComponentVariableTemplate<T>>());
-		auto derived = static_cast<RenderComponentVariableTemplate<T>*>(this);
-		derived->setValue(value);
-	}
+public:
+	RenderComponentVariable() = default;
+	~RenderComponentVariable() = default;
 
 	template<typename T>
 	const T& getValue() const
 	{
-		ANKI_ASSERT(Base::isTypeOf<RenderComponentVariableTemplate<T>>());
-		auto derived = static_cast<const RenderComponentVariableTemplate<T>*>(this);
-		return derived->getValue();
+		return m_mvar->getValue<T>();
 	}
 
 	const MaterialVariable& getMaterialVariable() const
@@ -65,42 +38,8 @@ public:
 		return *m_mvar;
 	}
 
-protected:
-	const MaterialVariable* m_mvar = nullptr;
-};
-
-/// RenderComponent variable. This class should not be visible to other interfaces except render component.
-template<typename T>
-class RenderComponentVariableTemplate : public RenderComponentVariable
-{
-public:
-	using Base = RenderComponentVariable;
-	using Type = T;
-
-	RenderComponentVariableTemplate(const MaterialVariable* mvar)
-		: RenderComponentVariable(mvar)
-	{
-		setupVisitable(this);
-	}
-
-	~RenderComponentVariableTemplate()
-	{
-	}
-
-	void setValue(const T& value)
-	{
-		ANKI_ASSERT(isTypeOf<RenderComponentVariableTemplate<T>>());
-		ANKI_ASSERT(Base::getMaterialVariable().getBuiltin() == BuiltinMaterialVariableId::NONE);
-		m_copy = value;
-	}
-
-	const T& getValue() const
-	{
-		return m_copy;
-	}
-
 private:
-	T m_copy; ///< Copy of the data
+	const MaterialVariable* m_mvar = nullptr;
 };
 
 /// Rendering data input.
@@ -212,7 +151,7 @@ class RenderComponent : public SceneComponent
 public:
 	static const SceneComponentType CLASS_TYPE = SceneComponentType::RENDER;
 
-	using Variables = DynamicArray<RenderComponentVariable*>;
+	using Variables = DynamicArray<RenderComponentVariable>;
 
 	RenderComponent(SceneNode* node, const Material* mtl);
 
@@ -253,7 +192,7 @@ public:
 	Bool getCastsShadow() const
 	{
 		const Material& mtl = getMaterial();
-		return mtl.getShadowEnabled();
+		return mtl.castsShadow();
 	}
 
 	/// Iterate variables using a lambda
@@ -264,7 +203,7 @@ public:
 		Variables::Iterator it = m_vars.getBegin();
 		for(; it != m_vars.getEnd() && !err; it++)
 		{
-			err = func(*(*it));
+			err = func(*it);
 		}
 
 		return err;

+ 2 - 2
src/anki/scene/Visibility.cpp

@@ -369,8 +369,8 @@ void VisibilityTestTask::test(ThreadHive& hive)
 			if(wantsRenderComponents || (wantsShadowCasters && rc->getCastsShadow()))
 			{
 				visible->moveBack(alloc,
-					rc->getMaterial().getForwardShading() ? VisibilityGroupType::RENDERABLES_FS
-														  : VisibilityGroupType::RENDERABLES_MS,
+					rc->getMaterial().isForwardShading() ? VisibilityGroupType::RENDERABLES_FS
+														 : VisibilityGroupType::RENDERABLES_MS,
 					visibleNode);
 
 				if(wantsShadowCasters)

+ 2 - 2
src/anki/scene/VisibilityInternal.h

@@ -51,8 +51,8 @@ public:
 	{
 		ANKI_ASSERT(a.m_node && b.m_node);
 
-		return a.m_node->getComponent<RenderComponent>().getMaterial()
-			< b.m_node->getComponent<RenderComponent>().getMaterial();
+		return a.m_node->getComponent<RenderComponent>().getMaterial().getUuid()
+			< b.m_node->getComponent<RenderComponent>().getMaterial().getUuid();
 	}
 };
 

+ 12 - 0
src/anki/util/DynamicArray.h

@@ -330,6 +330,18 @@ public:
 		}
 	}
 
+	template<typename Y, PtrSize S>
+	WeakArray(const Array<Y, S>& arr)
+		: Base(&arr[0], S)
+	{
+	}
+
+	template<typename Y>
+	WeakArray(const DynamicArrayBase<Y>& arr)
+		: Base((arr.getSize()) ? &arr[0] : nullptr, arr.getSize())
+	{
+	}
+
 	/// Copy.
 	WeakArray(const WeakArray& b)
 		: Base(b.m_data, b.m_size)

+ 8 - 0
src/anki/util/Functions.h

@@ -218,6 +218,14 @@ TForwardIterator binarySearch(TForwardIterator first, TForwardIterator last, con
 	first = std::lower_bound(first, last, value, comp);
 	return (first != last && !comp(value, *first)) ? first : last;
 }
+
+/// Individual classes should specialize that function if they are packed. If a class is packed it can be used as
+/// whole in hashing.
+template<typename T>
+constexpr Bool isPacked()
+{
+	return false;
+}
 /// @}
 
 } // end namespace anki

+ 96 - 15
src/anki/util/String.cpp

@@ -12,46 +12,127 @@
 namespace anki
 {
 
-Error CString::toF64(F64& out) const
+Error CString::toNumber(F64& out) const
 {
 	checkInit();
-	Error err = ErrorCode::NONE;
+	errno = 0;
 	out = std::strtod(m_ptr, nullptr);
 
-	if(out == HUGE_VAL)
+	if(errno)
 	{
+		errno = 0;
 		ANKI_UTIL_LOGE("Conversion failed");
-		err = ErrorCode::USER_DATA;
+		return ErrorCode::USER_DATA;
 	}
 
-	return err;
+	return ErrorCode::NONE;
 }
 
-Error CString::toF32(F32& out) const
+Error CString::toNumber(F32& out) const
 {
 	F64 d;
-	Error err = toF64(d);
-	if(!err)
+	ANKI_CHECK(toNumber(d));
+	out = d;
+	return ErrorCode::NONE;
+}
+
+Error CString::toNumber(I8& out) const
+{
+	I64 i64 = 0;
+	ANKI_CHECK(toNumber(i64));
+
+	if(i64 < MIN_I8 || i64 > MAX_I8)
 	{
-		out = d;
+		ANKI_UTIL_LOGE("Conversion failed. Our of range");
+		return ErrorCode::USER_DATA;
 	}
 
-	return err;
+	out = I8(i64);
+	return ErrorCode::NONE;
 }
 
-Error CString::toI64(I64& out) const
+Error CString::toNumber(I64& out) const
 {
 	checkInit();
-	Error err = ErrorCode::NONE;
+	errno = 0;
+	static_assert(sizeof(long long) == sizeof(I64), "See file");
 	out = std::strtoll(m_ptr, nullptr, 10);
 
-	if(out == LLONG_MAX || out == LLONG_MIN)
+	if(errno)
+	{
+		errno = 0;
+		ANKI_UTIL_LOGE("Conversion failed");
+		return ErrorCode::USER_DATA;
+	}
+
+	return ErrorCode::NONE;
+}
+
+Error CString::toNumber(I32& out) const
+{
+	checkInit();
+	errno = 0;
+	long long i = std::strtoll(m_ptr, nullptr, 10);
+
+	if(errno || i < MIN_I32 || i > MAX_I32)
 	{
+		errno = 0;
 		ANKI_UTIL_LOGE("Conversion failed");
-		err = ErrorCode::USER_DATA;
+		return ErrorCode::USER_DATA;
+	}
+
+	out = I32(i);
+
+	return ErrorCode::NONE;
+}
+
+Error CString::toNumber(U64& out) const
+{
+	checkInit();
+	errno = 0;
+	static_assert(sizeof(unsigned long long) == sizeof(U64), "See file");
+	out = std::strtoull(m_ptr, nullptr, 10);
+
+	if(errno)
+	{
+		errno = 0;
+		ANKI_UTIL_LOGE("Conversion failed");
+		return ErrorCode::USER_DATA;
+	}
+
+	return ErrorCode::NONE;
+}
+
+Error CString::toNumber(U32& out) const
+{
+	checkInit();
+	errno = 0;
+	unsigned long long i = std::strtoull(m_ptr, nullptr, 10);
+
+	if(errno || i > MAX_U32)
+	{
+		errno = 0;
+		ANKI_UTIL_LOGE("Conversion failed");
+		return ErrorCode::USER_DATA;
+	}
+
+	out = U32(i);
+	return ErrorCode::NONE;
+}
+
+Error CString::toNumber(U8& out) const
+{
+	U64 i64 = 0;
+	ANKI_CHECK(toNumber(i64));
+
+	if(i64 > MAX_U8)
+	{
+		ANKI_UTIL_LOGE("Conversion failed. Our of range");
+		return ErrorCode::USER_DATA;
 	}
 
-	return err;
+	out = U8(i64);
+	return ErrorCode::NONE;
 }
 
 String& String::operator=(StringAuto&& b)

+ 59 - 9
src/anki/util/String.h

@@ -207,13 +207,28 @@ public:
 	}
 
 	/// Convert to F64.
-	ANKI_USE_RESULT Error toF64(F64& out) const;
+	ANKI_USE_RESULT Error toNumber(F64& out) const;
 
 	/// Convert to F32.
-	ANKI_USE_RESULT Error toF32(F32& out) const;
+	ANKI_USE_RESULT Error toNumber(F32& out) const;
+
+	/// Convert to I8.
+	ANKI_USE_RESULT Error toNumber(I8& out) const;
 
 	/// Convert to I64.
-	ANKI_USE_RESULT Error toI64(I64& out) const;
+	ANKI_USE_RESULT Error toNumber(I64& out) const;
+
+	/// Convert to I32.
+	ANKI_USE_RESULT Error toNumber(I32& out) const;
+
+	/// Convert to U8.
+	ANKI_USE_RESULT Error toNumber(U8& out) const;
+
+	/// Convert to U64.
+	ANKI_USE_RESULT Error toNumber(U64& out) const;
+
+	/// Convert to U32.
+	ANKI_USE_RESULT Error toNumber(U32& out) const;
 
 private:
 	const Char* m_ptr = nullptr;
@@ -347,6 +362,11 @@ public:
 		return &m_data[m_data.getSize() - 1];
 	}
 
+	operator Bool() const
+	{
+		return !isEmpty();
+	}
+
 	/// Return true if strings are equal
 	Bool operator==(const String& b) const
 	{
@@ -502,21 +522,51 @@ public:
 	void toString(Allocator alloc, TNumber number);
 
 	/// Convert to F64.
-	ANKI_USE_RESULT Error toF64(F64& out) const
+	ANKI_USE_RESULT Error toNumber(F64& out) const
 	{
-		return toCString().toF64(out);
+		return toCString().toNumber(out);
 	}
 
 	/// Convert to F32.
-	ANKI_USE_RESULT Error toF32(F32& out) const
+	ANKI_USE_RESULT Error toNumber(F32& out) const
 	{
-		return toCString().toF32(out);
+		return toCString().toNumber(out);
 	}
 
 	/// Convert to I64.
-	ANKI_USE_RESULT Error toI64(I64& out) const
+	ANKI_USE_RESULT Error toNumber(I64& out) const
+	{
+		return toCString().toNumber(out);
+	}
+
+	/// Convert to I32.
+	ANKI_USE_RESULT Error toNumber(I32& out) const
+	{
+		return toCString().toNumber(out);
+	}
+
+	/// Convert to I8.
+	ANKI_USE_RESULT Error toNumber(I8& out) const
+	{
+		return toCString().toNumber(out);
+	}
+
+	/// Convert to U64.
+	ANKI_USE_RESULT Error toNumber(U64& out) const
+	{
+		return toCString().toNumber(out);
+	}
+
+	/// Convert to U32.
+	ANKI_USE_RESULT Error toNumber(U32& out) const
+	{
+		return toCString().toNumber(out);
+	}
+
+	/// Convert to U8.
+	ANKI_USE_RESULT Error toNumber(U8& out) const
 	{
-		return toCString().toI64(out);
+		return toCString().toNumber(out);
 	}
 
 protected:

+ 5 - 0
src/anki/util/StringList.h

@@ -33,6 +33,11 @@ public:
 	// Use the base constructors
 	using Base::Base;
 
+	operator Bool() const
+	{
+		return !Base::isEmpty();
+	}
+
 	void destroy(Allocator alloc);
 
 	/// Join all the elements into a single big string using a the seperator @a separator

+ 0 - 142
tests/resource/MaterialLoader.cpp

@@ -1,142 +0,0 @@
-// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include "tests/framework/Framework.h"
-#include "anki/resource/MaterialLoader.h"
-#include "anki/misc/Xml.h"
-#include <unordered_map>
-
-namespace anki
-{
-
-static const char* MTL = R"(
-<material>
-	<levelsOfDetail>3</levelsOfDetail>
-	<shadow>1</shadow>
-
-	<programs>
-		<program>
-			<type>vert</type>
-			<inputs>
-				<input>
-					<name>anki_mvp</name>
-					<type>mat4</type>
-				</input>
-				<input>
-					<name>c</name>
-					<type>float</type>
-					<value>1.0</value>
-					<const>1</const>
-				</input>
-				<input>
-					<name>anki_msDepthRt</name>
-					<type>sampler2D</type>
-				</input>
-				<input>
-					<name>sampl</name>
-					<type>sampler2D</type>
-					<value>aasdfasdf/asdfasdf</value>
-				</input>
-				<input>
-					<name>mouse</name>
-					<type>float</type>
-					<value>1.0</value>
-					<inShadow>0</inShadow>
-				</input>
-				<input>
-					<name>cat</name>
-					<type>vec3</type>
-					<value>1.0 1.0 1.0</value>
-					<inShadow>1</inShadow>
-				</input>
-			</inputs>
-
-			<includes><include>file.glsl</include></includes>
-
-			<operations>
-				<operation>
-					<id>12</id>
-					<returnType>vec3</returnType>
-					<function>ha</function>
-				</operation>
-				<operation>
-					<id>123</id>
-					<returnType>vec3</returnType>
-					<function>foo</function>
-					<arguments>
-						<argument>anki_mvp</argument>
-						<argument>c</argument>
-						<argument>cat</argument>
-						<argument>out12</argument>
-						<argument>anki_msDepthRt</argument>
-					</arguments>
-				</operation>
-				<operation>
-					<id>124</id>
-					<returnType>void</returnType>
-					<function>boo</function>
-					<arguments>
-						<argument>out123</argument>
-						<argument>mouse</argument>
-						<argument>sampl</argument>
-					</arguments>
-				</operation>
-			</operations>
-		</program>
-	</programs>
-</material>
-)";
-
-ANKI_TEST(Resource, MaterialLoader)
-{
-	XmlDocument doc;
-	HeapAllocator<U8> alloc(allocAligned, nullptr);
-	MaterialLoader loader(alloc);
-
-	ANKI_TEST_EXPECT_NO_ERR(doc.parse(MTL, alloc));
-	ANKI_TEST_EXPECT_NO_ERR(loader.parseXmlDocument(doc));
-
-	{
-		RenderingKey key(Pass::SM, 1, false, 3);
-		loader.mutate(key);
-		printf("%s\n", &loader.getShaderSource(ShaderType::VERTEX)[0]);
-
-		std::unordered_map<std::string, Bool> map;
-		map["anki_mvp"] = true;
-		map["c"] = false;
-		map["anki_msDepthRt"] = false;
-		map["sampl"] = true;
-		map["mouse"] = true;
-		map["cat"] = true;
-
-		Error err = loader.iterateAllInputVariables([&](const MaterialLoaderInputVariable& in) -> Error {
-			ANKI_TEST_EXPECT_EQ(map[&in.m_name.toCString()[0]], true);
-
-			if(in.m_flags.m_inBlock)
-			{
-				printf("var in block: %s %d %d %d %d\n",
-					&in.m_name[0],
-					in.m_blockInfo.m_offset,
-					in.m_blockInfo.m_arraySize,
-					in.m_blockInfo.m_arrayStride,
-					in.m_blockInfo.m_matrixStride);
-			}
-
-			return ErrorCode::NONE;
-		});
-		(void)err;
-	}
-
-	// Check block size
-	/*{
-		RenderingKey key(Pass::MS_FS, 1, false, 4);
-		loader.mutate(key);
-
-		ANKI_TEST_EXPECT_EQ(loader.getUniformBlockSize(),
-			16 * 4 * sizeof(F32) + 4 * sizeof(F32) + 3 * sizeof(F32));
-	}*/
-}
-
-} // end namespace anki

+ 3 - 3
tests/util/String.cpp

@@ -192,18 +192,18 @@ ANKI_TEST(Util, String)
 		I64 i;
 		String a;
 		a.create(alloc, "123456789");
-		ANKI_TEST_EXPECT_NO_ERR(a.toI64(i));
+		ANKI_TEST_EXPECT_NO_ERR(a.toNumber(i));
 		ANKI_TEST_EXPECT_EQ(i, 123456789);
 		a.destroy(alloc);
 
 		a.create(alloc, "-9223372036854775807");
-		ANKI_TEST_EXPECT_NO_ERR(a.toI64(i));
+		ANKI_TEST_EXPECT_NO_ERR(a.toNumber(i));
 		ANKI_TEST_EXPECT_EQ(i, -9223372036854775807);
 		a.destroy(alloc);
 
 		F64 f;
 		a.create(alloc, "123456789.145");
-		ANKI_TEST_EXPECT_NO_ERR(a.toF64(f));
+		ANKI_TEST_EXPECT_NO_ERR(a.toNumber(f));
 		ANKI_TEST_EXPECT_EQ(f, 123456789.145);
 		a.destroy(alloc);
 	}

+ 9 - 1
tools/format_source.sh

@@ -7,11 +7,19 @@ filecount=${#files[@]}
 count=0
 for f in ${files[@]}
 do
+	# Run it in parallel
 	echo -ne Formatting ${count}/${filecount}\\r
-	./thirdparty/bin/clang-format -sort-includes=false -i ${f}
+	./thirdparty/bin/clang-format -sort-includes=false -i ${f} &
 	count=$((${count}+1))
+
+	# Throttle the parallel commands
+	if !((count % 8)); then
+		wait
+	fi
 done
 
+wait
+
 echo Done! Formatted ${filecount} files
 
 

+ 163 - 372
tools/scene/ExporterMaterial.cpp

@@ -6,45 +6,73 @@
 #include "Exporter.h"
 #include <iostream>
 
+const char* MATERIAL_TEMPLATE = R"(<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="programs/MsGeneric.ankiprog">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
+		<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
+		<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
+		<mutator name="METAL_TEX" value="%metalTexMutator%"/>
+		<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
+		<mutator name="PARALLAX" value="%parallaxMutator%"/>
+		<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="normalMat" builtin="NORMAL_MATRIX"/>
+		%modelViewMat%
+	
+		%diff%
+		%spec%
+		%roughness%
+		%metallic%
+		%normal%
+		%emission%
+		%subsurface%
+		%height%
+	</inputs>
+</material>
+)";
+
 void Exporter::exportMaterial(const aiMaterial& mtl) const
 {
-	std::string diffTex;
-	std::string normTex;
-	std::string specColTex;
-	std::string shininessTex;
-	std::string dispTex;
-	std::string emissiveTex;
-	std::string metallicTex;
-
 	aiString path;
 
 	std::string name = getMaterialName(mtl);
 	LOGI("Exporting material %s", name.c_str());
 
+	std::string xml = MATERIAL_TEMPLATE;
+
 	// Diffuse texture
 	if(mtl.GetTextureCount(aiTextureType_DIFFUSE) > 0)
 	{
 		if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
 		{
-			diffTex = getFilename(path.C_Str());
+			std::string diffTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(xml, "%diff%", "<input shaderInput=\"diffTex\" value=\"" + diffTex + "\"/>");
+			xml = replaceAllString(xml, "%diffTexMutator%", "1");
 		}
 		else
 		{
 			ERROR("Failed to retrieve texture");
 		}
 	}
-
-	// Normal texture
-	if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
+	else
 	{
-		if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
-		{
-			normTex = getFilename(path.C_Str());
-		}
-		else
-		{
-			ERROR("Failed to retrieve texture");
-		}
+		aiColor3D diffCol = {0.0, 0.0, 0.0};
+		mtl.Get(AI_MATKEY_COLOR_DIFFUSE, diffCol);
+
+		xml = replaceAllString(xml,
+			"%diff%",
+			"<input shaderInput=\"diffColor\" value=\"" + std::to_string(diffCol[0]) + " " + std::to_string(diffCol[1])
+				+ " "
+				+ std::to_string(diffCol[2])
+				+ "\"/>");
+
+		xml = replaceAllString(xml, "%diffTexMutator%", "0");
 	}
 
 	// Specular color
@@ -52,51 +80,63 @@ void Exporter::exportMaterial(const aiMaterial& mtl) const
 	{
 		if(mtl.GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS)
 		{
-			specColTex = getFilename(path.C_Str());
+			std::string specTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(xml, "%spec%", "<input shaderInput=\"specTex\" value=\"" + specTex + "\"/>");
+			xml = replaceAllString(xml, "%specTexMutator%", "1");
 		}
 		else
 		{
 			ERROR("Failed to retrieve texture");
 		}
 	}
+	else
+	{
+		aiColor3D specCol = {0.0, 0.0, 0.0};
+		mtl.Get(AI_MATKEY_COLOR_SPECULAR, specCol);
+
+		xml = replaceAllString(xml,
+			"%spec%",
+			"<input shaderInput=\"specColor\" value=\"" + std::to_string(specCol[0]) + " " + std::to_string(specCol[1])
+				+ " "
+				+ std::to_string(specCol[2])
+				+ "\"/>");
 
-	// Shininess color
+		xml = replaceAllString(xml, "%specTexMutator%", "0");
+	}
+
+	// Roughness
 	if(mtl.GetTextureCount(aiTextureType_SHININESS) > 0)
 	{
 		if(mtl.GetTexture(aiTextureType_SHININESS, 0, &path) == AI_SUCCESS)
 		{
-			shininessTex = getFilename(path.C_Str());
-		}
-		else
-		{
-			ERROR("Failed to retrieve texture");
-		}
-	}
+			std::string shininessTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(
+				xml, "%roughness%", "<input shaderInput=\"roughnessTex\" value=\"" + shininessTex + "\"/>");
 
-	// Height texture
-	if(mtl.GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
-	{
-		if(mtl.GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
-		{
-			dispTex = getFilename(path.C_Str());
+			xml = replaceAllString(xml, "%roughnessTexMutator%", "1");
 		}
 		else
 		{
 			ERROR("Failed to retrieve texture");
 		}
 	}
-
-	// Emissive texture
-	if(mtl.GetTextureCount(aiTextureType_EMISSIVE) > 0)
+	else
 	{
-		if(mtl.GetTexture(aiTextureType_EMISSIVE, 0, &path) == AI_SUCCESS)
-		{
-			emissiveTex = getFilename(path.C_Str());
-		}
-		else
+		float shininess = 0.0;
+		mtl.Get(AI_MATKEY_SHININESS, shininess);
+		const float MAX_SHININESS = 511.0;
+		shininess = std::min(MAX_SHININESS, shininess);
+		if(shininess > MAX_SHININESS)
 		{
-			ERROR("Failed to retrieve texture");
+			LOGW("Shininness exceeds %f", MAX_SHININESS);
 		}
+
+		shininess = shininess / MAX_SHININESS;
+
+		xml = replaceAllString(
+			xml, "%roughness%", "<input shaderInput=\"roughness\" value=\"" + std::to_string(shininess) + "\" />");
+
+		xml = replaceAllString(xml, "%roughnessTexMutator%", "0");
 	}
 
 	// Metallic texture
@@ -104,382 +144,133 @@ void Exporter::exportMaterial(const aiMaterial& mtl) const
 	{
 		if(mtl.GetTexture(aiTextureType_REFLECTION, 0, &path) == AI_SUCCESS)
 		{
-			metallicTex = getFilename(path.C_Str());
+			std::string metallicTex = m_texrpath + getFilename(path.C_Str());
+			xml =
+				replaceAllString(xml, "%metallic%", "<input shaderInput=\"metalTex\" value=\"" + metallicTex + "\"/>");
+
+			xml = replaceAllString(xml, "%metalTexMutator%", "1");
 		}
 		else
 		{
 			ERROR("Failed to retrieve texture");
 		}
 	}
-
-	// Write file
-	static const char* diffNormSpecFragTemplate =
-#include "templates/diffNormSpecFrag.h"
-		;
-	static const char* simpleVertTemplate =
-#include "templates/simpleVert.h"
-		;
-	static const char* tessVertTemplate =
-#include "templates/tessVert.h"
-		;
-
-	static const char* readRgbFromTextureTemplate = R"(
-				<operation>
-					<id>%id%</id>
-					<returnType>vec3</returnType>
-					<function>readRgbFromTexture</function>
-					<arguments>
-						<argument>%map%</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>)";
-
-	static const char* readRFromTextureTemplate = R"(
-				<operation>
-					<id>%id%</id>
-					<returnType>float</returnType>
-					<function>readRFromTexture</function>
-					<arguments>
-						<argument>%map%</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>)";
-
-	// Compose full template
-	// First geometry part
-	std::string materialStr;
-	materialStr = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
-	materialStr += "\n<material>\n\t<programs>\n";
-	if(/*dispTex.empty()*/ 1)
-	{
-		materialStr += simpleVertTemplate;
-	}
 	else
 	{
-		materialStr += tessVertTemplate;
-	}
-
-	materialStr += "\n";
+		float metallic = 0.0;
+		if(mtl.mAnKiProperties.find("metallic") != mtl.mAnKiProperties.end())
+		{
+			metallic = std::stof(mtl.mAnKiProperties.at("metallic"));
+		}
 
-	// Then fragment part
-	materialStr += diffNormSpecFragTemplate;
-	materialStr += "\n\t</programs>\t</material>";
+		xml = replaceAllString(
+			xml, "%metallic%", "<input shaderInput=\"metallic\" value=\"" + std::to_string(metallic) + "\"/>");
 
-	// Replace strings
-	if(!dispTex.empty())
-	{
-		materialStr = replaceAllString(materialStr, "%dispMap%", m_texrpath + dispTex);
+		xml = replaceAllString(xml, "%metalTexMutator%", "0");
 	}
 
-	// Diffuse
-	if(!diffTex.empty())
-	{
-		materialStr = replaceAllString(materialStr,
-			"%diffuseColorInput%",
-			R"(<input><type>sampler2D</type><name>uDiffuseColor</name><value>)" + m_texrpath + diffTex
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr, "%diffuseColorFunc%", readRgbFromTextureTemplate);
-
-		materialStr = replaceAllString(materialStr, "%id%", "10");
-
-		materialStr = replaceAllString(materialStr, "%map%", "uDiffuseColor");
-
-		materialStr = replaceAllString(materialStr, "%diffuseColorArg%", "out10");
-	}
-	else
+	// Normal texture
+	if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
 	{
-		aiColor3D diffCol = {0.0, 0.0, 0.0};
-		mtl.Get(AI_MATKEY_COLOR_DIFFUSE, diffCol);
-
-		materialStr = replaceAllString(materialStr,
-			"%diffuseColorInput%",
-			R"(<input><type>vec3</type><name>uDiffuseColor</name><value>)" + std::to_string(diffCol[0]) + " "
-				+ std::to_string(diffCol[1])
-				+ " "
-				+ std::to_string(diffCol[2])
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr, "%diffuseColorFunc%", "");
-
-		materialStr = replaceAllString(materialStr, "%diffuseColorArg%", "uDiffuseColor");
-	}
+		if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
+		{
+			std::string normTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(xml, "%normal%", "<input shaderInput=\"normalTex\" value=\"" + normTex + "\"/>");
 
-	// Normal
-	if(!normTex.empty())
-	{
-		materialStr = replaceAllString(materialStr,
-			"%normalInput%",
-			R"(<input><type>sampler2D</type><name>uNormal</name><value>)" + m_texrpath + normTex
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr,
-			"%normalFunc%",
-			R"(
-				<operation>
-					<id>20</id>
-					<returnType>vec3</returnType>
-					<function>readNormalFromTexture</function>
-					<arguments>
-						<argument>out0</argument>
-						<argument>out1</argument>
-						<argument>uNormal</argument>
-						<argument>out2</argument>
-					</arguments>
-				</operation>)");
-
-		materialStr = replaceAllString(materialStr, "%normalArg%", "out20");
+			xml = replaceAllString(xml, "%normalTexMutator%", "1");
+		}
+		else
+		{
+			ERROR("Failed to retrieve texture");
+		}
 	}
 	else
 	{
-		materialStr = replaceAllString(materialStr, "%normalInput%", " ");
-
-		materialStr = replaceAllString(materialStr, "%normalFunc%", " ");
-
-		materialStr = replaceAllString(materialStr, "%normalArg%", "out0");
+		xml = replaceAllString(xml, "%normal%", "");
+		xml = replaceAllString(xml, "%normalTexMutator%", "0");
 	}
 
-	// Specular
-	if(!specColTex.empty())
+	// Emissive texture
+	if(mtl.GetTextureCount(aiTextureType_EMISSIVE) > 0)
 	{
-		materialStr = replaceAllString(materialStr,
-			"%specularColorInput%",
-			R"(<input><type>sampler2D</type><name>uSpecularColor</name><value>)" + m_texrpath + specColTex
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr, "%specularColorFunc%", readRgbFromTextureTemplate);
-
-		materialStr = replaceAllString(materialStr, "%id%", "50");
-
-		materialStr = replaceAllString(materialStr, "%map%", "uSpecularColor");
+		if(mtl.GetTexture(aiTextureType_EMISSIVE, 0, &path) == AI_SUCCESS)
+		{
+			std::string emissiveTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(
+				xml, "%emission%", "<input shaderInput=\"emissiveTex\" value=\"" + emissiveTex + "\"/>");
 
-		materialStr = replaceAllString(materialStr, "%specularColorArg%", "out50");
+			xml = replaceAllString(xml, "%emissiveTexMutator%", "1");
+		}
+		else
+		{
+			ERROR("Failed to retrieve texture");
+		}
 	}
 	else
 	{
-		aiColor3D specCol = {0.0, 0.0, 0.0};
-		mtl.Get(AI_MATKEY_COLOR_SPECULAR, specCol);
+		aiColor3D emissionCol = {0.0, 0.0, 0.0};
+		mtl.Get(AI_MATKEY_COLOR_EMISSIVE, emissionCol);
 
-		materialStr = replaceAllString(materialStr,
-			"%specularColorInput%",
-			R"(<input><type>vec3</type><name>uSpecularColor</name><value>)" + std::to_string(specCol[0]) + " "
-				+ std::to_string(specCol[1])
+		xml = replaceAllString(xml,
+			"%emission%",
+			"<input shaderInput=\"emission\" value=\"" + std::to_string(emissionCol[0]) + " "
+				+ std::to_string(emissionCol[1])
 				+ " "
-				+ std::to_string(specCol[2])
-				+ R"(</value></input>)");
+				+ std::to_string(emissionCol[2])
+				+ "\"/>");
 
-		materialStr = replaceAllString(materialStr, "%specularColorFunc%", "");
-
-		materialStr = replaceAllString(materialStr, "%specularColorArg%", "uSpecularColor");
+		xml = replaceAllString(xml, "%emissiveTexMutator%", "0");
 	}
 
-	// Roughness
-	if(!shininessTex.empty())
-	{
-		materialStr = replaceAllString(materialStr,
-			"%specularPowerInput%",
-			R"(<input><type>sampler2D</type><name>roughness</name><value>)" + m_texrpath + shininessTex
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr, "%specularPowerValue%", m_texrpath + shininessTex);
-
-		materialStr = replaceAllString(materialStr, "%specularPowerFunc%", readRFromTextureTemplate);
-
-		materialStr = replaceAllString(materialStr, "%id%", "60");
-
-		materialStr = replaceAllString(materialStr, "%map%", "roughness");
-
-		materialStr = replaceAllString(materialStr, "%specularPowerArg%", "out60");
-	}
-	else
+	// Subsurface
 	{
-		float shininess = 0.0;
-		mtl.Get(AI_MATKEY_SHININESS, shininess);
-		const float MAX_SHININESS = 511.0;
-		shininess = std::min(MAX_SHININESS, shininess);
-		if(shininess > MAX_SHININESS)
+		float subsurface = 0.0;
+		if(mtl.mAnKiProperties.find("subsurface") != mtl.mAnKiProperties.end())
 		{
-			LOGW("Shininness exceeds %f", MAX_SHININESS);
+			subsurface = std::stof(mtl.mAnKiProperties.at("subsurface"));
 		}
 
-		shininess = shininess / MAX_SHININESS;
-
-		materialStr = replaceAllString(materialStr,
-			"%specularPowerInput%",
-			R"(<input><type>float</type><name>roughness</name><const>1</const><value>)" + std::to_string(shininess)
-				+ R"(</value></input>)");
-
-		materialStr = replaceAllString(materialStr, "%specularPowerFunc%", "");
-
-		materialStr = replaceAllString(materialStr, "%specularPowerArg%", "roughness");
-	}
-
-	materialStr = replaceAllString(materialStr, "%maxSpecularPower%", " ");
-
-	// Emission
-	aiColor3D emissionCol = {0.0, 0.0, 0.0};
-	mtl.Get(AI_MATKEY_COLOR_EMISSIVE, emissionCol);
-	float emission = (emissionCol[0] + emissionCol[1] + emissionCol[2]) / 3.0;
-
-	if(!emissiveTex.empty())
-	{
-		materialStr = replaceAllString(materialStr,
-			"%emissionInput%",
-			"<input><type>sampler2D</type><name>emissionTex</name><value>" + m_texrpath + emissiveTex
-				+ "</value></input>)\n"
-				+ "\t\t\t\t<input><type>float</type><name>emission</"
-				  "name><value>"
-				+ std::to_string(10.0)
-				+ "</value><const>1</const></input>");
-
-		std::string func = readRFromTextureTemplate;
-		func = replaceAllString(func, "%id%", "71");
-		func = replaceAllString(func, "%map%", "emissionTex");
-		func += R"(
-				<operation>
-					<id>70</id>
-					<returnType>float</returnType>
-					<function>mul</function>
-					<arguments>
-						<argument>out71</argument>
-						<argument>emission</argument>
-					</arguments>
-				</operation>)";
-
-		materialStr = replaceAllString(materialStr, "%emissionFunc%", func);
-
-		materialStr = replaceAllString(materialStr, "%map%", "emissionTex");
-
-		materialStr = replaceAllString(materialStr, "%emissionArg%", "out70");
-	}
-	else
-	{
-		materialStr = replaceAllString(materialStr,
-			"%emissionInput%",
-			R"(<input><type>float</type><name>emission</name><value>)" + std::to_string(emission)
-				+ R"(</value><const>1</const></input>)");
-
-		materialStr = replaceAllString(materialStr, "%emissionFunc%", "");
-
-		materialStr = replaceAllString(materialStr, "%emissionArg%", "emission");
+		xml = replaceAllString(
+			xml, "%subsurface%", "<input shaderInput=\"subsurface\" value=\"" + std::to_string(subsurface) + "\"/>");
 	}
 
-	// Metallic
-	if(!metallicTex.empty())
+	// Height texture
+	if(mtl.GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
 	{
-		materialStr = replaceAllString(materialStr,
-			"%metallicInput%",
-			"<input><type>sampler2D</type><name>metallicTex</name><value>" + m_texrpath + metallicTex
-				+ "</value></input>");
-
-		std::string func = readRFromTextureTemplate;
-		func = replaceAllString(func, "%id%", "80");
-		func = replaceAllString(func, "%map%", "metallicTex");
-
-		materialStr = replaceAllString(materialStr, "%metallicFunc%", func);
+		if(mtl.GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
+		{
+			std::string dispTex = m_texrpath + getFilename(path.C_Str());
+			xml = replaceAllString(xml,
+				"%height%",
+				"<input shaderInput=\"heightTex\" value=\"" + dispTex
+					+ "\"/>\n"
+					  "\t\t<input shaderInput=\"heightMapScale\" value=\"0.05\"/>");
 
-		materialStr = replaceAllString(materialStr, "%map%", "metallicTex");
+			xml = replaceAllString(
+				xml, "%modelViewMat%", "<input shaderInput=\"modelViewMat\" builtin=\"MODEL_VIEW_MATRIX\"/>");
 
-		materialStr = replaceAllString(materialStr, "%metallicArg%", "out80");
-	}
-	else
-	{
-		float metallic = 0.0;
-		if(mtl.mAnKiProperties.find("metallic") != mtl.mAnKiProperties.end())
+			xml = replaceAllString(xml, "%parallaxMutator%", "1");
+		}
+		else
 		{
-			metallic = std::stof(mtl.mAnKiProperties.at("metallic"));
+			ERROR("Failed to retrieve texture");
 		}
-
-		materialStr = replaceAllString(materialStr,
-			"%metallicInput%",
-			R"(<input><type>float</type><name>metallic</name><value>)" + std::to_string(metallic)
-				+ R"(</value><const>1</const></input>)");
-
-		materialStr = replaceAllString(materialStr, "%metallicFunc%", "");
-
-		materialStr = replaceAllString(materialStr, "%metallicArg%", "metallic");
-	}
-
-	// Height to parallax
-	if(!dispTex.empty())
-	{
-		materialStr = replaceAllString(materialStr,
-			"%heightVertInput%",
-			"<input><type>mat4</type><name>anki_mv"
-			"</name><inShadow>0</inShadow></input>");
-
-		materialStr = replaceAllString(materialStr,
-			"%heightVertFunc%",
-			R"(<operation>
-					<id>2</id>
-					<returnType>void</returnType>
-					<function>writeParallax</function>
-					<arguments>
-						<argument>anki_n</argument>
-						<argument>anki_mv</argument>
-					</arguments>
-				</operation>)");
-
-		materialStr = replaceAllString(materialStr,
-			"%heightInput%",
-			"<input><type>sampler2D</type><name>heightMap</name>"
-			"<value>"
-				+ m_texrpath
-				+ dispTex
-				+ "</value></input>\n"
-				  "\t\t\t\t<input><type>float</type><name>heightMapScale</name>"
-				  "<value>0.05</value><const>1</const></input>");
-
-		// At this point everyone will have to use out4 as tex coords
-		materialStr = replaceAllString(materialStr, "<argument>out2</argument>", "<argument>out4</argument>");
-
-		materialStr = replaceAllString(materialStr,
-			"%heightFunc%",
-			R"(<operation>
-					<id>4</id>
-					<returnType>vec2</returnType>
-					<function>computeTextureCoordParallax</function>
-					<arguments>
-						<argument>heightMap</argument>
-						<argument>out2</argument>
-						<argument>heightMapScale</argument>
-					</arguments>
-				</operation>)");
 	}
 	else
 	{
-		materialStr = replaceAllString(materialStr, "%heightVertInput%", " ");
-		materialStr = replaceAllString(materialStr, "%heightVertFunc%", " ");
-		materialStr = replaceAllString(materialStr, "%heightInput%", " ");
-		materialStr = replaceAllString(materialStr, "%heightFunc%", " ");
-	}
-
-	// Continue
-	materialStr = replaceAllString(materialStr, "%diffuseMap%", m_texrpath + diffTex);
-
-	// Subsurface
-	float subsurface = 0.0;
-	if(mtl.mAnKiProperties.find("subsurface") != mtl.mAnKiProperties.end())
-	{
-		subsurface = std::stof(mtl.mAnKiProperties.at("subsurface"));
+		xml = replaceAllString(xml, "%height%", "");
+		xml = replaceAllString(xml, "%modelViewMat%", "");
+		xml = replaceAllString(xml, "%parallaxMutator%", "0");
 	}
 
-	materialStr = replaceAllString(materialStr,
-		"%subsurfaceInput%",
-		"<input><type>float</type><name>subsurface</name>"
-		"<const>1</const><value>"
-			+ std::to_string(subsurface)
-			+ "</value></input>");
-	materialStr = replaceAllString(materialStr, "%subsurfaceArg%", "subsurface");
-
 	// Replace texture extensions with .anki
-	materialStr = replaceAllString(materialStr, ".tga", ".ankitex");
-	materialStr = replaceAllString(materialStr, ".png", ".ankitex");
-	materialStr = replaceAllString(materialStr, ".jpg", ".ankitex");
-	materialStr = replaceAllString(materialStr, ".jpeg", ".ankitex");
+	xml = replaceAllString(xml, ".tga", ".ankitex");
+	xml = replaceAllString(xml, ".png", ".ankitex");
+	xml = replaceAllString(xml, ".jpg", ".ankitex");
+	xml = replaceAllString(xml, ".jpeg", ".ankitex");
 
 	// Open and write file
 	std::fstream file;
 	file.open(m_outputDirectory + name + ".ankimtl", std::ios::out);
-	file << materialStr;
+	file << xml;
 }

+ 4 - 3
tools/scene/Main.cpp

@@ -9,9 +9,10 @@ static void parseCommandLineArgs(int argc, char** argv, Exporter& exporter)
 {
 	static const char* usage = R"(Usage: %s in_file out_dir [options]
 Options:
--rpath <string>    : Append a string to the meshes and materials
--texrpath <string> : Append a string to the textures paths
--flipyz            : Flip y with z (For blender exports)
+-rpath <string>     : Replace all absolute paths of assets with that path
+-texrpath <string>  : Same as rpath but for textures
+-progrpath <string> : Same as rpath but for shader programs
+-flipyz             : Flip y with z (For blender exports)
 )";
 
 	// Parse config

+ 273 - 0
tools/shader/generate_program_permutations.py

@@ -0,0 +1,273 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+# All rights reserved.
+# Code licensed under the BSD License.
+# http://www.anki3d.org/LICENSE
+
+import os
+import argparse
+
+# Template
+template_xml = """<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by generate_program_permutations.py -->
+<shaderProgram>
+	<shaders>
+		<shader>
+			<type>vert</type>
+			<inputs>
+%vertInputs%
+			</inputs>
+			<source><![CDATA[#include "shaders/MsCommonVert.glsl"
+void main() {
+%vertSrc%
+}
+			]]></source>
+		</shader>
+		<shader>
+			<type>frag</type>
+			<inputs>
+%fragInputs%
+			</inputs>
+			<source><![CDATA[#include "shaders/MsCommonFrag.glsl"
+void main() {
+%fragSrc%
+}
+			]]></source>
+		</shader>
+	</shaders>
+</shaderProgram>
+"""
+
+# Input type
+INPUT_NONE = 0
+INPUT_TEXTURE = 1
+INPUT_CONST = 2
+INPUT_VALUE = 3 # non-const
+INPUT_COUNT = 4
+
+# Variables
+DIFFUSE = 0
+SPECULAR = 1
+ROUGHNESS = 2
+METALLIC = 3
+NORMAL = 4
+EMISSION = 5
+SUBSURFACE = 6
+HEIGHT = 7
+VARIABLE_COUNT = 8
+
+allowed_permutations = [
+	[INPUT_TEXTURE, INPUT_CONST],
+	[INPUT_TEXTURE, INPUT_CONST],
+	[INPUT_TEXTURE, INPUT_CONST],
+	[INPUT_TEXTURE, INPUT_CONST],
+	[INPUT_TEXTURE, INPUT_NONE],
+	[INPUT_TEXTURE, INPUT_CONST],
+	[INPUT_CONST],
+	[INPUT_NONE, INPUT_TEXTURE]]
+
+def parse_commandline():
+	""" Parse the command line arguments """
+
+	parser = argparse.ArgumentParser(description = "Create shader program permutations",
+			formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+
+	parser.add_argument("-o", "--output-dir", required = True, help = "specify the output directory")
+
+	args = parser.parse_args()
+
+	return args.output_dir
+
+def spin_wheel(wheel):
+	""" Spin the wheel """
+
+	done = False
+	start = VARIABLE_COUNT - 1
+
+	while True:
+		if start == -1:
+			done = True
+			break
+
+		wheel[start] += 1
+		if wheel[start] >= INPUT_COUNT:
+			wheel[start] = INPUT_NONE
+			start = start - 1
+		else:
+			break
+
+	return done
+
+def mutate(wheel):
+	""" Create the mutation's source """
+	
+	parallax = wheel[HEIGHT] == INPUT_TEXTURE
+
+	# Vert input & source
+	if parallax:
+		vert_inputs = """				<input><type>mat4</type><name>mvp</name></input>
+				<input><type>mat3</type><name>normalMat</name><depth>0</depth></input>
+				<input><type>mat4</type><name>modelViewMat</name><depth>0</depth></input>
+"""
+
+		vert_src = """#if COLOR
+positionUvNormalTangent(mvp, normalMat);
+parallax(vodelViewMat);
+#else
+ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
+#endif
+"""
+	else:
+		vert_inputs = """				<input><type>mat4</type><name>mvp</name></input>
+				<input><type>mat3</type><name>normalMat</name><depth>0</depth></input>
+"""
+
+		vert_src = """#if COLOR
+positionUvNormalTangent(mvp, normalMat);
+#else
+ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
+#endif
+"""
+
+	# Frag inputs & source & filename
+	frag_ins = ""
+	frag_src = "#if COLOR\n"
+	fname = "ms_"
+
+	if parallax:
+		frag_ins += "\t\t\t\t<input><type>float</type><name>heightMapScale</name><const>1</const><depth>0</depth></input>\n"
+
+		frag_src += "vec2 uv = computeTextureCoordParallax(heightTex, in_uv, heightMapScale);\n"
+	else:
+		frag_src += "vec2 uv = in_uv;\n"
+
+	if wheel[DIFFUSE] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>diffTex</name><depth>0</depth></input>\n"
+
+		frag_src += "vec3 diffColor = texture(diffTex, uv).rgb;\n"
+
+		fname += "difft_"
+	elif wheel[DIFFUSE] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>vec3</type><name>diffColor</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "diffc_"
+	else:
+		assert 0
+
+	if wheel[SPECULAR] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>specTex</name><depth>0</depth></input>\n"
+
+		frag_src += "vec3 specColor = texture(specTex, uv).rgb;\n"
+
+		fname += "spect_"
+	elif wheel[SPECULAR] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>vec3</type><name>specColor</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "specc_"
+	else:
+		assert 0
+
+	if wheel[ROUGHNESS] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>roughnessTex</name><depth>0</depth></input>\n"
+
+		frag_src += "float roughness = texture(roughnessTex, uv).r;\n"
+
+		fname += "rought_"
+	elif wheel[ROUGHNESS] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>float</type><name>roughness</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "roughc_"
+	else:
+		assert 0
+		
+	if wheel[METALLIC] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>metallicTex</name><depth>0</depth></input>\n"
+
+		frag_src += "float metallic = texture(metallicTex, uv).r;\n"
+
+		fname += "metalt_"
+	elif wheel[METALLIC] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>float</type><name>metallic</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "metalc_"
+	else:
+		assert 0
+
+	if wheel[NORMAL] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>normalTex</name><depth>0</depth></input>\n"
+
+		frag_src += "vec3 normal = readNormalFromTexture(normalTex, uv);\n"
+
+		fname += "normalt_"
+	elif wheel[NORMAL] == INPUT_NONE:
+		frag_src += "vec3 normal = in_normal;\n"
+
+		fname += "normal0_"
+	else:
+		assert 0
+
+	if wheel[EMISSION] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>emissionTex</name><depth>0</depth></input>\n"
+
+		frag_src += "vec3 emission = texture(emissionTex, uv).rgb;\n"
+
+		fname += "emist_"
+	elif wheel[EMISSION] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>vec3</type><name>emission</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "emisc_"
+	else:
+		assert 0
+
+	if wheel[SUBSURFACE] == INPUT_CONST:
+		frag_ins += "\t\t\t\t<input><type>float</type><name>subsurface</name><const>1</const><depth>0</depth></input>\n"
+
+		fname += "subsc_"
+	else:
+		assert 0
+
+	if wheel[HEIGHT] == INPUT_NONE:
+		# nothing
+		fname += "par0"
+	elif wheel[HEIGHT] == INPUT_TEXTURE:
+		frag_ins += "\t\t\t\t<input><type>sampler2D</type><name>heightTex</name><depth>0</depth></input>\n"
+
+		fname += "par1"
+	else:
+		assert 0
+
+	frag_src += "writeRts(diffColor, normal, specColor, roughness, subsurface, emission, metallic);\n"
+	frag_src += "#endif\n"
+
+	xml = template_xml
+	xml = xml.replace("%vertInputs%", vert_inputs)
+	xml = xml.replace("%vertSrc%", vert_src)
+	xml = xml.replace("%fragInputs%", frag_ins)
+	xml = xml.replace("%fragSrc%", frag_src)
+
+	return (xml, fname)
+
+def main():
+	""" Main function """
+
+	out_dir = parse_commandline()
+
+	wheel = [INPUT_NONE, INPUT_NONE, INPUT_NONE, INPUT_NONE, INPUT_NONE, INPUT_NONE, INPUT_NONE, INPUT_NONE]
+
+	while(not spin_wheel(wheel)):
+		if wheel[DIFFUSE] in allowed_permutations[DIFFUSE] \
+				and wheel[SPECULAR] in allowed_permutations[SPECULAR] \
+				and wheel[ROUGHNESS] in allowed_permutations[ROUGHNESS] \
+				and wheel[METALLIC] in allowed_permutations[METALLIC] \
+				and wheel[NORMAL] in allowed_permutations[NORMAL] \
+				and wheel[EMISSION] in allowed_permutations[EMISSION] \
+				and wheel[SUBSURFACE] in allowed_permutations[SUBSURFACE] \
+				and wheel[HEIGHT] in allowed_permutations[HEIGHT]:
+			(xml, fname) = mutate(wheel)
+
+			file = open(fname + ".ankiprog", "w")
+			file.write(xml)
+
+if __name__ == "__main__":
+	main()