Browse Source

WIP shader includes

Marko Pintera 10 years ago
parent
commit
43c78bc4d0
51 changed files with 2324 additions and 1678 deletions
  1. 7 5
      BansheeCore/BansheeCore.vcxproj
  2. 19 13
      BansheeCore/BansheeCore.vcxproj.filters
  3. 11 0
      BansheeCore/Include/BsCoreApplication.h
  4. 6 4
      BansheeCore/Include/BsCorePrerequisites.h
  5. 0 36
      BansheeCore/Include/BsGpuProgIncludeRTTI.h
  6. 2 9
      BansheeCore/Include/BsGpuProgram.h
  7. 1 3
      BansheeCore/Include/BsGpuProgramManager.h
  8. 6 6
      BansheeCore/Include/BsShaderInclude.h
  9. 3 3
      BansheeCore/Include/BsShaderIncludeImporter.h
  10. 36 0
      BansheeCore/Include/BsShaderIncludeRTTI.h
  11. 61 0
      BansheeCore/Include/BsShaderManager.h
  12. 64 53
      BansheeCore/Source/BsCoreApplication.cpp
  13. 0 37
      BansheeCore/Source/BsGpuProgInclude.cpp
  14. 0 44
      BansheeCore/Source/BsGpuProgIncludeImporter.cpp
  15. 4 28
      BansheeCore/Source/BsGpuProgram.cpp
  16. 3 3
      BansheeCore/Source/BsGpuProgramManager.cpp
  17. 2 2
      BansheeCore/Source/BsImporter.cpp
  18. 37 0
      BansheeCore/Source/BsShaderInclude.cpp
  19. 44 0
      BansheeCore/Source/BsShaderIncludeImporter.cpp
  20. 16 0
      BansheeCore/Source/BsShaderManager.cpp
  21. 1 1
      BansheeD3D9RenderSystem/Source/BsD3D9HLSLProgramFactory.cpp
  22. 1 2
      BansheeD3D9RenderSystem/Source/BsD3D9RenderAPI.cpp
  23. 2 0
      BansheeEditor/BansheeEditor.vcxproj
  24. 6 0
      BansheeEditor/BansheeEditor.vcxproj.filters
  25. 3 7
      BansheeEditor/Include/BsBuiltinEditorResources.h
  26. 5 0
      BansheeEditor/Include/BsEditorApplication.h
  27. 21 0
      BansheeEditor/Include/BsShaderIncludeHandler.h
  28. 0 4
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  29. 1 1
      BansheeEditor/Source/BsCodeEditor.cpp
  30. 42 34
      BansheeEditor/Source/BsEditorApplication.cpp
  31. 52 0
      BansheeEditor/Source/BsShaderIncludeHandler.cpp
  32. 1 1
      BansheeEditorExec/BsEditorExec.cpp
  33. 4 4
      BansheeEngine/Include/BsBuiltinResources.h
  34. 0 8
      BansheeEngine/Include/BsEnums.h
  35. 0 10
      BansheeEngine/Include/BsRenderer.h
  36. 15 13
      BansheeEngine/Source/BsApplication.cpp
  37. 0 23
      BansheeEngine/Source/BsRenderer.cpp
  38. 2 2
      BansheeRenderer/Source/BsBansheeLitTexRenderableController.cpp
  39. 2 2
      BansheeRenderer/Source/BsBansheeRenderer.cpp
  40. 521 512
      BansheeSL/BsLexerFX.c
  41. 1 1
      BansheeSL/BsLexerFX.h
  42. 1 0
      BansheeSL/BsLexerFX.l
  43. 495 491
      BansheeSL/BsParserFX.c
  44. 44 43
      BansheeSL/BsParserFX.h
  45. 12 3
      BansheeSL/BsParserFX.y
  46. 134 15
      BansheeSL/Include/BsSLFXCompiler.h
  47. 3 1
      BansheeSL/Source/BsASTFX.c
  48. 609 127
      BansheeSL/Source/BsSLFXCompiler.cpp
  49. 0 59
      MBansheeEngine/GpuProgram.cs
  50. 0 1
      MBansheeEngine/MBansheeEngine.csproj
  51. 24 67
      TODO.txt

+ 7 - 5
BansheeCore/BansheeCore.vcxproj

@@ -280,7 +280,7 @@
   <ItemGroup>
     <ClInclude Include="Include\BsCoreObjectCore.h" />
     <ClInclude Include="Include\BsDrawList.h" />
-    <ClInclude Include="Include\BsGpuProgIncludeRTTI.h" />
+    <ClInclude Include="Include\BsShaderIncludeRTTI.h" />
     <ClInclude Include="Include\BsIResourceListener.h" />
     <ClInclude Include="Include\BsMaterialParam.h" />
     <ClInclude Include="Include\BsRenderStats.h" />
@@ -340,7 +340,7 @@
     <ClInclude Include="Include\BsGpuBufferView.h" />
     <ClInclude Include="Include\BsGpuParamDesc.h" />
     <ClInclude Include="Include\BsGpuParams.h" />
-    <ClInclude Include="Include\BsGpuProgInclude.h" />
+    <ClInclude Include="Include\BsShaderInclude.h" />
     <ClInclude Include="Include\BsGpuProgram.h" />
     <ClInclude Include="Include\BsGpuProgramRTTI.h" />
     <ClInclude Include="Include\BsHardwareBuffer.h" />
@@ -352,7 +352,8 @@
     <ClInclude Include="Include\BsMeshManager.h" />
     <ClInclude Include="Include\BsOcclusionQuery.h" />
     <ClInclude Include="Include\BsPixelBuffer.h" />
-    <ClInclude Include="Include\BsGpuProgIncludeImporter.h" />
+    <ClInclude Include="Include\BsShaderIncludeImporter.h" />
+    <ClInclude Include="Include\BsShaderManager.h" />
     <ClInclude Include="Include\BsSubMesh.h" />
     <ClInclude Include="Include\BsTextureImportOptions.h" />
     <ClInclude Include="Include\BsTextureImportOptionsRTTI.h" />
@@ -452,7 +453,7 @@
     <ClCompile Include="Source\BsGpuParamBlockBuffer.cpp" />
     <ClCompile Include="Source\BsGpuParams.cpp" />
     <ClCompile Include="Source\BsProfilerGPU.cpp" />
-    <ClCompile Include="Source\BsGpuProgInclude.cpp" />
+    <ClCompile Include="Source\BsShaderInclude.cpp" />
     <ClCompile Include="Source\BsGpuProgram.cpp" />
     <ClCompile Include="Source\BsGpuResourceData.cpp" />
     <ClCompile Include="Source\BsHardwareBufferManager.cpp" />
@@ -466,7 +467,7 @@
     <ClCompile Include="Source\BsOcclusionQuery.cpp" />
     <ClCompile Include="Source\BsOSInputHandler.cpp" />
     <ClCompile Include="Source\BsPixelBuffer.cpp" />
-    <ClCompile Include="Source\BsGpuProgIncludeImporter.cpp" />
+    <ClCompile Include="Source\BsShaderIncludeImporter.cpp" />
     <ClCompile Include="Source\BsPixelData.cpp" />
     <ClCompile Include="Source\BsPixelUtil.cpp" />
     <ClCompile Include="Source\BsPixelVolume.cpp" />
@@ -478,6 +479,7 @@
     <ClCompile Include="Source\BsResourceListenerManager.cpp" />
     <ClCompile Include="Source\BsResourceManifest.cpp" />
     <ClCompile Include="Source\BsResourceMetaData.cpp" />
+    <ClCompile Include="Source\BsShaderManager.cpp" />
     <ClCompile Include="Source\BsTextureImportOptions.cpp" />
     <ClCompile Include="Source\BsTextureView.cpp" />
     <ClCompile Include="Source\BsTextData.cpp" />

+ 19 - 13
BansheeCore/BansheeCore.vcxproj.filters

@@ -263,9 +263,6 @@
     <ClInclude Include="Include\BsGpuResourceData.h">
       <Filter>Header Files\Resources</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGpuProgInclude.h">
-      <Filter>Header Files\Resources</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsVideoModeInfo.h">
       <Filter>Header Files\RenderAPI</Filter>
     </ClInclude>
@@ -422,9 +419,6 @@
     <ClInclude Include="Include\BsImporter.h">
       <Filter>Header Files\Importer</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGpuProgIncludeImporter.h">
-      <Filter>Header Files\Importer</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsCoreThreadAccessor.h">
       <Filter>Header Files\Core</Filter>
     </ClInclude>
@@ -518,9 +512,18 @@
     <ClInclude Include="Include\Win32\BsWin32Platform.h">
       <Filter>Header Files\Platform</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGpuProgIncludeRTTI.h">
+    <ClInclude Include="Include\BsShaderIncludeImporter.h">
+      <Filter>Header Files\Importer</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsShaderInclude.h">
+      <Filter>Header Files\Resources</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsShaderIncludeRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsShaderManager.h">
+      <Filter>Header Files\Material</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCoreApplication.cpp">
@@ -574,9 +577,6 @@
     <ClCompile Include="Source\BsMaterialRTTI.cpp">
       <Filter>Source Files\RTTI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGpuProgInclude.cpp">
-      <Filter>Source Files\Resources</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsGpuResourceData.cpp">
       <Filter>Source Files\Resources</Filter>
     </ClCompile>
@@ -736,9 +736,6 @@
     <ClCompile Include="Source\BsOSInputHandler.cpp">
       <Filter>Source Files\Input</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGpuProgIncludeImporter.cpp">
-      <Filter>Source Files\Importer</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsImporter.cpp">
       <Filter>Source Files\Importer</Filter>
     </ClCompile>
@@ -823,5 +820,14 @@
     <ClCompile Include="Source\Win32\BsWin32BrowseDialogs.cpp">
       <Filter>Source Files\Platform</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsShaderIncludeImporter.cpp">
+      <Filter>Source Files\Importer</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsShaderInclude.cpp">
+      <Filter>Source Files\Resources</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsShaderManager.cpp">
+      <Filter>Source Files\Material</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 11 - 0
BansheeCore/Include/BsCoreApplication.h

@@ -87,6 +87,11 @@ namespace BansheeEngine
 			void shutdownPlugin(DynLib* library);
 
 	protected:
+		/**
+		 * @copydoc	Module::onStartUp
+		 */
+		virtual void onStartUp();
+
 		/**
 		 * @brief	Called for each iteration of the main loop. Called before any game objects or plugins are updated.
 		 */
@@ -97,6 +102,11 @@ namespace BansheeEngine
 		 */
 		virtual void postUpdate();
 
+		/**
+		 * @brief	Returns a handler that is used for resolving shader include file paths.
+		 */
+		virtual ShaderIncludeHandlerPtr getShaderIncludeHandler() const;
+
 	private:
 		/**
 		 * @brief	Called when the frame finishes rendering.
@@ -117,6 +127,7 @@ namespace BansheeEngine
 		typedef void(*UpdatePluginFunc)();
 
 		RenderWindowPtr mPrimaryWindow;
+		START_UP_DESC mStartUpDesc;
 
 		DynLib* mRendererPlugin;
 

+ 6 - 4
BansheeCore/Include/BsCorePrerequisites.h

@@ -121,7 +121,7 @@ namespace BansheeEngine
 	struct GpuParamDataDesc;
 	struct GpuParamObjectDesc;
 	struct GpuParamBlockDesc;
-	class GpuProgInclude;
+	class ShaderInclude;
 	class TextureView;
 	class CoreObject;
 	class CoreObjectCore;
@@ -165,6 +165,7 @@ namespace BansheeEngine
 	class GpuProgramCore;
 	class IResourceListener;
 	class TextureProperties;
+	class IShaderIncludeHandler;
 	// Asset import
 	class SpecificImporter;
 	class Importer;
@@ -242,7 +243,7 @@ namespace BansheeEngine
 	typedef std::shared_ptr<GpuParams> GpuParamsPtr;
 	typedef std::shared_ptr<TextureView> TextureViewPtr;
 	typedef std::shared_ptr<Viewport> ViewportPtr;
-	typedef std::shared_ptr<GpuProgInclude> GpuProgIncludePtr;
+	typedef std::shared_ptr<ShaderInclude> ShaderIncludePtr;
 	typedef std::shared_ptr<ImportOptions> ImportOptionsPtr;
 	typedef std::shared_ptr<const ImportOptions> ConstImportOptionsPtr;
 	typedef std::shared_ptr<Font> FontPtr;
@@ -260,6 +261,7 @@ namespace BansheeEngine
 	typedef std::shared_ptr<RenderQueue> RenderQueuePtr;
 	typedef std::shared_ptr<GpuParamDesc> GpuParamDescPtr;
 	typedef std::shared_ptr<ResourceMetaData> ResourceMetaDataPtr;
+	typedef std::shared_ptr<IShaderIncludeHandler> ShaderIncludeHandlerPtr;
 }
 
 /************************************************************************/
@@ -322,7 +324,7 @@ namespace BansheeEngine
 		TID_EmulatedParamBlock = 1069,
 		TID_TextureImportOptions = 1070,
 		TID_ResourceMetaData = 1071,
-		TID_GpuProgramInclude = 1072,
+		TID_ShaderInclude = 1072,
 		TID_Viewport = 1073,
 		TID_ResourceDependencies = 1074
 	};
@@ -341,7 +343,7 @@ namespace BansheeEngine
 	typedef ResourceHandle<Texture> HTexture;
 	typedef ResourceHandle<Mesh> HMesh;
 	typedef ResourceHandle<Material> HMaterial;
-	typedef ResourceHandle<GpuProgInclude> HGpuProgInclude;
+	typedef ResourceHandle<ShaderInclude> HShaderInclude;
 	typedef ResourceHandle<Font> HFont;
 	typedef ResourceHandle<Shader> HShader;
 }

+ 0 - 36
BansheeCore/Include/BsGpuProgIncludeRTTI.h

@@ -1,36 +0,0 @@
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsRTTIType.h"
-#include "BsGpuProgInclude.h"
-
-namespace BansheeEngine
-{
-	class BS_CORE_EXPORT GpuProgIncludeRTTI : public RTTIType <GpuProgInclude, Resource, GpuProgIncludeRTTI>
-	{
-	private:
-		String& getString(GpuProgInclude* obj) { return obj->mString; }
-		void setString(GpuProgInclude* obj, String& val) { obj->mString = val; }
-	public:
-		GpuProgIncludeRTTI()
-		{
-			addPlainField("mString", 0, &GpuProgIncludeRTTI::getString, &GpuProgIncludeRTTI::setString);
-		}
-
-		virtual const String& getRTTIName()
-		{
-			static String name = "GpuProgInclude";
-			return name;
-		}
-
-		virtual UINT32 getRTTIId()
-		{
-			return TID_GpuProgramInclude;
-		}
-
-		virtual std::shared_ptr<IReflectable> newRTTIObject()
-		{
-			return GpuProgInclude::_createPtr(""); // Initial string doesn't matter, it'll get overwritten
-		}
-	};
-}

+ 2 - 9
BansheeCore/Include/BsGpuProgram.h

@@ -245,18 +245,16 @@ namespace BansheeEngine
 		 * @param	language	Language the source is written in, e.g. "hlsl" or "glsl".
 		 * @param	gptype		Type of the program, e.g. vertex or fragment.
 		 * @param	profile		Program profile specifying supported feature-set. Must match the type.
-		 * @param	includes	Optional includes to append to the source before compiling.
 		 * @param	requiresAdjacency	If true then adjacency information will be provided when rendering using this program.
 		 */
 		static GpuProgramPtr create(const String& source, const String& entryPoint, const String& language, GpuProgramType gptype,
-			GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes = nullptr, bool requiresAdjacency = false);
+			GpuProgramProfile profile, bool requiresAdjacency = false);
 
 	protected:
 		friend class GpuProgramManager;
 
 		GpuProgram(const String& source, const String& entryPoint, const String& language,
-			GpuProgramType gptype, GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes,
-			bool isAdjacencyInfoRequired = false);
+			GpuProgramType gptype, GpuProgramProfile profile, bool isAdjacencyInfoRequired = false);
 
 		/**
 		 * @copydoc	CoreObject::createCore
@@ -268,11 +266,6 @@ namespace BansheeEngine
 		 */
 		size_t calculateSize() const { return 0; } // TODO 
 
-		/**
-		 * @brief	Merges the shader code with optional includes.
-		 */
-		static String mergeWithIncludes(const String& source, const Vector<HGpuProgInclude>* includes);
-
 	protected:
 		bool mNeedsAdjacencyInfo;
 		String mLanguage;

+ 1 - 3
BansheeCore/Include/BsGpuProgramManager.h

@@ -61,12 +61,10 @@ namespace BansheeEngine
 		 * @param	language	Language the source is written in, e.g. "hlsl" or "glsl".
 		 * @param	gptype		Type of the program, e.g. vertex or fragment.
 		 * @param	profile		Program profile specifying supported feature-set. Must match the type.
-		 * @param	includes	Optional includes to append to the source before compiling.
 		 * @param	requiresAdjacency	If true then adjacency information will be provided when rendering using this program.
 		 */
 		GpuProgramPtr create(const String& source, const String& entryPoint, const String& language, 
-			GpuProgramType gptype, GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes,
-			bool requiresAdjacency = false);
+			GpuProgramType gptype, GpuProgramProfile profile, bool requiresAdjacency = false);
 
 		/**
 		 * @brief	Creates a completely empty and uninitialized GpuProgram.

+ 6 - 6
BansheeCore/Include/BsGpuProgInclude.h → BansheeCore/Include/BsShaderInclude.h

@@ -6,9 +6,9 @@
 namespace BansheeEngine
 {
 	/**
-	 * @brief	Raw text resource that serves as an include file for GPU programs.
+	 * @brief	Raw text resource that serves as an include file for shaders.
 	 */
-	class BS_CORE_EXPORT GpuProgInclude : public Resource
+	class BS_CORE_EXPORT ShaderInclude : public Resource
 	{
 	public:
 		/**
@@ -19,16 +19,16 @@ namespace BansheeEngine
 		/**
 		 * @brief	Creates a new include file resource with the specified include string.
 		 */
-		static HGpuProgInclude create(const String& includeString);
+		static HShaderInclude create(const String& includeString);
 
 		/**
 		 * @brief	Creates an include file resource with the specified include string.
 		 *
 		 * @note	Internal method. Use "create" for normal use.
 		 */
-		static GpuProgIncludePtr _createPtr(const String& includeString);
+		static ShaderIncludePtr _createPtr(const String& includeString);
 	private:
-		GpuProgInclude(const String& includeString);
+		ShaderInclude(const String& includeString);
 
 		String mString;
 
@@ -36,7 +36,7 @@ namespace BansheeEngine
 		/* 								SERIALIZATION                      		*/
 		/************************************************************************/
 	public:
-		friend class GpuProgIncludeRTTI;
+		friend class ShaderIncludeRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		virtual RTTITypeBase* getRTTI() const;
 	};

+ 3 - 3
BansheeCore/Include/BsGpuProgIncludeImporter.h → BansheeCore/Include/BsShaderIncludeImporter.h

@@ -9,11 +9,11 @@ namespace BansheeEngine
 	 * @brief	Importer using for importing GPU program (i.e. shader) include files.
 	 * 			Include files are just text files ending with ".bsi" extension.
 	 */
-	class BS_CORE_EXPORT GpuProgIncludeImporter : public SpecificImporter
+	class BS_CORE_EXPORT ShaderIncludeImporter : public SpecificImporter
 	{
 	public:
-		GpuProgIncludeImporter();
-		virtual ~GpuProgIncludeImporter();
+		ShaderIncludeImporter();
+		virtual ~ShaderIncludeImporter();
 
 		/** @copydoc SpecificImporter::isExtensionSupported */
 		virtual bool isExtensionSupported(const WString& ext) const;

+ 36 - 0
BansheeCore/Include/BsShaderIncludeRTTI.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsShaderInclude.h"
+
+namespace BansheeEngine
+{
+	class BS_CORE_EXPORT ShaderIncludeRTTI : public RTTIType <ShaderInclude, Resource, ShaderIncludeRTTI>
+	{
+	private:
+		String& getString(ShaderInclude* obj) { return obj->mString; }
+		void setString(ShaderInclude* obj, String& val) { obj->mString = val; }
+	public:
+		ShaderIncludeRTTI()
+		{
+			addPlainField("mString", 0, &ShaderIncludeRTTI::getString, &ShaderIncludeRTTI::setString);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ShaderInclude";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ShaderInclude;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return ShaderInclude::_createPtr(""); // Initial string doesn't matter, it'll get overwritten
+		}
+	};
+}

+ 61 - 0
BansheeCore/Include/BsShaderManager.h

@@ -0,0 +1,61 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Interface that provides a method for finding a shader include resource
+	 *			based on the name of the include that was provided in a shader file.
+	 */
+	class BS_CORE_EXPORT IShaderIncludeHandler
+	{
+	public:
+		virtual ~IShaderIncludeHandler() { }
+
+		/**
+		 * @brief	Attempts to find a shader include resource based on its name.
+		 */
+		virtual HShaderInclude findInclude(const String& name) const = 0;
+	};
+
+	/**
+	 * @brief	Implements shader include finding by converting the shader 
+	 *			include name into a path that the resource will be loaded from.
+	 */
+	class BS_CORE_EXPORT DefaultShaderIncludeHandler : public IShaderIncludeHandler
+	{
+	public:
+		/**
+		 * @copydoc	IShaderIncludeHandler::findInclude
+		 */
+		virtual HShaderInclude findInclude(const String& name) const override;
+	};
+
+	/**
+	 * @brief	A global manager that handles various shader specific operations.
+	 */
+	class BS_CORE_EXPORT ShaderManager : public Module <ShaderManager>
+	{
+	public:
+		ShaderManager(const ShaderIncludeHandlerPtr& handler) { mIncludeHandler = handler; }
+
+		/**
+		 * @brief	Attempts to find a shader include based on the include name.
+		 *
+		 * @note	The name is usually a path to the resource relative to the working folder,
+		 *			but can be other things depending on active handler.
+		 */
+		HShaderInclude findInclude(const String& name) const;
+
+		/**
+		 * @brief	Changes the active include handler that determines how is
+		 *			a shader include name mapped to the actual resource.
+		 */
+		void setIncludeHandler(const ShaderIncludeHandlerPtr& handler) { mIncludeHandler = handler; }
+
+	private:
+		ShaderIncludeHandlerPtr mIncludeHandler;
+	};
+}

+ 64 - 53
BansheeCore/Source/BsCoreApplication.cpp

@@ -41,6 +41,7 @@
 #include "BsMessageHandler.h"
 #include "BsResourceListenerManager.h"
 #include "BsRenderStateManager.h"
+#include "BsShaderManager.h"
 
 #include "BsMaterial.h"
 #include "BsShader.h"
@@ -60,59 +61,8 @@ namespace BansheeEngine
 
 	CoreApplication::CoreApplication(START_UP_DESC& desc)
 		:mPrimaryWindow(nullptr), mIsFrameRenderingFinished(true), mRunMainLoop(false), 
-		mRendererPlugin(nullptr), mSimThreadId(BS_THREAD_CURRENT_ID)
-	{
-		signal(SIGABRT, handleAbort);
-
-		UINT32 numWorkerThreads = BS_THREAD_HARDWARE_CONCURRENCY - 1; // Number of cores while excluding current thread.
-
-		Platform::_startUp();
-		MemStack::beginThread();
-
-		MessageHandler::startUp();
-		UUIDGenerator::startUp();
-		ProfilerCPU::startUp();
-		ProfilingManager::startUp();
-		ThreadPool::startUp<TThreadPool<ThreadBansheePolicy>>((numWorkerThreads));
-		TaskScheduler::startUp();
-		TaskScheduler::instance().removeWorker();
-		RenderStats::startUp();
-		CoreThread::startUp();
-		StringTable::startUp();
-		DeferredCallManager::startUp();
-		Time::startUp();
-		DynLibManager::startUp();
-		CoreObjectManager::startUp();
-		GameObjectManager::startUp();
-		Resources::startUp();
-		ResourceListenerManager::startUp();
-		GpuProgramManager::startUp();
-		RenderStateManager::startUp();
-		GpuProgramCoreManager::startUp();
-		RenderAPIManager::startUp();
-
-		mPrimaryWindow = RenderAPIManager::instance().initialize(desc.renderSystem, desc.primaryWindowDesc);
-
-		Input::startUp();
-		RendererManager::startUp();
-
-		loadPlugin(desc.renderer, &mRendererPlugin);
-
-		SceneManagerFactory::create();
-		RendererManager::instance().setActive(desc.renderer);
-
-		ProfilerGPU::startUp();
-		MeshManager::startUp();
-		MaterialManager::startUp();
-		FontManager::startUp();
-
-		Importer::startUp();
-
-		for (auto& importerName : desc.importers)
-			loadPlugin(importerName);
-
-		loadPlugin(desc.input, nullptr, mPrimaryWindow.get());
-	}
+		mRendererPlugin(nullptr), mSimThreadId(BS_THREAD_CURRENT_ID), mStartUpDesc(desc)
+	{ }
 
 	CoreApplication::~CoreApplication()
 	{
@@ -162,11 +112,67 @@ namespace BansheeEngine
 		ProfilerCPU::shutDown();
 		UUIDGenerator::shutDown();
 		MessageHandler::shutDown();
+		ShaderManager::shutDown();
 
 		MemStack::endThread();
 		Platform::_shutDown();
 	}
 
+	void CoreApplication::onStartUp()
+	{
+		signal(SIGABRT, handleAbort);
+
+		UINT32 numWorkerThreads = BS_THREAD_HARDWARE_CONCURRENCY - 1; // Number of cores while excluding current thread.
+
+		Platform::_startUp();
+		MemStack::beginThread();
+
+		ShaderManager::startUp(getShaderIncludeHandler());
+		MessageHandler::startUp();
+		UUIDGenerator::startUp();
+		ProfilerCPU::startUp();
+		ProfilingManager::startUp();
+		ThreadPool::startUp<TThreadPool<ThreadBansheePolicy>>((numWorkerThreads));
+		TaskScheduler::startUp();
+		TaskScheduler::instance().removeWorker();
+		RenderStats::startUp();
+		CoreThread::startUp();
+		StringTable::startUp();
+		DeferredCallManager::startUp();
+		Time::startUp();
+		DynLibManager::startUp();
+		CoreObjectManager::startUp();
+		GameObjectManager::startUp();
+		Resources::startUp();
+		ResourceListenerManager::startUp();
+		GpuProgramManager::startUp();
+		RenderStateManager::startUp();
+		GpuProgramCoreManager::startUp();
+		RenderAPIManager::startUp();
+
+		mPrimaryWindow = RenderAPIManager::instance().initialize(mStartUpDesc.renderSystem, mStartUpDesc.primaryWindowDesc);
+
+		Input::startUp();
+		RendererManager::startUp();
+
+		loadPlugin(mStartUpDesc.renderer, &mRendererPlugin);
+
+		SceneManagerFactory::create();
+		RendererManager::instance().setActive(mStartUpDesc.renderer);
+
+		ProfilerGPU::startUp();
+		MeshManager::startUp();
+		MaterialManager::startUp();
+		FontManager::startUp();
+
+		Importer::startUp();
+
+		for (auto& importerName : mStartUpDesc.importers)
+			loadPlugin(importerName);
+
+		loadPlugin(mStartUpDesc.input, nullptr, mPrimaryWindow.get());
+	}
+
 	void CoreApplication::runMainLoop()
 	{
 		mRunMainLoop = true;
@@ -361,6 +367,11 @@ namespace BansheeEngine
 		mPluginUpdateFunctions.erase(library);
 	}
 
+	ShaderIncludeHandlerPtr CoreApplication::getShaderIncludeHandler() const
+	{
+		return bs_shared_ptr<DefaultShaderIncludeHandler>();
+	}
+
 	CoreApplication& gCoreApplication()
 	{
 		return CoreApplication::instance();

+ 0 - 37
BansheeCore/Source/BsGpuProgInclude.cpp

@@ -1,37 +0,0 @@
-#include "BsGpuProgInclude.h"
-#include "BsResources.h"
-#include "BsGpuProgIncludeRTTI.h"
-
-namespace BansheeEngine
-{
-	GpuProgInclude::GpuProgInclude(const String& includeString)
-		:Resource(false), mString(includeString)
-	{
-
-	}
-
-	HGpuProgInclude GpuProgInclude::create(const String& includeString)
-	{
-		return static_resource_cast<GpuProgInclude>(gResources()._createResourceHandle(_createPtr(includeString)));
-	}
-
-	GpuProgIncludePtr GpuProgInclude::_createPtr(const String& includeString)
-	{
-		GpuProgIncludePtr gpuProgIncludePtr = bs_core_ptr<GpuProgInclude, PoolAlloc>(
-			new (bs_alloc<GpuProgInclude, PoolAlloc>()) GpuProgInclude(includeString));
-		gpuProgIncludePtr->_setThisPtr(gpuProgIncludePtr);
-		gpuProgIncludePtr->initialize();
-
-		return gpuProgIncludePtr;
-	}
-
-	RTTITypeBase* GpuProgInclude::getRTTIStatic()
-	{
-		return GpuProgIncludeRTTI::instance();
-	}
-
-	RTTITypeBase* GpuProgInclude::getRTTI() const
-	{
-		return GpuProgInclude::getRTTIStatic();
-	}
-}

+ 0 - 44
BansheeCore/Source/BsGpuProgIncludeImporter.cpp

@@ -1,44 +0,0 @@
-#include "BsGpuProgIncludeImporter.h"
-#include "BsGpuProgInclude.h"
-#include "BsDataStream.h"
-#include "BsFileSystem.h"
-
-namespace BansheeEngine
-{
-	GpuProgIncludeImporter::GpuProgIncludeImporter()
-		:SpecificImporter()
-	{
-
-	}
-
-	GpuProgIncludeImporter::~GpuProgIncludeImporter()
-	{
-
-	}
-
-	bool GpuProgIncludeImporter::isExtensionSupported(const WString& ext) const
-	{
-		WString lowerCaseExt = ext;
-		StringUtil::toLowerCase(lowerCaseExt);
-
-		return lowerCaseExt == L"bsi";
-	}
-
-	bool GpuProgIncludeImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
-	{
-		return true; // Plain-text so I don't even check for magic number
-	}
-
-	ResourcePtr GpuProgIncludeImporter::import(const Path& filePath, ConstImportOptionsPtr importOptions)
-	{
-		DataStreamPtr stream = FileSystem::openFile(filePath);
-		String includeString = stream->getAsString();
-
-		GpuProgIncludePtr gpuInclude = GpuProgInclude::_createPtr(includeString);
-
-		WString fileName = filePath.getWFilename(false);
-		gpuInclude->setName(fileName);
-
-		return gpuInclude;
-	}
-}

+ 4 - 28
BansheeCore/Source/BsGpuProgram.cpp

@@ -6,7 +6,6 @@
 #include "BsRenderAPI.h"
 #include "BsAsyncOp.h"
 #include "BsGpuParams.h"
-#include "BsGpuProgInclude.h"
 #include "BsGpuProgramManager.h"
 #include "BsResources.h"
 #include "BsGpuProgramRTTI.h"
@@ -54,8 +53,8 @@ namespace BansheeEngine
 	}
 
 	GpuProgram::GpuProgram(const String& source, const String& entryPoint, const String& language,
-		GpuProgramType gptype, GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes, bool isAdjacencyInfoRequired) 
-		: mProperties(mergeWithIncludes(source, includes), entryPoint, gptype, profile), mLanguage(language),
+		GpuProgramType gptype, GpuProgramProfile profile, bool isAdjacencyInfoRequired) 
+		: mProperties(source, entryPoint, gptype, profile), mLanguage(language),
 		 mNeedsAdjacencyInfo(isAdjacencyInfoRequired)
     {
 
@@ -92,33 +91,10 @@ namespace BansheeEngine
 			mLanguage, mProperties.getType(), mProperties.getProfile(), mNeedsAdjacencyInfo);
 	}
 
-	String GpuProgram::mergeWithIncludes(const String& source, const Vector<HGpuProgInclude>* includes)
-	{
-		if (includes != nullptr)
-		{
-			StringStream stringStream;
-			for (auto iter = includes->begin(); iter != includes->end(); ++iter)
-			{
-				if (*iter != nullptr)
-				{
-					stringStream << (*iter)->getString();
-				}
-			}
-
-			stringStream << source;
-
-			return stringStream.str();
-		}
-		else
-		{
-			return source;
-		}
-	}
-
 	GpuProgramPtr GpuProgram::create(const String& source, const String& entryPoint, const String& language, GpuProgramType gptype,
-		GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes, bool requiresAdjacency)
+		GpuProgramProfile profile, bool requiresAdjacency)
 	{
-		return GpuProgramManager::instance().create(source, entryPoint, language, gptype, profile, includes, requiresAdjacency);
+		return GpuProgramManager::instance().create(source, entryPoint, language, gptype, profile, requiresAdjacency);
 	}
 
 	/************************************************************************/

+ 3 - 3
BansheeCore/Source/BsGpuProgramManager.cpp

@@ -60,10 +60,10 @@ namespace BansheeEngine
 	};
 
 	GpuProgramPtr GpuProgramManager::create(const String& source, const String& entryPoint, const String& language,
-		GpuProgramType gptype, GpuProgramProfile profile, const Vector<HGpuProgInclude>* includes,
+		GpuProgramType gptype, GpuProgramProfile profile,
 		bool requiresAdjacencyInformation)
 	{
-		GpuProgram* program = new (bs_alloc<GpuProgram>()) GpuProgram(source, entryPoint, language, gptype, profile, includes, requiresAdjacencyInformation);
+		GpuProgram* program = new (bs_alloc<GpuProgram>()) GpuProgram(source, entryPoint, language, gptype, profile, requiresAdjacencyInformation);
 		GpuProgramPtr ret = bs_core_ptr<GpuProgram, GenAlloc>(program);
 		ret->_setThisPtr(ret);
 		ret->initialize();
@@ -73,7 +73,7 @@ namespace BansheeEngine
 
 	GpuProgramPtr GpuProgramManager::createEmpty(const String& language, GpuProgramType type)
 	{
-		GpuProgram* program = new (bs_alloc<GpuProgram>()) GpuProgram("", "", language, GPT_VERTEX_PROGRAM, GPP_VS_1_1, nullptr, false);
+		GpuProgram* program = new (bs_alloc<GpuProgram>()) GpuProgram("", "", language, GPT_VERTEX_PROGRAM, GPP_VS_1_1, false);
 		GpuProgramPtr ret = bs_core_ptr<GpuProgram, GenAlloc>(program);
 		ret->_setThisPtr(ret);
 

+ 2 - 2
BansheeCore/Source/BsImporter.cpp

@@ -2,7 +2,7 @@
 #include "BsResource.h"
 #include "BsFileSystem.h"
 #include "BsSpecificImporter.h"
-#include "BsGpuProgIncludeImporter.h"
+#include "BsShaderIncludeImporter.h"
 #include "BsImportOptions.h"
 #include "BsDebug.h"
 #include "BsDataStream.h"
@@ -14,7 +14,7 @@ namespace BansheeEngine
 {
 	Importer::Importer()
 	{
-		_registerAssetImporter(bs_new<GpuProgIncludeImporter>());
+		_registerAssetImporter(bs_new<ShaderIncludeImporter>());
 	}
 
 	Importer::~Importer()

+ 37 - 0
BansheeCore/Source/BsShaderInclude.cpp

@@ -0,0 +1,37 @@
+#include "BsShaderInclude.h"
+#include "BsResources.h"
+#include "BsShaderIncludeRTTI.h"
+
+namespace BansheeEngine
+{
+	ShaderInclude::ShaderInclude(const String& includeString)
+		:Resource(false), mString(includeString)
+	{
+
+	}
+
+	HShaderInclude ShaderInclude::create(const String& includeString)
+	{
+		return static_resource_cast<ShaderInclude>(gResources()._createResourceHandle(_createPtr(includeString)));
+	}
+
+	ShaderIncludePtr ShaderInclude::_createPtr(const String& includeString)
+	{
+		ShaderIncludePtr shaderIncludePtr = bs_core_ptr<ShaderInclude, PoolAlloc>(
+			new (bs_alloc<ShaderInclude, PoolAlloc>()) ShaderInclude(includeString));
+		shaderIncludePtr->_setThisPtr(shaderIncludePtr);
+		shaderIncludePtr->initialize();
+
+		return shaderIncludePtr;
+	}
+
+	RTTITypeBase* ShaderInclude::getRTTIStatic()
+	{
+		return ShaderIncludeRTTI::instance();
+	}
+
+	RTTITypeBase* ShaderInclude::getRTTI() const
+	{
+		return ShaderInclude::getRTTIStatic();
+	}
+}

+ 44 - 0
BansheeCore/Source/BsShaderIncludeImporter.cpp

@@ -0,0 +1,44 @@
+#include "BsShaderIncludeImporter.h"
+#include "BsShaderInclude.h"
+#include "BsDataStream.h"
+#include "BsFileSystem.h"
+
+namespace BansheeEngine
+{
+	ShaderIncludeImporter::ShaderIncludeImporter()
+		:SpecificImporter()
+	{
+
+	}
+
+	ShaderIncludeImporter::~ShaderIncludeImporter()
+	{
+
+	}
+
+	bool ShaderIncludeImporter::isExtensionSupported(const WString& ext) const
+	{
+		WString lowerCaseExt = ext;
+		StringUtil::toLowerCase(lowerCaseExt);
+
+		return lowerCaseExt == L"bslinc";
+	}
+
+	bool ShaderIncludeImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
+	{
+		return true; // Plain-text so I don't even check for magic number
+	}
+
+	ResourcePtr ShaderIncludeImporter::import(const Path& filePath, ConstImportOptionsPtr importOptions)
+	{
+		DataStreamPtr stream = FileSystem::openFile(filePath);
+		String includeString = stream->getAsString();
+
+		ShaderIncludePtr gpuInclude = ShaderInclude::_createPtr(includeString);
+
+		WString fileName = filePath.getWFilename(false);
+		gpuInclude->setName(fileName);
+
+		return gpuInclude;
+	}
+}

+ 16 - 0
BansheeCore/Source/BsShaderManager.cpp

@@ -0,0 +1,16 @@
+#include "BsShaderManager.h"
+#include "BsResources.h"
+#include "BsImporter.h"
+
+namespace BansheeEngine
+{
+	HShaderInclude DefaultShaderIncludeHandler::findInclude(const String& name) const
+	{
+		return Importer::instance().import<ShaderInclude>(name);
+	}
+
+	HShaderInclude ShaderManager::findInclude(const String& name) const
+	{
+		return mIncludeHandler->findInclude(name);
+	}
+}

+ 1 - 1
BansheeD3D9RenderSystem/Source/BsD3D9HLSLProgramFactory.cpp

@@ -3,7 +3,7 @@
 
 namespace BansheeEngine
 {
-    String D3D9HLSLProgramFactory::LANGUAGE_NAME = "hlsl";
+    String D3D9HLSLProgramFactory::LANGUAGE_NAME = "hlsl9";
 
     D3D9HLSLProgramFactory::D3D9HLSLProgramFactory()
     {

+ 1 - 2
BansheeD3D9RenderSystem/Source/BsD3D9RenderAPI.cpp

@@ -62,7 +62,7 @@ namespace BansheeEngine
 
 	const String& D3D9RenderAPI::getShadingLanguageName() const
 	{
-		static String strName("hlsl");
+		static String strName("hlsl9");
 		return strName;
 	}
 
@@ -1783,7 +1783,6 @@ namespace BansheeEngine
 		{		
 			mCurrentCapabilities = rsc;
 			mCurrentCapabilities->addShaderProfile("hlsl");
-			mCurrentCapabilities->addShaderProfile("cg");
 
 			if (mCurrentCapabilities->isShaderProfileSupported("hlsl"))
 				GpuProgramCoreManager::instance().addFactory(mHLSLProgramFactory);

+ 2 - 0
BansheeEditor/BansheeEditor.vcxproj

@@ -342,6 +342,7 @@
     <ClInclude Include="Include\BsSceneViewHandler.h" />
     <ClInclude Include="Include\BsSelection.h" />
     <ClInclude Include="Include\BsSelectionRenderer.h" />
+    <ClInclude Include="Include\BsShaderIncludeHandler.h" />
     <ClInclude Include="Include\BsTestTextSprite.h" />
     <ClInclude Include="Include\DbgEditorWidget1.h" />
     <ClInclude Include="Include\DbgEditorWidget2.h" />
@@ -412,6 +413,7 @@
     <ClCompile Include="Source\BsSceneViewHandler.cpp" />
     <ClCompile Include="Source\BsSelection.cpp" />
     <ClCompile Include="Source\BsSelectionRenderer.cpp" />
+    <ClCompile Include="Source\BsShaderIncludeHandler.cpp" />
     <ClCompile Include="Source\BsUndoRedo.cpp" />
     <ClCompile Include="Source\BsEditorApplication.cpp" />
     <ClCompile Include="Source\BsTestTextSprite.cpp" />

+ 6 - 0
BansheeEditor/BansheeEditor.vcxproj.filters

@@ -276,6 +276,9 @@
     <ClInclude Include="Include\BsSelectionRenderer.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsShaderIncludeHandler.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp">
@@ -485,5 +488,8 @@
     <ClCompile Include="Source\BsSelectionRenderer.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsShaderIncludeHandler.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 3 - 7
BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -79,6 +79,9 @@ namespace BansheeEngine
 		static const String ObjectFieldDropBtnStyleName;
 		static const String ObjectFieldClearBtnStyleName;
 
+		static const Path DefaultSkinFolder;
+		static const Path DefaultShaderFolder;
+
 	private:
 		/**
 		 * @brief	Imports all necessary resources and converts them to engine-ready format.
@@ -120,16 +123,9 @@ namespace BansheeEngine
 
 		GUISkin mSkin;
 
-		static const Path DefaultSkinFolder;
 		static const Path DefaultSkinFolderRaw;
-
-		static const Path DefaultShaderFolder;
 		static const Path DefaultShaderFolderRaw;
 
-		static const WString HLSL11ShaderSubFolder;
-		static const WString HLSL9ShaderSubFolder;
-		static const WString GLSLShaderSubFolder;
-
 		static const WString DefaultFontFilename;
 		static const UINT32 DefaultFontSize;
 

+ 5 - 0
BansheeEditor/Include/BsEditorApplication.h

@@ -47,6 +47,11 @@ namespace BansheeEngine
 
 		static void closeModalWindow(RenderWindowPtr window, HSceneObject sceneObject);
 
+		/**
+		 * @copydoc	Application::getShaderIncludeHandler
+		 */
+		virtual ShaderIncludeHandlerPtr getShaderIncludeHandler() const;
+
 	private:
 		static const Path WIDGET_LAYOUT_PATH;
 		static const Path BUILD_DATA_PATH;

+ 21 - 0
BansheeEditor/Include/BsShaderIncludeHandler.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsShaderManager.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Shader include handler for the editor. It uses project library to find
+	 *			the includes, and supports special keywords for built-in includes that
+	 *			reside outside of the project library.
+	 */
+	class BS_ED_EXPORT EditorShaderIncludeHandler : public IShaderIncludeHandler
+	{
+	public:
+		/**
+		 * @copydoc	IShaderIncludeHandler::findInclude
+		 */
+		virtual HShaderInclude findInclude(const String& name) const override;
+	};
+}

+ 0 - 4
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -51,10 +51,6 @@ namespace BansheeEngine
 
 	const Path BuiltinEditorResources::DefaultSkinFolder = L"..\\..\\..\\..\\Data\\Editor\\Skin\\";
 	const Path BuiltinEditorResources::DefaultShaderFolder = L"..\\..\\..\\..\\Data\\Editor\\Shaders\\";
-	
-	const WString BuiltinEditorResources::HLSL11ShaderSubFolder = L"HLSL11/";
-	const WString BuiltinEditorResources::HLSL9ShaderSubFolder = L"HLSL9/";
-	const WString BuiltinEditorResources::GLSLShaderSubFolder = L"GLSL/";
 
 	const WString BuiltinEditorResources::DefaultFontFilename = L"arial.ttf";
 	const UINT32 BuiltinEditorResources::DefaultFontSize = 10;

+ 1 - 1
BansheeEditor/Source/BsCodeEditor.cpp

@@ -77,7 +77,7 @@ namespace BansheeEngine
 
 		Vector<UINT32> scriptTypeIds =
 		{
-			TID_ScriptCode, TID_PlainText, TID_GpuProgram, TID_GpuProgramInclude
+			TID_ScriptCode, TID_PlainText, TID_Shader, TID_ShaderInclude
 		};
 
 		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = ProjectLibrary::instance().search(L"*", scriptTypeIds);

+ 42 - 34
BansheeEditor/Source/BsEditorApplication.cpp

@@ -15,6 +15,7 @@
 #include "BsCodeEditor.h"
 #include "BsBuildManager.h"
 #include "BsScriptCodeImporter.h"
+#include "BsShaderIncludeHandler.h"
 
 // DEBUG ONLY
 #include "DbgEditorWidget1.h"
@@ -64,40 +65,7 @@ namespace BansheeEngine
 		:Application(createRenderWindowDesc(), renderSystemPlugin, RendererPlugin::Default), 
 		mActiveRSPlugin(renderSystemPlugin), mSBansheeEditorPlugin(nullptr)
 	{
-		// TODO - Load project settings
-		mEditorSettings = bs_shared_ptr<EditorSettings>();
-
-		BuiltinEditorResources::startUp();
-
-		{
-			auto inputConfig = VirtualInput::instance().getConfiguration();
-
-			inputConfig->registerButton("Rename", BC_F2);
-			inputConfig->registerButton("Undo", BC_Z, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Redo", BC_Y, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Copy", BC_C, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Cut", BC_X, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Paste", BC_V, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Delete", BC_DELETE);
-		}
-
-		ScriptCodeImporter* scriptCodeImporter = bs_new<ScriptCodeImporter>();
-		Importer::instance()._registerAssetImporter(scriptCodeImporter);
-
-		ResourceImporter* resourceImporter = bs_new<ResourceImporter>();
-		Importer::instance()._registerAssetImporter(resourceImporter);
-
-		ProjectLibrary::startUp(getProjectPath());
-
-		UndoRedo::startUp();
-		EditorWindowManager::startUp();
-		EditorWidgetManager::startUp();
-
-		ScenePicking::startUp();
-		Selection::startUp();
-		GizmoManager::startUp();
-		BuildManager::startUp();
-		CodeEditorManager::startUp();
+		
 	}
 
 	EditorApplication::~EditorApplication()
@@ -148,6 +116,41 @@ namespace BansheeEngine
 	{
 		Application::onStartUp();
 
+		// TODO - Load project settings
+		mEditorSettings = bs_shared_ptr<EditorSettings>();
+
+		BuiltinEditorResources::startUp();
+
+		{
+			auto inputConfig = VirtualInput::instance().getConfiguration();
+
+			inputConfig->registerButton("Rename", BC_F2);
+			inputConfig->registerButton("Undo", BC_Z, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Redo", BC_Y, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Copy", BC_C, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Cut", BC_X, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Paste", BC_V, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Delete", BC_DELETE);
+		}
+
+		ScriptCodeImporter* scriptCodeImporter = bs_new<ScriptCodeImporter>();
+		Importer::instance()._registerAssetImporter(scriptCodeImporter);
+
+		ResourceImporter* resourceImporter = bs_new<ResourceImporter>();
+		Importer::instance()._registerAssetImporter(resourceImporter);
+
+		ProjectLibrary::startUp(getProjectPath());
+
+		UndoRedo::startUp();
+		EditorWindowManager::startUp();
+		EditorWidgetManager::startUp();
+
+		ScenePicking::startUp();
+		Selection::startUp();
+		GizmoManager::startUp();
+		BuildManager::startUp();
+		CodeEditorManager::startUp();
+
 		MainEditorWindow* mainWindow = MainEditorWindow::create(getPrimaryWindow());
 		loadPlugin("SBansheeEditor", &mSBansheeEditorPlugin); // Managed part of the editor
 
@@ -381,6 +384,11 @@ namespace BansheeEngine
 		fs.encode(layout.get());
 	}
 
+	ShaderIncludeHandlerPtr EditorApplication::getShaderIncludeHandler() const
+	{
+		return bs_shared_ptr<EditorShaderIncludeHandler>();
+	}
+
 	EditorApplication& gEditorApplication()
 	{
 		return static_cast<EditorApplication&>(EditorApplication::instance());

+ 52 - 0
BansheeEditor/Source/BsShaderIncludeHandler.cpp

@@ -0,0 +1,52 @@
+#include "BsShaderIncludeHandler.h"
+#include "BsProjectLibrary.h"
+#include "BsResources.h"
+#include "BsProjectResourceMeta.h"
+#include "BsBuiltinResources.h"
+#include "BsBuiltinEditorResources.h"
+
+namespace BansheeEngine
+{
+	HShaderInclude EditorShaderIncludeHandler::findInclude(const String& name) const
+	{
+		if (name.substr(0, 8) == "$ENGINE$")
+		{
+			if (name.size() > 8)
+			{
+				Path fullPath = BuiltinResources::DefaultShaderFolder;
+				Path includePath = name.substr(9, name.size() - 9);
+
+				fullPath.append(includePath);
+				fullPath.setFilename(includePath.getFilename() + ".asset");
+
+				return static_resource_cast<ShaderInclude>(Resources::instance().load(fullPath));
+			}
+		}
+		else if (name.substr(0, 8) == "$EDITOR$")
+		{
+			if (name.size() > 8)
+			{
+				Path fullPath = BuiltinEditorResources::DefaultShaderFolder;
+				Path includePath = name.substr(9, name.size() - 9);
+
+				fullPath.append(includePath);
+				fullPath.setFilename(includePath.getFilename() + ".asset");
+
+				return static_resource_cast<ShaderInclude>(Resources::instance().load(fullPath));
+			}
+		}
+		else
+		{
+			Path path = name;
+
+			ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+			if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
+			{
+				ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+				return static_resource_cast<ShaderInclude>(Resources::instance().loadFromUUID(resEntry->meta->getUUID()));
+			}
+		}
+
+		return HShaderInclude();
+	}
+}

+ 1 - 1
BansheeEditorExec/BsEditorExec.cpp

@@ -65,7 +65,7 @@ int CALLBACK WinMain(
 	InitializeDebugConsole();
 #endif
 
-	EditorApplication::startUp(RenderSystemPlugin::DX11);
+	EditorApplication::startUp(RenderSystemPlugin::OpenGL);
 	EditorApplication::instance().runMainLoop();
 	EditorApplication::shutDown();
 

+ 4 - 4
BansheeEngine/Include/BsBuiltinResources.h

@@ -98,6 +98,10 @@ namespace BansheeEngine
 		 */
 		HMaterial createDummyMaterial() const;
 
+		static const Path DefaultSkinFolder;
+		static const Path DefaultCursorFolder;
+		static const Path DefaultShaderFolder;
+
 	private:
 		/**
 		 * @brief	Imports all necessary resources and converts them to engine-ready format.
@@ -160,10 +164,6 @@ namespace BansheeEngine
 		static const Path DefaultCursorFolderRaw;
 		static const Path DefaultShaderFolderRaw;
 
-		static const Path DefaultSkinFolder;
-		static const Path DefaultCursorFolder;
-		static const Path DefaultShaderFolder;
-
 		static const WString DefaultFontFilename;
 		static const UINT32 DefaultFontSize;
 

+ 0 - 8
BansheeEngine/Include/BsEnums.h

@@ -38,12 +38,4 @@ namespace BansheeEngine
 	{
 		LayoutX, LayoutY, LayoutExplicit
 	};
-
-	/**
-	 * @brief	Languages used for writing GPU programs.
-	 */
-	enum class GpuLanguage
-	{
-		HLSL, GLSL, Undefined
-	};
 }

+ 0 - 10
BansheeEngine/Include/BsRenderer.h

@@ -36,16 +36,6 @@ namespace BansheeEngine
 		 *			Internal method.
 		 */
 		virtual void _notifyRenderableRemoved(RenderableHandlerCore* renderable) { }
-
-		/**
-		 * @brief	Returns a GPU language type based on its name.
-		 */
-		static GpuLanguage getGpuLanguageType(const String& name);
-
-		/**
-		 * @brief	Returns a GPU language name based on its type.
-		 */
-		static String getGpuLanguageName(GpuLanguage language);
 	};
 
 	/**

+ 15 - 13
BansheeEngine/Source/BsApplication.cpp

@@ -39,18 +39,7 @@ namespace BansheeEngine
 		:CoreApplication(createStartUpDesc(primaryWindowDesc, getLibNameForRenderSystem(renderSystem), getLibNameForRenderer(renderer))),
 		mMonoPlugin(nullptr), mSBansheeEnginePlugin(nullptr)
 	{
-		PlainTextImporter* importer = bs_new<PlainTextImporter>();
-		Importer::instance()._registerAssetImporter(importer);
-
-		VirtualInput::startUp();
-		ScriptManager::startUp();
-		BuiltinResources::startUp();
-		GUIManager::startUp();
-		GUIMaterialManager::startUp();
-		OverlayManager::startUp();
-		ShortcutManager::startUp();
 
-		Cursor::startUp();
 	}
 
 	Application::~Application()
@@ -86,13 +75,26 @@ namespace BansheeEngine
 
 	void Application::onStartUp()
 	{
+		CoreApplication::onStartUp();
+
+		PlainTextImporter* importer = bs_new<PlainTextImporter>();
+		Importer::instance()._registerAssetImporter(importer);
+
+		VirtualInput::startUp();
+		ScriptManager::startUp();
+		BuiltinResources::startUp();
+		GUIManager::startUp();
+		GUIMaterialManager::startUp();
+		OverlayManager::startUp();
+		ShortcutManager::startUp();
+
+		Cursor::startUp();
+
 #if BS_VER == BS_VER_DEV
 		loadPlugin("BansheeMono", &mMonoPlugin);
 		loadPlugin("SBansheeEngine", &mSBansheeEnginePlugin); // Scripting interface
 #endif
 
-		CoreApplication::onStartUp();
-
 		Cursor::instance().setCursor(CursorType::Arrow);
 	}
 

+ 0 - 23
BansheeEngine/Source/BsRenderer.cpp

@@ -3,29 +3,6 @@
 
 namespace BansheeEngine
 {
-	GpuLanguage Renderer::getGpuLanguageType(const String& name)
-	{
-		if (name == "hlsl")
-			return GpuLanguage::HLSL;
-		else if (name == "glsl")
-			return GpuLanguage::GLSL;
-
-		return GpuLanguage::Undefined;
-	}
-
-	String Renderer::getGpuLanguageName(GpuLanguage language)
-	{
-		switch (language)
-		{
-		case GpuLanguage::HLSL:
-			return "hlsl";
-		case GpuLanguage::GLSL:
-			return "glsl";
-		}
-
-		return "";
-	}
-
 	SPtr<Renderer> gRenderer()
 	{
 		return std::static_pointer_cast<Renderer>(RendererManager::instance().getActive());

+ 2 - 2
BansheeRenderer/Source/BsBansheeLitTexRenderableController.cpp

@@ -332,8 +332,8 @@ namespace BansheeEngine
 				return dot(lightDir, float4(0.5f, 0.5f, 0.5f, 0.5f));
 			})";
 
-			vsProgram = GpuProgramCore::create(vsCode, "vs_main", "hlsl", GPT_VERTEX_PROGRAM, GPP_VS_2_0);
-			psProgram = GpuProgramCore::create(psCode, "ps_main", "hlsl", GPT_FRAGMENT_PROGRAM, GPP_FS_2_0);
+			vsProgram = GpuProgramCore::create(vsCode, "vs_main", "hlsl9", GPT_VERTEX_PROGRAM, GPP_VS_2_0);
+			psProgram = GpuProgramCore::create(psCode, "ps_main", "hlsl9", GPT_FRAGMENT_PROGRAM, GPP_FS_2_0);
 		}
 		else if (rsName == RenderAPIOpenGL)
 		{

+ 2 - 2
BansheeRenderer/Source/BsBansheeRenderer.cpp

@@ -426,8 +426,8 @@ namespace BansheeEngine
 					return float4(0.3f, 0.9f, 0.3f, 1.0f);
 				})";
 
-			vsProgram = GpuProgramCore::create(vsCode, "vs_main", "hlsl", GPT_VERTEX_PROGRAM, GPP_VS_2_0);
-			psProgram = GpuProgramCore::create(psCode, "ps_main", "hlsl", GPT_FRAGMENT_PROGRAM, GPP_FS_2_0);
+			vsProgram = GpuProgramCore::create(vsCode, "vs_main", "hlsl9", GPT_VERTEX_PROGRAM, GPP_VS_2_0);
+			psProgram = GpuProgramCore::create(psCode, "ps_main", "hlsl9", GPT_FRAGMENT_PROGRAM, GPP_FS_2_0);
 		}
 		else if (rsName == RenderAPIOpenGL)
 		{

File diff suppressed because it is too large
+ 521 - 512
BansheeSL/BsLexerFX.c


+ 1 - 1
BansheeSL/BsLexerFX.h

@@ -335,7 +335,7 @@ extern int yylex \
 #undef YY_DECL
 #endif
 
-#line 226 "BsLexerFX.l"
+#line 227 "BsLexerFX.l"
 
 #line 341 "BsLexerFX.h"
 #undef yyIN_HEADER

+ 1 - 0
BansheeSL/BsLexerFX.l

@@ -90,6 +90,7 @@ Geometry		{ return TOKEN_GEOMETRY; }
 Hull			{ return TOKEN_HULL; }
 Domain			{ return TOKEN_DOMAIN; }
 Compute			{ return TOKEN_COMPUTE; }
+Common			{ return TOKEN_COMMON; }
 StencilRef		{ return TOKEN_STENCILREF; }
 
 	/* Rasterizer state keywords */

File diff suppressed because it is too large
+ 495 - 491
BansheeSL/BsParserFX.c


+ 44 - 43
BansheeSL/BsParserFX.h

@@ -142,48 +142,49 @@ extern int yydebug;
      TOKEN_HULL = 320,
      TOKEN_DOMAIN = 321,
      TOKEN_COMPUTE = 322,
-     TOKEN_STENCILREF = 323,
-     TOKEN_FILLMODE = 324,
-     TOKEN_CULLMODE = 325,
-     TOKEN_DEPTHBIAS = 326,
-     TOKEN_SDEPTHBIAS = 327,
-     TOKEN_DEPTHCLIP = 328,
-     TOKEN_SCISSOR = 329,
-     TOKEN_MULTISAMPLE = 330,
-     TOKEN_AALINE = 331,
-     TOKEN_DEPTHREAD = 332,
-     TOKEN_DEPTHWRITE = 333,
-     TOKEN_COMPAREFUNC = 334,
-     TOKEN_STENCIL = 335,
-     TOKEN_STENCILREADMASK = 336,
-     TOKEN_STENCILWRITEMASK = 337,
-     TOKEN_STENCILOPFRONT = 338,
-     TOKEN_STENCILOPBACK = 339,
-     TOKEN_FAIL = 340,
-     TOKEN_ZFAIL = 341,
-     TOKEN_ALPHATOCOVERAGE = 342,
-     TOKEN_INDEPENDANTBLEND = 343,
-     TOKEN_TARGET = 344,
-     TOKEN_INDEX = 345,
-     TOKEN_BLEND = 346,
-     TOKEN_COLOR = 347,
-     TOKEN_ALPHA = 348,
-     TOKEN_WRITEMASK = 349,
-     TOKEN_SOURCE = 350,
-     TOKEN_DEST = 351,
-     TOKEN_OP = 352,
-     TOKEN_ADDRMODE = 353,
-     TOKEN_MINFILTER = 354,
-     TOKEN_MAGFILTER = 355,
-     TOKEN_MIPFILTER = 356,
-     TOKEN_MAXANISO = 357,
-     TOKEN_MIPBIAS = 358,
-     TOKEN_MIPMIN = 359,
-     TOKEN_MIPMAX = 360,
-     TOKEN_BORDERCOLOR = 361,
-     TOKEN_U = 362,
-     TOKEN_V = 363,
-     TOKEN_W = 364
+     TOKEN_COMMON = 323,
+     TOKEN_STENCILREF = 324,
+     TOKEN_FILLMODE = 325,
+     TOKEN_CULLMODE = 326,
+     TOKEN_DEPTHBIAS = 327,
+     TOKEN_SDEPTHBIAS = 328,
+     TOKEN_DEPTHCLIP = 329,
+     TOKEN_SCISSOR = 330,
+     TOKEN_MULTISAMPLE = 331,
+     TOKEN_AALINE = 332,
+     TOKEN_DEPTHREAD = 333,
+     TOKEN_DEPTHWRITE = 334,
+     TOKEN_COMPAREFUNC = 335,
+     TOKEN_STENCIL = 336,
+     TOKEN_STENCILREADMASK = 337,
+     TOKEN_STENCILWRITEMASK = 338,
+     TOKEN_STENCILOPFRONT = 339,
+     TOKEN_STENCILOPBACK = 340,
+     TOKEN_FAIL = 341,
+     TOKEN_ZFAIL = 342,
+     TOKEN_ALPHATOCOVERAGE = 343,
+     TOKEN_INDEPENDANTBLEND = 344,
+     TOKEN_TARGET = 345,
+     TOKEN_INDEX = 346,
+     TOKEN_BLEND = 347,
+     TOKEN_COLOR = 348,
+     TOKEN_ALPHA = 349,
+     TOKEN_WRITEMASK = 350,
+     TOKEN_SOURCE = 351,
+     TOKEN_DEST = 352,
+     TOKEN_OP = 353,
+     TOKEN_ADDRMODE = 354,
+     TOKEN_MINFILTER = 355,
+     TOKEN_MAGFILTER = 356,
+     TOKEN_MIPFILTER = 357,
+     TOKEN_MAXANISO = 358,
+     TOKEN_MIPBIAS = 359,
+     TOKEN_MIPMIN = 360,
+     TOKEN_MIPMAX = 361,
+     TOKEN_BORDERCOLOR = 362,
+     TOKEN_U = 363,
+     TOKEN_V = 364,
+     TOKEN_W = 365
    };
 #endif
 
@@ -202,7 +203,7 @@ typedef union YYSTYPE
 
 
 /* Line 2579 of glr.c  */
-#line 206 "BsParserFX.h"
+#line 207 "BsParserFX.h"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */

+ 12 - 3
BansheeSL/BsParserFX.y

@@ -118,7 +118,7 @@ void yyerror(YYLTYPE *locp, ParseState* parse_state, yyscan_t scanner, const cha
 %token	TOKEN_RENDERER TOKEN_LANGUAGE TOKEN_INCLUDE TOKEN_PASS
 
 	/* Pass keywords */
-%token	TOKEN_VERTEX TOKEN_FRAGMENT TOKEN_GEOMETRY TOKEN_HULL TOKEN_DOMAIN TOKEN_COMPUTE
+%token	TOKEN_VERTEX TOKEN_FRAGMENT TOKEN_GEOMETRY TOKEN_HULL TOKEN_DOMAIN TOKEN_COMPUTE TOKEN_COMMON
 %token	TOKEN_STENCILREF
 
 %token	TOKEN_FILLMODE TOKEN_CULLMODE TOKEN_DEPTHBIAS TOKEN_SDEPTHBIAS
@@ -241,6 +241,7 @@ shader_option
 	: TOKEN_SEPARABLE '=' TOKEN_BOOLEAN ';'		{ $$.type = OT_Separable; $$.value.intValue = $3; }
 	| TOKEN_QUEUE '=' TOKEN_INTEGER ';'			{ $$.type = OT_Queue; $$.value.intValue = $3; }
 	| TOKEN_PRIORITY '=' TOKEN_INTEGER ';'		{ $$.type = OT_Priority; $$.value.intValue = $3; }
+	| TOKEN_INCLUDE '=' TOKEN_STRING ';'		{ $$.type = OT_Include; $$.value.strValue = $3; }
 	;
 
 	/* Technique */
@@ -265,12 +266,14 @@ technique_body
 technique_statement
 	: technique_option
 	| pass				{ $$.type = OT_Pass; $$.value.nodePtr = $1; }
+	| pass_option
+	| code				{ $$.type = OT_Code; $$.value.nodePtr = $1; }
 	;
 
 technique_option
 	: TOKEN_RENDERER '=' TOKEN_STRING ';'	{ $$.type = OT_Renderer; $$.value.strValue = $3; }
 	| TOKEN_LANGUAGE '=' TOKEN_STRING ';'	{ $$.type = OT_Language; $$.value.strValue = $3; }
-	| TOKEN_INCLUDE '=' TOKEN_STRING ';'	{ $$.type = OT_Include; $$.value.strValue = $3; }
+	;
 
 	/* Pass */
 
@@ -297,7 +300,8 @@ pass_statement
 	;
 
 pass_option
-	: TOKEN_FILLMODE '=' TOKEN_FILLMODEVALUE ';'				{ $$.type = OT_FillMode; $$.value.intValue = $3; }
+	: TOKEN_INDEX '=' TOKEN_INTEGER ';'							{ $$.type = OT_Index; $$.value.intValue = $3; }
+	| TOKEN_FILLMODE '=' TOKEN_FILLMODEVALUE ';'				{ $$.type = OT_FillMode; $$.value.intValue = $3; }
 	| TOKEN_CULLMODE '=' TOKEN_CULLMODEVALUE ';'				{ $$.type = OT_CullMode; $$.value.intValue = $3; }
 	| TOKEN_DEPTHBIAS '=' TOKEN_FLOAT ';'						{ $$.type = OT_DepthBias; $$.value.floatValue = $3; }
 	| TOKEN_SDEPTHBIAS '=' TOKEN_FLOAT ';'						{ $$.type = OT_SDepthBias; $$.value.floatValue = $3; }
@@ -368,6 +372,11 @@ code_header
 			$$ = nodeCreate(parse_state->memContext, NT_Code); 
 			nodePush(parse_state, $$);
 		}
+	| TOKEN_COMMON '='
+		{ 
+			$$ = nodeCreate(parse_state->memContext, NT_Code); 
+			nodePush(parse_state, $$);
+		}
 	;
 
 	/* Stencil op */

+ 134 - 15
BansheeSL/Include/BsSLFXCompiler.h

@@ -3,6 +3,9 @@
 #include "BsSLPrerequisites.h"
 #include "BsShader.h"
 #include "BsGpuProgram.h"
+#include "BsRasterizerState.h"
+#include "BsDepthStencilState.h"
+#include "BsBlendState.h"
 
 extern "C" {
 #include "BsASTFX.h"
@@ -15,16 +18,46 @@ namespace BansheeEngine
 	 */
 	class BSLFXCompiler
 	{
+		/**
+		 * @brief	Possible types of code blocks within a shader.
+		 */
+		enum class CodeBlockType
+		{
+			Vertex, Fragment, Geometry, Hull, Domain, Compute, Common
+		};
+
 		/**
 		 * @brief	Represents a block of code written in a GPU program language for
 		 *			a specific GPU program type. (i.e. non-FX code)
 		 */
 		struct CodeBlock
 		{
-			GpuProgramType type;
+			CodeBlockType type;
 			String code;
 		};
 
+		/**
+		 * @brief	Temporary data describing a pass during parsing.
+		 */
+		struct PassData
+		{
+			BLEND_STATE_DESC blendDesc;
+			RASTERIZER_STATE_DESC rasterizerDesc;
+			DEPTH_STENCIL_STATE_DESC depthStencilDesc;
+
+			bool blendIsDefault = true;
+			bool rasterizerIsDefault = true;
+			bool depthStencilIsDefault = true;
+
+			String commonCode;
+			String vertexCode;
+			String fragmentCode;
+			String geometryCode;
+			String hullCode;
+			String domainCode;
+			String computeCode;
+		};
+
 	public:
 		/**
 		 * @brief	Transforms a source file written in BSL FX syntax into a Shader object.
@@ -47,6 +80,79 @@ namespace BansheeEngine
 		 */
 		static Vector<CodeBlock> parseCodeBlocks(String& source);
 
+		/**
+		 * @brief	Merges two shader AST nodes. The first node will contain the 
+		 *			combination of both after the method executes.
+		 *
+		 * @param	mergeInto		Parse state containing the node to be merged into.
+		 *							this node.
+		 * @param	mergeFrom		Second of the nodes to be merged. All the contents of this node
+		 *							will be merged into the first node. This node and its children will
+		 *							remain unmodified.
+		 * @param	codeBlockOffset	Offset to apply to any code block indexes belonging to the second node.
+		 */
+		static void mergeAST(ParseState* mergeInto, ASTFXNode* mergeFrom, UINT32 codeBlockOffset);
+
+		/**
+		 * @brief	Finds the blocks node in the root node of the provided parse state, and merges any entries
+		 *			from "blocksNode" into it. A new node is created if one doesn't exist.
+		 */
+		static void mergeBlocks(ParseState* mergeInto, ASTFXNode* blocksNode);
+
+		/**
+		 * @brief	Finds the parameters node in the root node of the provided parse state, and merges any entries
+		 *			from "paramsNode" into it. A new node is created if one doesn't exist.
+		 */
+		static void mergeParameters(ParseState* mergeInto, ASTFXNode* paramsNode);
+
+		/**
+		 * @brief	Retrieves the renderer and language specified for the technique. These two values are considered
+		 *			a unique identifier for a technique.
+		 */
+		static void getTechniqueIdentifier(ASTFXNode* technique, StringID& renderer, String& language);
+
+		/**
+		 * @brief	Checks if two techniques can be matched based on the options
+		 *			specified in their child nodes.
+		 */
+		static bool isTechniqueMergeValid(ASTFXNode* into, ASTFXNode* from);
+
+		/**
+		 * @brief	Merges pass states and code blocks. All code blocks from "mergeFromNode" 
+		 *			will have their indexes incremented by "codeBlockOffset".
+		 */
+		static void mergePass(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset);
+
+		/**
+		 * @brief	Merges code blocks. All code blocks from "mergeFromNode"
+		 *			will have their indexes incremented by "codeBlockOffset".
+		 */
+		static void mergeCode(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset);
+
+		/**
+		 * @brief	Merges all pass states by copying all child nodes and their options to the destination node. 
+		 *			
+		 * @note	Certain node types are ignored as we handle their merging specially.
+		 *			Should only be called on Technique nodes or its children.
+		 */
+		static void mergePassStates(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode);
+
+		/**
+		 * @brief	Merges two techniques. All technique states, code blocks and passes will be merged.
+		 *			Passes will be merged according to the pass index (new passes will be inserted if the destination
+		 *			doesn't already have a pass with an index belonging to the source pass). 
+		 *			All code blocks from "mergeFromNode" will have their indexes incremented by "codeBlockOffset".
+		 */
+		static void mergeTechnique(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset);
+
+		/**
+		 * @brief	Find matching techniques from the root shader node in "mergeInto" and merges them with "techniqueNode".
+		 *			All code blocks from "mergeFromNode" will have their indexes incremented by "codeBlockOffset".
+		 *
+		 * @see		BSLFXCompiler::mergeTechnique
+		 */
+		static void mergeTechniques(ParseState* mergeInto, ASTFXNode* techniqueNode, UINT32 codeBlockOffset);
+
 		/**
 		 * @brief	Converts FX renderer name into an in-engine renderer identifier.
 		 */
@@ -151,22 +257,22 @@ namespace BansheeEngine
 		static void parseRenderTargetBlendState(BLEND_STATE_DESC& desc, ASTFXNode* targetNode);
 
 		/**
-		 * @brief	Parses the blend state AST node and outputs a blend state object, or a nullptr
-		 *			in case AST node is empty. 
+		 * @brief	Parses the blend state AST node and outputs a blend state descriptor. Returns false
+		 *			if the descriptor wasn't modified.
 		 */
-		static BlendStatePtr parseBlendState(ASTFXNode* passNode);
+		static bool parseBlendState(BLEND_STATE_DESC& desc, ASTFXNode* passNode);
 
 		/**
-		 * @brief	Parses the rasterizer state AST node and outputs a rasterizer state object, or a nullptr
-		 *			in case AST node is empty. 
+		 * @brief	Parses the rasterizer state AST node and outputs a rasterizer state descriptor. Returns false
+		 *			if the descriptor wasn't modified.
 		 */
-		static RasterizerStatePtr parseRasterizerState(ASTFXNode* passNode);
+		static bool parseRasterizerState(RASTERIZER_STATE_DESC& desc, ASTFXNode* passNode);
 
 		/**
-		 * @brief	Parses the depth-stencil state AST node and outputs a depth-stencil state object, or a nullptr
-		 *			in case AST node is empty. 
+		 * @brief	Parses the depth-stencil state AST node and outputs a depth-stencil state descriptor. Returns false
+		 *			if the descriptor wasn't modified.
 		 */
-		static DepthStencilStatePtr parseDepthStencilState(ASTFXNode* passNode);
+		static bool parseDepthStencilState(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* passNode);
 
 		/**
 		 * @brief	Parses the sampler state AST node and outputs a sampler state object, or a nullptr
@@ -174,6 +280,17 @@ namespace BansheeEngine
 		 */
 		static SamplerStatePtr parseSamplerState(ASTFXNode* samplerStateNode);
 
+		/**
+		 * @brief	Parses a code AST node and outputs the result in one of the streams within the
+		 *			provided pass data.
+		 *
+		 * @param	codeNode	AST node to parse
+		 * @param	codeBlocks	GPU program source code retrieved from "parseCodeBlocks".
+		 * @param	passData	Pass data containing temporary pass data, including the code streams
+		 *						that the code block code will be written to.
+		 */
+		static void parseCodeBlock(ASTFXNode* codeNode, const Vector<CodeBlock>& codeBlocks, PassData& passData);
+
 		/**
 		 * @brief	Parses the pass AST node and generates a single pass object. Returns null
 		 *			if no pass can be parsed. This method will generate any child state objects and
@@ -181,12 +298,13 @@ namespace BansheeEngine
 		 *
 		 * @param	passNode		Node to parse.
 		 * @param	codeBlocks		GPU program source code retrieved from "parseCodeBlocks".
-		 * @param	includes		Files paths relative to the current executable that will be injected
-		 *							into GPU program code before compilation.
+		 * @param	passData		Data containing pass render state descriptors.
 		 * @param	renderAPI		API to use for compiling the GPU programs.
 		 * @param	language		GPU program language to use for parsing the provided code blocks.
+		 * @param	seqIdx			Output sequential index of the pass that determines its rendering order.
 		 */
-		static PassPtr parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, const Vector<String>& includes, const StringID& renderAPI, const String& language);
+		static PassPtr parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData, 
+			const StringID& renderAPI, const String& language, UINT32& seqIdx);
 
 		/**
 		 * @brief	Parses the technique AST node and generates a single technique object. Returns null
@@ -213,12 +331,13 @@ namespace BansheeEngine
 		 * @brief	Parses the AST node hierarchy and generates a shader object.
 		 *
 		 * @param	name		Optional name for the shader.
-		 * @param	rootNode	Root node of the AST hierarchy retrieved from "parseFX".
+		 * @param	parseState	Parser state object that has previously been initialized with the
+		 *						AST using "parseFX".
 		 * @param	codeBlocks	GPU program source code retrieved from "parseCodeBlocks".
 		 *
 		 * @return	A generated shader object, or a nullptr if shader was invalid.
 		 */
-		static ShaderPtr parseShader(const String& name, ASTFXNode* rootNode, const Vector<CodeBlock>& codeBlocks);
+		static ShaderPtr parseShader(const String& name, ParseState* parseState, Vector<CodeBlock>& codeBlocks);
 
 		/**
 		 * @brief	Converts a null-terminated string into a standard string, and eliminates quotes that are assumed

+ 3 - 1
BansheeSL/Source/BsASTFX.c

@@ -65,7 +65,9 @@ OptionInfo OPTION_LOOKUP[] =
 	{ OT_Blocks, ODT_Complex },
 	{ OT_Parameter, ODT_Complex },
 	{ OT_Block, ODT_Complex },
-	{ OT_SamplerState, ODT_Complex }
+	{ OT_SamplerState, ODT_Complex },
+	{ OT_Code, ODT_Complex },
+	{ OT_StencilRef, ODT_Int }
 };
 
 NodeOptions* nodeOptionsCreate(void* context)

+ 609 - 127
BansheeSL/Source/BsSLFXCompiler.cpp

@@ -5,11 +5,10 @@
 #include "BsTechnique.h"
 #include "BsPass.h"
 #include "BsSamplerState.h"
-#include "BsRasterizerState.h"
-#include "BsDepthStencilState.h"
-#include "BsBlendState.h"
 #include "BsRenderAPI.h"
 #include "BsDebug.h"
+#include "BsShaderManager.h"
+#include "BsShaderInclude.h"
 
 extern "C" {
 #include "BsMMAlloc.h"
@@ -17,6 +16,8 @@ extern "C" {
 #include "BsLexerFX.h"
 }
 
+using namespace std;
+
 namespace BansheeEngine
 {
 	// Print out the FX AST, only for debug purposes
@@ -72,12 +73,53 @@ namespace BansheeEngine
 		ShaderPtr output;
 		if (parseState->hasError > 0)
 		{
-			LOGERR("Error while parsing a Shader: " + String(parseState->errorMessage) + ". Location: " +
+			LOGERR("Error while parsing shader FX code: " + String(parseState->errorMessage) + ". Location: " +
 				toString(parseState->errorLine) + " (" + toString(parseState->errorColumn) + ")");
 		}
 		else
 		{
-			output = parseShader("Shader", parseState->rootNode, codeBlocks);
+			output = parseShader("Shader", parseState, codeBlocks);
+
+			StringStream gpuProgError;
+			bool hasError = false;
+			if (output != nullptr)
+			{
+				TechniquePtr bestTechnique = output->getBestTechnique();
+
+				if (bestTechnique != nullptr)
+				{
+					UINT32 numPasses = bestTechnique->getNumPasses();
+
+					for (UINT32 i = 0; i < numPasses; i++)
+					{
+						PassPtr pass = bestTechnique->getPass(i);
+
+						auto checkCompileStatus = [&](const String& prefix, const GpuProgramPtr& prog)
+						{
+							if (prog != nullptr)
+							{
+								prog->blockUntilCoreInitialized();
+
+								if (!prog->isCompiled())
+								{
+									hasError = true;
+									gpuProgError << prefix <<": " << prog->getCompileErrorMessage() << std::endl;
+								}
+							}
+						};
+
+						checkCompileStatus("Vertex program", pass->getVertexProgram());
+						checkCompileStatus("Fragment program", pass->getFragmentProgram());
+						checkCompileStatus("Geometry program", pass->getGeometryProgram());
+						checkCompileStatus("Hull program", pass->getHullProgram());
+						checkCompileStatus("Domain program", pass->getDomainProgram());
+						checkCompileStatus("Compute program", pass->getComputeProgram());
+					}
+				}
+			}
+
+			if (hasError)
+				LOGERR("Failed compiling GPU program(s): " + gpuProgError.str());
 
 			// Only enable for debug purposes
 			//SLFXDebugPrint(parseState->rootNode, "");
@@ -107,7 +149,7 @@ namespace BansheeEngine
 
 	Vector<BSLFXCompiler::CodeBlock> BSLFXCompiler::parseCodeBlocks(String& source)
 	{
-		std::regex pattern = std::regex(R"((Vertex|Fragment|Geometry|Hull|Domain|Compute)\s*=\s*\{)");
+		std::regex pattern = std::regex(R"((Vertex|Fragment|Geometry|Hull|Domain|Compute|Common)\s*=\s*\{)");
 		std::smatch matches;
 
 		Vector<CodeBlock> codeBlocks;
@@ -122,17 +164,19 @@ namespace BansheeEngine
 
 			std::string type = matches[1].str();
 			if (type == "Vertex")
-				newBlock.type = GPT_VERTEX_PROGRAM;
+				newBlock.type = CodeBlockType::Vertex;
 			else if (type == "Fragment")
-				newBlock.type = GPT_FRAGMENT_PROGRAM;
+				newBlock.type = CodeBlockType::Fragment;
 			else if (type == "Geometry")
-				newBlock.type = GPT_GEOMETRY_PROGRAM;
+				newBlock.type = CodeBlockType::Geometry;
 			else if (type == "Hull")
-				newBlock.type = GPT_HULL_PROGRAM;
+				newBlock.type = CodeBlockType::Hull;
 			else if (type == "Domain")
-				newBlock.type = GPT_DOMAIN_PROGRAM;
+				newBlock.type = CodeBlockType::Domain;
 			else if (type == "Compute")
-				newBlock.type = GPT_COMPUTE_PROGRAM;
+				newBlock.type = CodeBlockType::Compute;
+			else if (type == "Common")
+				newBlock.type = CodeBlockType::Common;
 
 			offset += (UINT32)matches.position() + (UINT32)matches.length();
 
@@ -170,6 +214,323 @@ namespace BansheeEngine
 		return codeBlocks;
 	}
 
+	void BSLFXCompiler::mergeBlocks(ParseState* mergeInto, ASTFXNode* blocksNode)
+	{
+		if (blocksNode == nullptr || blocksNode->type != NT_Blocks)
+			return;
+
+		ASTFXNode* destBlocksNode = nullptr;
+		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
+		{
+			NodeOption* option = &mergeInto->rootNode->options->entries[i];
+
+			if (option->type == OT_Blocks)
+			{
+				destBlocksNode = option->value.nodePtr;
+				break;
+			}
+		}
+
+		if (destBlocksNode == nullptr)
+		{
+			destBlocksNode = nodeCreate(mergeInto->memContext, NT_Blocks);
+			NodeOption blocksOption; blocksOption.type = OT_Blocks; blocksOption.value.nodePtr = destBlocksNode;
+			nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, &blocksOption);
+		}
+
+		for (int i = 0; i < blocksNode->options->count; i++)
+		{
+			NodeOption* option = &blocksNode->options->entries[i];
+
+			if (option->type == OT_Block)
+			{
+				ASTFXNode* dstBlock = nodeCreate(mergeInto->memContext, NT_Block);
+				NodeOption blockOption; blockOption.type = OT_Block; blockOption.value.nodePtr = dstBlock;
+				nodeOptionsAdd(mergeInto->memContext, destBlocksNode->options, &blockOption);
+
+				ASTFXNode* srcBlock = option->value.nodePtr;
+				for (int j = 0; j < srcBlock->options->count; j++)
+					nodeOptionsAdd(mergeInto->memContext, dstBlock->options, &srcBlock->options->entries[j]);
+			}
+		}
+	}
+
+	void BSLFXCompiler::mergeParameters(ParseState* mergeInto, ASTFXNode* paramsNode)
+	{
+		if (paramsNode == nullptr || paramsNode->type != NT_Parameters)
+			return;
+
+		ASTFXNode* destParamsNode = nullptr;
+		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
+		{
+			NodeOption* option = &mergeInto->rootNode->options->entries[i];
+
+			if (option->type == OT_Parameters)
+			{
+				destParamsNode = option->value.nodePtr;
+				break;
+			}
+		}
+
+		if (destParamsNode == nullptr)
+		{
+			destParamsNode = nodeCreate(mergeInto->memContext, NT_Parameters);
+			NodeOption paramsOption; paramsOption.type = OT_Parameters; paramsOption.value.nodePtr = destParamsNode;
+			nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, &paramsOption);
+		}
+
+		for (int i = 0; i < paramsNode->options->count; i++)
+		{
+			NodeOption* option = &paramsNode->options->entries[i];
+
+			if (option->type == OT_Parameter)
+			{
+				ASTFXNode* dstParam = nodeCreate(mergeInto->memContext, NT_Parameter);
+				NodeOption paramOption; paramOption.type = OT_Parameter; paramOption.value.nodePtr = dstParam;
+				nodeOptionsAdd(mergeInto->memContext, destParamsNode->options, &paramOption);
+
+				ASTFXNode* srcParam = option->value.nodePtr;
+				for (int j = 0; j < srcParam->options->count; j++)
+					nodeOptionsAdd(mergeInto->memContext, dstParam->options, &srcParam->options->entries[j]);
+			}
+		}
+	}
+
+	void BSLFXCompiler::getTechniqueIdentifier(ASTFXNode* technique, StringID& renderer, String& language)
+	{
+		renderer = RendererAny;
+		language = "Any";
+
+		for (int i = 0; i < technique->options->count; i++)
+		{
+			NodeOption* option = &technique->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Renderer:
+				renderer = parseRenderer(removeQuotes(option->value.strValue));
+				break;
+			case OT_Language:
+				language = removeQuotes(option->value.strValue);
+				break;
+			}
+		}
+	}
+
+	bool BSLFXCompiler::isTechniqueMergeValid(ASTFXNode* into, ASTFXNode* from)
+	{
+		StringID intoRenderer = RendererAny;
+		String intoLanguage = "Any";
+
+		StringID fromRenderer = RendererAny;
+		String fromLanguage = "Any";
+
+		getTechniqueIdentifier(into, intoRenderer, intoLanguage);
+		getTechniqueIdentifier(from, fromRenderer, fromLanguage);
+
+		return (intoRenderer == fromRenderer || fromRenderer == RendererAny) && (intoLanguage == fromLanguage || fromLanguage == "Any");
+	}
+
+	void BSLFXCompiler::mergePass(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
+	{
+		if (mergeIntoNode == nullptr || mergeIntoNode->type != NT_Pass || mergeFromNode == nullptr || mergeFromNode->type != NT_Pass)
+			return;
+
+		mergeCode(mergeInto, mergeIntoNode, mergeFromNode, codeBlockOffset);
+		mergePassStates(mergeInto, mergeIntoNode, mergeFromNode);
+	}
+
+	void BSLFXCompiler::mergeCode(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
+	{
+		for (int i = 0; i < mergeFromNode->options->count; i++)
+		{
+			NodeOption* option = &mergeFromNode->options->entries[i];
+
+			if (option->type == OT_Code)
+			{
+				ASTFXNode* srcCodeNode = option->value.nodePtr;
+
+				UINT32 origIndex = 0;
+				for (int j = 0; j < srcCodeNode->options->count; j++)
+				{
+					NodeOption* codeOption = &srcCodeNode->options->entries[j];
+					if (codeOption->type == OT_Index)
+						origIndex = codeOption->value.intValue;
+				}
+
+				ASTFXNode* destCodeNode = nodeCreate(mergeInto->memContext, NT_Code);
+				NodeOption index; index.type = OT_Index; index.value.intValue = codeBlockOffset + origIndex;
+				nodeOptionsAdd(mergeInto->memContext, destCodeNode->options, &index);
+
+				NodeOption code; code.type = OT_Code; code.value.nodePtr = destCodeNode;
+				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &code);
+			}
+		}
+	}
+
+	void BSLFXCompiler::mergePassStates(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode)
+	{
+		for (int i = 0; i < mergeFromNode->options->count; i++)
+		{
+			NodeOption* option = &mergeFromNode->options->entries[i];
+
+			// Skip these as we handle them specially elsewhere
+			if (option->type == OT_Code || option->type == OT_Pass || option->type == OT_Renderer || option->type == OT_Language)
+				continue;
+
+			OptionDataType dataType = OPTION_LOOKUP[(int)option->type].dataType;
+			if (dataType == ODT_Complex)
+			{
+				ASTFXNode* newNode = nullptr;
+				
+				if (option->value.nodePtr != nullptr)
+				{
+					newNode = nodeCreate(mergeInto->memContext, option->value.nodePtr->type);
+					mergePassStates(mergeInto, newNode, option->value.nodePtr);
+				}
+				
+				NodeOption newOption;
+				newOption.type = option->type;
+				newOption.value.nodePtr = newNode;
+
+				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &newOption);
+			}
+			else if (dataType == ODT_String)
+			{
+				NodeOption newOption;
+				newOption.type = option->type;
+				newOption.value.strValue = mmalloc_strdup(mergeInto->memContext, option->value.strValue);
+
+				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &newOption);
+			}
+			else
+			{
+				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, option);
+			}
+		}
+	}
+
+	void BSLFXCompiler::mergeTechnique(ParseState* mergeInto, ASTFXNode* mergeIntoNode, ASTFXNode* mergeFromNode, UINT32 codeBlockOffset)
+	{
+		if (mergeIntoNode == nullptr || mergeIntoNode->type != NT_Technique || mergeFromNode == nullptr || mergeFromNode->type != NT_Technique)
+			return;
+
+		mergeCode(mergeInto, mergeIntoNode, mergeFromNode, codeBlockOffset);
+		mergePassStates(mergeInto, mergeIntoNode, mergeFromNode);
+
+		Map<UINT32, ASTFXNode*, std::greater<UINT32>> fromPasses;
+
+		UINT32 nextPassIdx = 0;
+		for (int i = 0; i < mergeFromNode->options->count; i++)
+		{
+			NodeOption* option = &mergeFromNode->options->entries[i];
+
+			if (option->type == OT_Pass)
+			{
+				ASTFXNode* passNode = option->value.nodePtr;
+				UINT32 passIdx = nextPassIdx;
+				for (int j = 0; j < passNode->options->count; j++)
+				{
+					NodeOption* passOption = &passNode->options->entries[i];
+
+					if (passOption->type == OT_Index)
+						passIdx = (UINT32)passOption->value.intValue;
+				}
+
+				fromPasses[passIdx] = passNode;
+				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
+			}
+		}
+
+		Map<UINT32, ASTFXNode*, std::greater<UINT32>> intoPasses;
+
+		nextPassIdx = 0;
+		for (int i = 0; i < mergeIntoNode->options->count; i++)
+		{
+			NodeOption* option = &mergeIntoNode->options->entries[i];
+
+			if (option->type == OT_Pass)
+			{
+				ASTFXNode* passNode = option->value.nodePtr;
+				UINT32 passIdx = nextPassIdx;
+				for (int j = 0; j < passNode->options->count; j++)
+				{
+					NodeOption* passOption = &passNode->options->entries[i];
+
+					if (passOption->type == OT_Index)
+						passIdx = (UINT32)passOption->value.intValue;
+				}
+
+				intoPasses[passIdx] = passNode;
+				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
+			}
+		}
+
+		for (auto& fromPair : fromPasses)
+		{
+			auto iterFind = intoPasses.find(fromPair.first);
+			if (iterFind != intoPasses.end())
+			{
+				mergePass(mergeInto, iterFind->second, fromPair.second, codeBlockOffset);
+			}
+			else
+			{
+				ASTFXNode* passNode = nodeCreate(mergeInto->memContext, NT_Pass);
+				NodeOption passOption; passOption.type = OT_Pass; passOption.value.nodePtr = passNode;
+				nodeOptionsAdd(mergeInto->memContext, mergeIntoNode->options, &passOption);
+
+				mergePass(mergeInto, passNode, fromPair.second, codeBlockOffset);
+			}
+		}
+	}
+
+	void BSLFXCompiler::mergeTechniques(ParseState* mergeInto, ASTFXNode* techniqueNode, UINT32 codeBlockOffset)
+	{
+		if (techniqueNode == nullptr || techniqueNode->type != NT_Technique)
+			return;
+
+		for (int i = 0; i < mergeInto->rootNode->options->count; i++)
+		{
+			NodeOption* option = &mergeInto->rootNode->options->entries[i];
+
+			if (option->type == OT_Technique)
+			{
+				if (isTechniqueMergeValid(option->value.nodePtr, techniqueNode))
+					mergeTechnique(mergeInto, option->value.nodePtr, techniqueNode, codeBlockOffset);
+				break;
+			}
+		}
+	}
+
+	void BSLFXCompiler::mergeAST(ParseState* mergeInto, ASTFXNode* mergeFrom, UINT32 codeBlockOffset)
+	{
+		if (mergeInto->rootNode->type != NT_Shader || mergeFrom == nullptr || mergeFrom->type != NT_Shader)
+			return;
+
+		for (int i = 0; i < mergeFrom->options->count; i++)
+		{
+			NodeOption* option = &mergeFrom->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Separable:
+			case OT_Queue:
+			case OT_Priority:
+				nodeOptionsAdd(mergeInto->memContext, mergeInto->rootNode->options, option);
+				break;
+			case OT_Technique:
+				mergeTechniques(mergeInto, option->value.nodePtr, codeBlockOffset);
+				break;
+			case OT_Parameters:
+				mergeParameters(mergeInto, option->value.nodePtr);
+				break;
+			case OT_Blocks:
+				mergeBlocks(mergeInto, option->value.nodePtr);
+				break;
+			}
+		}
+	}
+
 	StringID BSLFXCompiler::parseRenderer(const String& name)
 	{
 		if (name == "Any")
@@ -190,7 +551,7 @@ namespace BansheeEngine
 		else if (name == "HLSL9")
 		{
 			renderAPI = RenderAPIDX9;
-			language = "hlsl";
+			language = "hlsl9";
 		}
 		else if (name == "GLSL")
 		{
@@ -599,12 +960,11 @@ namespace BansheeEngine
 		}
 	}
 
-	BlendStatePtr BSLFXCompiler::parseBlendState(ASTFXNode* passNode)
+	bool BSLFXCompiler::parseBlendState(BLEND_STATE_DESC& desc, ASTFXNode* passNode)
 	{
-		if (passNode == nullptr || passNode->type != NT_Pass)
-			return nullptr;
+		if (passNode == nullptr || (passNode->type != NT_Pass && passNode->type != NT_Technique))
+			return false;
 
-		BLEND_STATE_DESC desc;
 		bool default = true;
 
 		for (int i = 0; i < passNode->options->count; i++)
@@ -628,18 +988,14 @@ namespace BansheeEngine
 			}
 		}
 
-		if (default)
-			return nullptr;
-
-		return BlendState::create(desc);
+		return !default;
 	}
 
-	RasterizerStatePtr BSLFXCompiler::parseRasterizerState(ASTFXNode* passNode)
+	bool BSLFXCompiler::parseRasterizerState(RASTERIZER_STATE_DESC& desc, ASTFXNode* passNode)
 	{
-		if (passNode == nullptr || passNode->type != NT_Pass)
-			return nullptr;
+		if (passNode == nullptr || (passNode->type != NT_Pass && passNode->type != NT_Technique))
+			return false;
 
-		RASTERIZER_STATE_DESC desc;
 		bool default = true;
 
 		for (int i = 0; i < passNode->options->count; i++)
@@ -683,18 +1039,14 @@ namespace BansheeEngine
 			}
 		}
 
-		if (default)
-			return nullptr;
-
-		return RasterizerState::create(desc);
+		return !default;
 	}
 
-	DepthStencilStatePtr BSLFXCompiler::parseDepthStencilState(ASTFXNode* passNode)
+	bool BSLFXCompiler::parseDepthStencilState(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* passNode)
 	{
-		if (passNode == nullptr || passNode->type != NT_Pass)
-			return nullptr;
+		if (passNode == nullptr || (passNode->type != NT_Pass && passNode->type != NT_Technique))
+			return false;
 
-		DEPTH_STENCIL_STATE_DESC desc;
 		bool default = true;
 
 		for (int i = 0; i < passNode->options->count; i++)
@@ -738,10 +1090,7 @@ namespace BansheeEngine
 			}
 		}
 
-		if (default)
-			return nullptr;
-
-		return DepthStencilState::create(desc);
+		return !default;
 	}
 
 	SamplerStatePtr BSLFXCompiler::parseSamplerState(ASTFXNode* samplerStateNode)
@@ -808,16 +1157,68 @@ namespace BansheeEngine
 		return SamplerState::create(desc);
 	}
 
-	PassPtr BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, const Vector<String>& includes, const StringID& renderAPI, const String& language)
+	void BSLFXCompiler::parseCodeBlock(ASTFXNode* codeNode, const Vector<CodeBlock>& codeBlocks, PassData& passData)
+	{
+		if (codeNode == nullptr || codeNode->type != NT_Code)
+			return;
+
+		UINT32 index = (UINT32)-1;
+		for (int j = 0; j < codeNode->options->count; j++)
+		{
+			if (codeNode->options->entries[j].type == OT_Index)
+				index = codeNode->options->entries[j].value.intValue;
+		}
+
+		if (index != (UINT32)-1 && index < (UINT32)codeBlocks.size())
+		{
+			const CodeBlock& codeBlock = codeBlocks[index];
+			switch (codeBlock.type)
+			{
+			case CodeBlockType::Vertex:
+				passData.vertexCode += codeBlock.code;
+				break;
+			case CodeBlockType::Fragment:
+				passData.fragmentCode += codeBlock.code;
+				break;
+			case CodeBlockType::Geometry:
+				passData.geometryCode += codeBlock.code;
+				break;
+			case CodeBlockType::Hull:
+				passData.hullCode += codeBlock.code;
+				break;
+			case CodeBlockType::Domain:
+				passData.domainCode += codeBlock.code;
+				break;
+			case CodeBlockType::Compute:
+				passData.computeCode += codeBlock.code;
+				break;
+			case CodeBlockType::Common:
+				passData.commonCode += codeBlock.code;
+				break;
+			}
+		}
+	}
+
+	PassPtr BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<CodeBlock>& codeBlocks, PassData& passData, 
+		const StringID& renderAPI, const String& language, UINT32& seqIdx)
 	{
 		if (passNode == nullptr || passNode->type != NT_Pass)
 			return nullptr;
 
 		PASS_DESC passDesc;
 
-		passDesc.blendState = parseBlendState(passNode);
-		passDesc.rasterizerState = parseRasterizerState(passNode);
-		passDesc.depthStencilState = parseDepthStencilState(passNode);
+		passData.blendIsDefault &= !parseBlendState(passData.blendDesc, passNode);
+		passData.rasterizerIsDefault &= !parseRasterizerState(passData.rasterizerDesc, passNode);
+		passData.depthStencilIsDefault &= !parseDepthStencilState(passData.depthStencilDesc, passNode);
+
+		if (!passData.blendIsDefault)
+			passDesc.blendState = BlendState::create(passData.blendDesc);
+
+		if (!passData.rasterizerIsDefault)
+			passDesc.rasterizerState = RasterizerState::create(passData.rasterizerDesc);
+
+		if (!passData.depthStencilIsDefault)
+			passDesc.depthStencilState = DepthStencilState::create(passData.depthStencilDesc);
 
 		for (int i = 0; i < passNode->options->count; i++)
 		{
@@ -825,59 +1226,54 @@ namespace BansheeEngine
 
 			switch (option->type)
 			{
+			case OT_Index:
+				seqIdx = option->value.intValue;
+				break;
 			case OT_StencilRef:
 				passDesc.stencilRefValue = option->value.intValue;
 				break;
 			case OT_Code:
-			{
-				ASTFXNode* codeNode = option->value.nodePtr;
-
-				if (codeNode != nullptr && codeNode->type == NT_Code)
-				{
-					UINT32 index = (UINT32)-1;
-					for (int j = 0; j < codeNode->options->count; j++)
-					{
-						if (codeNode->options->entries[j].type == OT_Index)
-							index = codeNode->options->entries[j].value.intValue;
-					}
-
-					if (index != (UINT32)-1 && index < (UINT32)codeBlocks.size())
-					{
-						const CodeBlock& codeBlock = codeBlocks[index];
-						switch (codeBlock.type)
-						{
-						case GPT_VERTEX_PROGRAM:
-							passDesc.vertexProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						case GPT_FRAGMENT_PROGRAM:
-							passDesc.fragmentProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						case GPT_GEOMETRY_PROGRAM:
-							passDesc.geometryProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						case GPT_HULL_PROGRAM:
-							passDesc.hullProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						case GPT_DOMAIN_PROGRAM:
-							passDesc.domainProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						case GPT_COMPUTE_PROGRAM:
-							passDesc.computeProgram = GpuProgram::create(codeBlock.code, "main", language,
-								codeBlock.type, getProfile(renderAPI, codeBlock.type));
-							break;
-						}
-					}
-				}
-			}
+				parseCodeBlock(option->value.nodePtr, codeBlocks, passData);
 				break;
 			}
 		}
 
+		if (!passData.vertexCode.empty())
+		{
+			passDesc.vertexProgram = GpuProgram::create(passData.commonCode + passData.vertexCode, "main", language,
+				GPT_VERTEX_PROGRAM, getProfile(renderAPI, GPT_VERTEX_PROGRAM));
+		}
+
+		if (!passData.fragmentCode.empty())
+		{
+			passDesc.fragmentProgram = GpuProgram::create(passData.commonCode + passData.fragmentCode, "main", language,
+				GPT_FRAGMENT_PROGRAM, getProfile(renderAPI, GPT_FRAGMENT_PROGRAM));
+		}
+
+		if (!passData.geometryCode.empty())
+		{
+			passDesc.geometryProgram = GpuProgram::create(passData.commonCode + passData.geometryCode, "main", language,
+				GPT_GEOMETRY_PROGRAM, getProfile(renderAPI, GPT_GEOMETRY_PROGRAM));
+		}
+
+		if (!passData.hullCode.empty())
+		{
+			passDesc.hullProgram = GpuProgram::create(passData.commonCode + passData.hullCode, "main", language,
+				GPT_HULL_PROGRAM, getProfile(renderAPI, GPT_HULL_PROGRAM));
+		}
+
+		if (!passData.domainCode.empty())
+		{
+			passDesc.domainProgram = GpuProgram::create(passData.commonCode + passData.domainCode, "main", language,
+				GPT_DOMAIN_PROGRAM, getProfile(renderAPI, GPT_DOMAIN_PROGRAM));
+		}
+
+		if (!passData.computeCode.empty())
+		{
+			passDesc.computeProgram = GpuProgram::create(passData.commonCode + passData.computeCode, "main", language,
+				GPT_COMPUTE_PROGRAM, getProfile(renderAPI, GPT_COMPUTE_PROGRAM));
+		}
+
 		return Pass::create(passDesc);
 	}
 
@@ -890,8 +1286,8 @@ namespace BansheeEngine
 		StringID renderer = RendererAny;
 		StringID renderAPI = RenderAPIAny;
 		String language;
-		Vector<String> includes; // TODO - Need to figure out what to do with these
-
+		
+		PassData commonPassData;
 		for (int i = 0; i < techniqueNode->options->count; i++)
 		{
 			NodeOption* option = &techniqueNode->options->entries[i];
@@ -901,31 +1297,43 @@ namespace BansheeEngine
 			case OT_Pass:
 				passNodes.push_back(option->value.nodePtr);
 				break;
-			case OT_Include:
-			{
-				String include = removeQuotes(option->value.strValue);
-				includes.push_back(include);
-			}
-				break;
 			case OT_Renderer:
 				renderer = parseRenderer(removeQuotes(option->value.strValue));
 				break;
 			case OT_Language:
 				parseLanguage(removeQuotes(option->value.strValue), renderAPI, language);
 				break;
+			case OT_Code:
+				parseCodeBlock(option->value.nodePtr, codeBlocks, commonPassData);
+				break;
 			}
 		}
 
-		Vector<PassPtr> passes;
+		commonPassData.blendIsDefault = !parseBlendState(commonPassData.blendDesc, techniqueNode);
+		commonPassData.rasterizerIsDefault = !parseRasterizerState(commonPassData.rasterizerDesc, techniqueNode);
+		commonPassData.depthStencilIsDefault = !parseDepthStencilState(commonPassData.depthStencilDesc, techniqueNode);
+
+		UINT32 nextPassIdx = 0;
+		Map<UINT32, PassPtr, std::greater<UINT32>> passes;
 		for (auto& passNode : passNodes)
 		{
-			PassPtr pass = parsePass(passNode, codeBlocks, includes, renderAPI, language);
+			PassData passDataCopy = commonPassData;
+
+			UINT32 passIdx = nextPassIdx;
+			PassPtr pass = parsePass(passNode, codeBlocks, passDataCopy, renderAPI, language, passIdx);
+
 			if (pass != nullptr)
-				passes.push_back(pass);
+				passes[passIdx] = pass;
+
+			nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
 		}
 
-		if (passes.size() > 0)
-			return Technique::create(renderAPI, renderer, passes);
+		Vector<PassPtr> orderedPasses;
+		for (auto& KVP : passes)
+			orderedPasses.push_back(KVP.second);
+
+		if (orderedPasses.size() > 0)
+			return Technique::create(renderAPI, renderer, orderedPasses);
 
 		return nullptr;
 	}
@@ -1045,43 +1453,117 @@ namespace BansheeEngine
 		}
 	}
 
-	ShaderPtr BSLFXCompiler::parseShader(const String& name, ASTFXNode* rootNode, const Vector<CodeBlock>& codeBlocks)
+	ShaderPtr BSLFXCompiler::parseShader(const String& name, ParseState* parseState, Vector<CodeBlock>& codeBlocks)
 	{
-		SHADER_DESC shaderDesc;
-		Vector<TechniquePtr> techniques;
+		ASTFXNode* rootNode = parseState->rootNode;
+		if (rootNode == nullptr || rootNode->type != NT_Shader)
+			return nullptr;
+
+		Vector<String> includeDependencies;
 
-		if (rootNode->type == NT_Shader)
+		// Merge all include ASTs
+		std::function<void(ASTFXNode*, Vector<CodeBlock>&)> parseIncludes = [&](ASTFXNode* node, Vector<CodeBlock>& parentCodeBlocks)
 		{
-			for (int i = 0; i < rootNode->options->count; i++)
+			Vector<tuple<ParseState*, Vector<CodeBlock>>> toMerge;
+
+			for (int i = 0; i < node->options->count; i++)
 			{
-				NodeOption* option = &rootNode->options->entries[i];
+				NodeOption* option = &node->options->entries[i];
 
-				switch (option->type)
-				{
-				case OT_Separable:
-					shaderDesc.separablePasses = option->value.intValue > 1;
-					break;
-				case OT_Queue:
-					shaderDesc.queuePriority = option->value.intValue;
-					break;
-				case OT_Priority:
-					shaderDesc.queuePriority = option->value.intValue;
-					break;
-				case OT_Technique:
+				if (option->type == OT_Include)
 				{
-					TechniquePtr technique = parseTechnique(option->value.nodePtr, codeBlocks);
-					if (technique != nullptr)
-						techniques.push_back(technique);
+					String includePath = removeQuotes(option->value.strValue);
+					includeDependencies.push_back(includePath);
 
-					break;
+					HShaderInclude include = ShaderManager::instance().findInclude(includePath);
+
+					if (include != nullptr)
+					{
+						include.blockUntilLoaded();
+
+						String includeSource = include->getString();
+
+						ParseState* includeParseState = parseStateCreate();
+						Vector<CodeBlock> includeCodeBlocks = parseCodeBlocks(includeSource);
+						parseFX(parseState, includeSource.c_str());
+
+						ShaderPtr output;
+						if (parseState->hasError > 0)
+						{
+							LOGERR("Error while parsing shader FX code: " + String(parseState->errorMessage) + ". Location: " +
+								toString(parseState->errorLine) + " (" + toString(parseState->errorColumn) + ")");
+						}
+						else
+						{
+							parseIncludes(includeParseState->rootNode, includeCodeBlocks);
+
+							toMerge.push_back(make_tuple(includeParseState, includeCodeBlocks));
+						}
+
+						parseStateDelete(parseState);
+					}
+					else
+					{
+						LOGERR("Cannot find shader include file: " + includePath);
+					}
 				}
-				case OT_Parameters:
-					parseParameters(shaderDesc, option->value.nodePtr);
-					break;
-				case OT_Blocks:
-					parseBlocks(shaderDesc, option->value.nodePtr);
-					break;
+			}
+
+			UINT32 size = (UINT32)toMerge.size();
+			if (size > 0)
+			{
+				Vector<CodeBlock>& outputCodeBlocks = get<1>(toMerge[0]);
+				for (UINT32 i = 1; i < size; i++)
+				{
+					mergeAST(get<0>(toMerge[0]), get<0>(toMerge[i])->rootNode, (UINT32)outputCodeBlocks.size());
+
+					const Vector<CodeBlock>& curCodeBlocks = get<1>(toMerge[i]);
+					for (auto& codeBlock : curCodeBlocks)
+						outputCodeBlocks.push_back(codeBlock);
 				}
+
+				mergeAST(get<0>(toMerge[0]), parseState->rootNode, (UINT32)outputCodeBlocks.size());
+				for (auto& codeBlock : parentCodeBlocks)
+					outputCodeBlocks.push_back(codeBlock);
+
+				parentCodeBlocks = outputCodeBlocks;
+			}
+		};
+
+		parseIncludes(parseState->rootNode, codeBlocks);
+		
+		SHADER_DESC shaderDesc;
+		Vector<TechniquePtr> techniques;
+
+		for (int i = 0; i < rootNode->options->count; i++)
+		{
+			NodeOption* option = &rootNode->options->entries[i];
+
+			switch (option->type)
+			{
+			case OT_Separable:
+				shaderDesc.separablePasses = option->value.intValue > 1;
+				break;
+			case OT_Queue:
+				shaderDesc.queuePriority = option->value.intValue;
+				break;
+			case OT_Priority:
+				shaderDesc.queuePriority = option->value.intValue;
+				break;
+			case OT_Technique:
+			{
+				TechniquePtr technique = parseTechnique(option->value.nodePtr, codeBlocks);
+				if (technique != nullptr)
+					techniques.push_back(technique);
+
+				break;
+			}
+			case OT_Parameters:
+				parseParameters(shaderDesc, option->value.nodePtr);
+				break;
+			case OT_Blocks:
+				parseBlocks(shaderDesc, option->value.nodePtr);
+				break;
 			}
 		}
 

+ 0 - 59
MBansheeEngine/GpuProgram.cs

@@ -1,59 +0,0 @@
-namespace BansheeEngine
-{
-    public sealed class GpuProgram : Resource
-    {
-        // TODO - Dummy class
-    }
-
-    // Note: Must be equal to C++ enum GpuLanguage
-    public enum GpuLanguage
-    {
-        HLSL,
-        GLSL,
-        Undefined
-    }
-
-    // Note: Must be equal to C++ enum GpuProgramType
-    public enum GpuProgramType
-    {
-        Vertex,
-        Fragment,
-        Geometry,
-        Domain,
-        Hull,
-        Compute
-    };
-
-    // Note: Must be equal to C++ enum GpuProgramProfile
-    public enum GpuProgramProfile
-    {
-        None,
-        Fragment_1_1,
-        Fragment_1_2,
-        Fragment_1_3,
-        Fragment_1_4,
-        Fragment_2_0,
-        Fragment_2_x,
-        Fragment_2_a,
-        Fragment_2_b,
-        Fragment_3_0,
-        Fragment_3_x,
-        Fragment_4_0,
-        Fragment_4_1,
-        Fragment_5_0,
-        Vertex_1_1,
-        Vertex_2_0,
-        Vertex_2_x,
-        Vertex_2_a,
-        Vertex_3_0,
-        Vertex_4_0,
-        Vertex_4_1,
-        Vertex_5_0,
-        Geometry_4_0,
-        Geometry_4_1,
-        Geometry_5_0,
-        Hull_5_0,
-        Domain_5_0,
-        Compute_5_0
-    };
-}

+ 0 - 1
MBansheeEngine/MBansheeEngine.csproj

@@ -57,7 +57,6 @@
     <Compile Include="DontSerializeField.cs" />
     <Compile Include="Font.cs" />
     <Compile Include="GameObject.cs" />
-    <Compile Include="GpuProgram.cs" />
     <Compile Include="GUI\GUIArea.cs" />
     <Compile Include="GUI\GUILayoutUtility.cs" />
     <Compile Include="GUI\GUIPanel.cs" />

+ 24 - 67
TODO.txt

@@ -94,76 +94,32 @@ Later:
  - Raycast snapping Ribek suggested
 
 ----------------------------------------------------------------------
-C# Material
+Include files:
 
-C# material interface:
- - Pass
- - GpuProgram
- - DepthStencilState
- - RasterizerState
- - BlendState
- - SamplerState
- - Technique
- - Shader
- - Material
+Include merging:
+ - Finish up merging: Missing recursive parsing and loading of includes + code block array merging
+ - ShaderManager
+   - Do not use ResourceListener for shader updates as it cannot handle missing includes (i.e. if include gets imported after its shader)
+   - Specify includes by asset path
+     - By default they're searched relative to the working directory
+     - Higher layers can override the path resolver to ShaderManager
+       - e.g. EditorApplication can resolve paths that exist in Project Library (i.e. map from source asset -> actual asset)
+   - ShaderManager needs to be notified when an include is deleted/moved/renamed
+     - Job for project libary. Outside of editor do nothing. i.e. add method ShaderManager::recompileDependencies(include)
+	   - Recompile should just rebuild the entire Shader (not calling GpuProgram::recompile or similar)
 
---------
+Once include works modify existing shaders so they use it.
 
-Include files:
- - Remove references to HGpuProgInclude when creating a GpuProgram
-  - Keep using HGpuProgInclude since I need a special resource for includes (I don't want the importer to think 
-     they're Shaders, plus I can avoid making a special case for them in the parser)
- - Instead try to find and load included shader files in BansheeSL by calling Resources
-  - If resource cannot be found ignore it, but still register it as a dependency
- - All includes should be registered in ShaderManager
-  - Whenever a shader is loaded this should be notified so it may recompile all shaders
- - GpuProgram::recompile(newSource)
-   - TODO - Need info
-
-ShaderManager will also be needed when I'm changing shader defines
- - TODO - Test shaders and ensure #defines work
-  - DX11 might need to call D3DPreprocess and include a list of defines
- - GLSL - Need to manually specify #defines as separate lines
-
-ShaderManager will also be needed when switching render APIs during runtime (if that will be possible)
- - It can also serve as a place where we perform shader caching
-
- - Separate languages for DX9 and DX11
- - If I import a shader that tries to compile for a language different than we have available, create a null GpuProgram
-
-0. Move all shaders to new .shader format, ensure everything works
-0.1. Check in new shaders as dependencies
-
-1. Make render states and GPU program non-resources. Ensure they aren't referenced by handles anymore
-1.1. Get rid of GpuProgram importer and modify how BuiltinResources imports shaders
-2. Make render states and GPU programs be saved with the shader
-
-3. Rename HGpuInc to HShaderInc and make its file format be ".bslinc". Remove includes from GpuProgram creation (GpuProgram has mergeWithIncludes method that needs removing as well)
-4. Add ShaderManager that allows GPU programs to register themselves and to be recompiled once their includes change
-  - TODO - Perhaps use ResourceListener instead?
-5. Add support for includes in BansheeSL (injecting their code into actual shader source) + registering with shader manager
-5.1. Modify existing shaders as I have some that share code but differ in state options only
-
-6. Add support for registering default parameters (add a set of addParameter methods to SHADER_DESC). Keep sampler states and textures in a dynamic array, and all other data in a dynamic byte array.
-7. Make sure initializing the material properly sets up default parameters
-
--------------------
-
-TODO STAGE 2:
- - Handle include files (include files should just be plain text files with no additional parsing)
-  - When includes that shaders depend on are imported/changes I need to recompile those shaders
-    - Whenever a GPU program is created it registers itself with a manager and sets up the includes it relies on
-	- Whenever .shader import is called, it scans through all includes and finds out if any shaders depend on them
-	  - If so, it performs a recompile on those shaders
-  - Currently I handle includes on a pretty low level. Maybe I should handle them in BansheeSL instead? (Directly injecting the include code into the source code)
-  - How to handle parsing of include .shader files? They don't need techniques or passes.
- - Handle default values (ignore default values for shared param blocks for now)
-   - additional argument in Shader::addParameter, copy values to buffers when initializing Material
- - Make sure shader saving works and optionally make GpuProgram a non-resource
-  - Make sure GPU programs and states are saved with the Shader
-
-TODO STAGE 3:
- - Move all builtin shaders to the new .shader file model
+Test:
+ - If errors are reported properly from FX compiler and GPU program compilers
+ - Try preprocessing using one RenderAPi and then load the shaders using another and see if they're created normally
+
+----
+
+Add support for registering default parameters (add a set of addParameter methods to SHADER_DESC). Keep sampler states and textures in a dynamic array, and all other data in a dynamic byte array.
+ - Make sure initializing the material properly sets up default parameters
+
+----
 
 TODO LATER:
  - Add GpuProgram caching (e.g. save compiled programs somewhere for later use)
@@ -171,6 +127,7 @@ TODO LATER:
  - Parameter definitions don't support arrays (Leave this for a later iteration)
  - Parameter definitions don't support structs
  - Better error reporting instead of just "syntax error" that better informs the user what the error is
+ - Ensure #defines work properly
 
 ----------------------------------------------------------------------
 Scene View

Some files were not shown because too many files changed in this diff