Browse Source

First commit in a new repository.

Paul 9 years ago
commit
726818a0fc
100 changed files with 15152 additions and 0 deletions
  1. 40 0
      Praxis3D.sln
  2. 279 0
      Praxis3D/Data/Maps/default - Copy.pmap
  3. 616 0
      Praxis3D/Data/Maps/default.pmap
  4. 295 0
      Praxis3D/Data/Maps/default2.pmap
  5. 1062 0
      Praxis3D/Data/Maps/default_lite - Copy (2).pmap
  6. 162 0
      Praxis3D/Data/Maps/default_lite - Copy.pmap
  7. 905 0
      Praxis3D/Data/Maps/default_lite.pmap
  8. 287 0
      Praxis3D/Data/Maps/test.pmap
  9. 539 0
      Praxis3D/Data/Shaders/clouds.frag
  10. 48 0
      Praxis3D/Data/Shaders/clouds.vert
  11. 20 0
      Praxis3D/Data/Shaders/finalPass.frag
  12. 10 0
      Praxis3D/Data/Shaders/finalPass.vert
  13. 24 0
      Praxis3D/Data/Shaders/geomBillboard.frag
  14. 72 0
      Praxis3D/Data/Shaders/geomBillboard.geom
  15. 8 0
      Praxis3D/Data/Shaders/geomBillboard.vert
  16. 840 0
      Praxis3D/Data/Shaders/geometryPass.frag
  17. 36 0
      Praxis3D/Data/Shaders/geometryPass.vert
  18. 381 0
      Praxis3D/Data/Shaders/geometryPassInf.frag
  19. 41 0
      Praxis3D/Data/Shaders/geometryPassInf.vert
  20. 57 0
      Praxis3D/Data/Shaders/geometryPassTest.frag
  21. 35 0
      Praxis3D/Data/Shaders/geometryPassTest.vert
  22. 147 0
      Praxis3D/Data/Shaders/lightPass 2.frag
  23. 597 0
      Praxis3D/Data/Shaders/lightPass.frag
  24. 10 0
      Praxis3D/Data/Shaders/lightPass.vert
  25. 13 0
      Praxis3D/Data/Shaders/skybox.frag
  26. 15 0
      Praxis3D/Data/Shaders/skybox.vert
  27. 45 0
      Praxis3D/Data/Shaders/terrain.frag
  28. 91 0
      Praxis3D/Data/Shaders/terrain.tesc
  29. 24 0
      Praxis3D/Data/Shaders/terrain.tese
  30. 45 0
      Praxis3D/Data/Shaders/terrain.vert
  31. 77 0
      Praxis3D/Data/Shaders/terrain2.frag
  32. 35 0
      Praxis3D/Data/Shaders/terrain2.geom
  33. 31 0
      Praxis3D/Data/Shaders/terrain2.tesc
  34. 29 0
      Praxis3D/Data/Shaders/terrain2.tese
  35. 34 0
      Praxis3D/Data/Shaders/terrain2.vert
  36. 169 0
      Praxis3D/Data/Shaders/terrain3.frag
  37. 108 0
      Praxis3D/Data/Shaders/terrain3.geom
  38. 163 0
      Praxis3D/Data/Shaders/terrain3.tesc
  39. 98 0
      Praxis3D/Data/Shaders/terrain3.tese
  40. 53 0
      Praxis3D/Data/Shaders/terrain3.vert
  41. 59 0
      Praxis3D/Data/Shaders/terrain4.frag
  42. 35 0
      Praxis3D/Data/Shaders/terrain4.geom
  43. 136 0
      Praxis3D/Data/Shaders/terrain4.tesc
  44. 216 0
      Praxis3D/Data/Shaders/terrain4.tese
  45. 35 0
      Praxis3D/Data/Shaders/terrain4.vert
  46. 19 0
      Praxis3D/Data/Shaders/test.frag
  47. 40 0
      Praxis3D/Data/Shaders/test.vert
  48. 232 0
      Praxis3D/Data/Shaders/test2.frag
  49. 55 0
      Praxis3D/Data/Shaders/test2.vert
  50. 32 0
      Praxis3D/Data/Shaders/test3.frag
  51. 52 0
      Praxis3D/Data/Shaders/test3.vert
  52. 444 0
      Praxis3D/Data/Shaders/water.frag
  53. 55 0
      Praxis3D/Data/Shaders/water.vert
  54. 19 0
      Praxis3D/Data/config.ini
  55. 59 0
      Praxis3D/Data/error-strings-eng.data
  56. BIN
      Praxis3D/Debug/Praxis3D.tlog/CL.command.1.tlog
  57. BIN
      Praxis3D/Debug/Praxis3D.tlog/CL.read.1.tlog
  58. BIN
      Praxis3D/Debug/Praxis3D.tlog/CL.write.1.tlog
  59. 2 0
      Praxis3D/Debug/Praxis3D.tlog/Praxis3D.lastbuildstate
  60. BIN
      Praxis3D/Debug/Praxis3D.tlog/link.command.1.tlog
  61. BIN
      Praxis3D/Debug/Praxis3D.tlog/link.read.1.tlog
  62. BIN
      Praxis3D/Debug/Praxis3D.tlog/link.write.1.tlog
  63. BIN
      Praxis3D/Debug/engine.obj.enc
  64. BIN
      Praxis3D/Debug/shaderloader.obj.enc
  65. BIN
      Praxis3D/Debug/vc140.idb
  66. BIN
      Praxis3D/Praxis3D.rc
  67. 415 0
      Praxis3D/Praxis3D.vcxproj
  68. 488 0
      Praxis3D/Praxis3D.vcxproj.filters
  69. BIN
      Praxis3D/Release/Praxis3D.tlog/CL.command.1.tlog
  70. BIN
      Praxis3D/Release/Praxis3D.tlog/CL.read.1.tlog
  71. BIN
      Praxis3D/Release/Praxis3D.tlog/CL.write.1.tlog
  72. 2 0
      Praxis3D/Release/Praxis3D.tlog/Praxis3D.lastbuildstate
  73. BIN
      Praxis3D/Release/Praxis3D.tlog/link.command.1.tlog
  74. BIN
      Praxis3D/Release/Praxis3D.tlog/link.read.1.tlog
  75. BIN
      Praxis3D/Release/Praxis3D.tlog/link.write.1.tlog
  76. 127 0
      Praxis3D/Source/BaseGraphicsObjects.h
  77. 23 0
      Praxis3D/Source/BaseScriptObject.h
  78. 62 0
      Praxis3D/Source/CameraGraphicsObject.h
  79. 226 0
      Praxis3D/Source/CameraScript.h
  80. 508 0
      Praxis3D/Source/ChangeController.cpp
  81. 110 0
      Praxis3D/Source/ChangeController.h
  82. 208 0
      Praxis3D/Source/Clock.h
  83. 4 0
      Praxis3D/Source/ClockLocator.cpp
  84. 23 0
      Praxis3D/Source/ClockLocator.h
  85. 398 0
      Praxis3D/Source/Config.cpp
  86. 1120 0
      Praxis3D/Source/Config.h
  87. 277 0
      Praxis3D/Source/ConfigLoader.cpp
  88. 762 0
      Praxis3D/Source/ConfigLoader.h
  89. 84 0
      Praxis3D/Source/DebugMoveScript.h
  90. 152 0
      Praxis3D/Source/DebugUIScript.h
  91. 422 0
      Praxis3D/Source/DeferredRenderer.cpp
  92. 134 0
      Praxis3D/Source/DeferredRenderer.h
  93. 238 0
      Praxis3D/Source/Engine.cpp
  94. 56 0
      Praxis3D/Source/Engine.h
  95. 7 0
      Praxis3D/Source/EngineDefinitions.h
  96. 19 0
      Praxis3D/Source/EngineState.h
  97. 34 0
      Praxis3D/Source/EnumFactory.h
  98. 121 0
      Praxis3D/Source/EnvironmentMapObjects.h
  99. 7 0
      Praxis3D/Source/ErrorCodes.cpp
  100. 94 0
      Praxis3D/Source/ErrorCodes.h

+ 40 - 0
Praxis3D.sln

@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Praxis3D", "Praxis3D\Praxis3D.vcxproj", "{A97875DA-A9E8-4B91-928C-8F0CE88581B9}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug 64bit|x64 = Debug 64bit|x64
+		Debug 64bit|x86 = Debug 64bit|x86
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release 64bit|x64 = Release 64bit|x64
+		Release 64bit|x86 = Release 64bit|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug 64bit|x64.ActiveCfg = Debug 64bit|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug 64bit|x64.Build.0 = Debug 64bit|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug 64bit|x86.ActiveCfg = Debug 64bit|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug 64bit|x86.Build.0 = Debug 64bit|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug|x64.ActiveCfg = Debug|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug|x64.Build.0 = Debug|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug|x86.ActiveCfg = Debug|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Debug|x86.Build.0 = Debug|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release 64bit|x64.ActiveCfg = Release 64bit|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release 64bit|x64.Build.0 = Release 64bit|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release 64bit|x86.ActiveCfg = Release 64bit|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release 64bit|x86.Build.0 = Release 64bit|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release|x64.ActiveCfg = Release|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release|x64.Build.0 = Release|x64
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release|x86.ActiveCfg = Release|Win32
+		{A97875DA-A9E8-4B91-928C-8F0CE88581B9}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 279 - 0
Praxis3D/Data/Maps/default - Copy.pmap

@@ -0,0 +1,279 @@
+{
+	"LoadInBackground": "true",
+	"Systems":
+	{
+		"Graphics":
+		{
+			"Scene":
+			{
+				"ModelPoolSize" : "10",
+				"PointLightPoolSize" : "10",
+				"ShaderPoolSize" : "10",
+				"SpotLightPoolSize" : "10"
+			},
+			"Objects":
+			[
+				{
+					"Type": "ModelObject",
+					"Name": "Test1",
+					"OffsetPosition": "0.0, 0.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "30000.0, 30000.0, 30000.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "skybox.frag",
+						"VertexShader": "skybox.vert"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain1",
+					"OffsetPosition": "0.0, -100.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "200.0, 200.0, 200.0",
+					"Lighting": "true",
+					"Models":
+					{
+						"Filename": "terrain_plane2.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "height\diffuse 2.jpg",
+								"Index": "1"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "height\normal 2.jpg",
+								"Index": "1"
+							}
+						],
+						"Height":
+						[
+							{
+								"Filename": "height\h2.tif",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "terrain3.frag",
+						"TessControlShader": "terrain4.tesc",
+						"TessEvaluationShader": "terrain4.tese",
+						"VertexShader": "terrain3.vert"
+					}
+				},
+				{
+					"Type": "ModelObject 2",
+					"Name": "Terrain Test 1",
+					"Position": "0.0, -100.0, 0.0",
+					"Rotation": "-90.0, 0.0, 0.0",
+					"Scale": "1.0, 1.0, 1.0",
+					"Models":
+					{
+						"Filename": "terrain1.3ds"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "terrain1_TX.jpg",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Dumpster 1",
+					"Position": "10.0, 0.0, 0.0",
+					"Rotation": "-90.0, 0.0, 90.0",
+					"Scale": "5.0, 5.0, 5.0",
+					"Models":
+					{
+						"Filename": "dumpster.3ds"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "Dumpster_DIFF_2048.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 1",
+					"Position": "2.0, 0.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 2",
+					"Rotation": "0.0, 150.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						
+					}
+				},
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 1",
+					"Position": "0.0, -20.0, 0.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 2",
+					"Position": "10.0, 1.0, 10.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 3",
+					"Position": "-10.0, -1.0, -10.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Direction": "0.0, 1.0, 00.0",
+					"Intensity": "0.0"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "0.0, -20.0, 0.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 2",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "10.0, 1.0, 10.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 3",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "-10.0, -1.0, -10.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 1",
+					"Color": "1.0, 0.0, 0.0",
+					"Position": "0.0, 1.0, 0.0",
+					"Direction": "0.0, 1.0, 0.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 2",
+					"Color": "0.0, 1.0, 0.0",
+					"Position": "0.0, 1.0, 0.0",
+					"Direction": "0.0, 1.0, 0.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 3",
+					"Color": "0.0, 0.0, 1.0",
+					"Position": "10.0, 0.0, 0.0",
+					"Direction": "0.0, 1.0, 0.0"
+				}
+			]
+		},
+		"Scripting":
+		{
+			"Objects":
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "0.0, 20.0, 0.0",
+					"Keybindings":
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings":
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugVertSyncKey": "68"
+					}
+				}
+			]
+		}
+	},
+	"ObjectLinks":
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Test1"
+//		},
+//		{
+//			"Subject": "Free Camera 1",
+//			"Observer": "Terrain1"
+		}
+	]
+}

+ 616 - 0
Praxis3D/Data/Maps/default.pmap

@@ -0,0 +1,616 @@
+{
+	"LoadInBackground": "true",
+	"Systems":
+	{
+		"Graphics":
+		{
+			"Scene":
+			{
+				"ModelPoolSize" : "15",
+				"PointLightPoolSize" : "10",
+				"ShaderPoolSize" : "10",
+				"SpotLightPoolSize" : "10"
+			},
+			"Objects":
+			[
+				{
+					"Type": "ModelObject",
+					"Name": "Skybox 1",
+					"OffsetPosition": "0.0, 0.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "30000.0, 30000.0, 30000.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "skybox.frag",
+						"VertexShader": "skybox.vert"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain1",
+					"Position": "0.0, -1.0, 0.0",
+					"OffsetPosition": "0.0, 0.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "400.0, 400.0, 400.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "terrain_plane2.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "height\terrain1_d_low.png",
+								"Index": "1"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "height\terrain1_n_low.jpg",
+								"Index": "1"
+							}
+						],
+						"Height":
+						[
+							{
+								"Filename": "height\terrain1_h_low.jpg",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "terrain3.frag",
+						"GeometryShader": "terrain3.geom",
+						"TessControlShader": "terrain4.tesc",
+						"TessEvaluationShader": "terrain4.tese",
+						"VertexShader": "terrain3.vert"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain Mesh 1",
+					"Position": "185.0, 105.0, 240.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "1.0, 1.0, 1.0",
+					"Models":
+					{
+						"Filename": "Terrain_5_cut.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "Textures\Color.tif",
+								"Index": "1"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "Textures\Normal.tif",
+								"Index": "1"
+							}
+						],
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 1",
+					"Position": "15.0, -14.0, 140.0",
+					"Rotation": "0.0, 180.0, 0.0",
+					"Scale": "0.5, 0.5, 0.5",
+					"Models":
+					{
+						"Filename": "WoodenCabinObj.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "Textures\WoodCabinDif.jpg",
+								"Index": "1"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "Textures\WoodCabinNM.jpg",
+								"Index": "1"
+							}
+						],
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 2",
+					"Position": "50.0, -10.0, 130.0",
+					"Rotation": "0.0, 180.0, 0.0",
+					"Scale": "0.1, 0.1, 0.1",
+					"Models":
+					{
+						"Filename": "Furnished_House.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 3",
+					"Position": "0.0, 1.0, 0.0",
+					"Rotation": "0.0, 180.0, 0.0",
+					"Scale": "1.0, 1.0, 1.0",
+					"Models":
+					{
+						"Filename": "House_06.obj"
+					},
+					"Materials":
+					{
+						"Normal":
+						[
+							{
+								"Filename": "maps\corrugated_01_n.tga",
+								"Index": "0"
+							},
+							{
+								"Filename": "maps\corrugated_01_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "maps\concrete_01_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "maps\wood_01_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "maps\brick_01_n.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "maps\plywood_01_n.tga",
+								"Index": "5"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Tree 1",
+					"Position": "0.0, 1.0, 10.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "1.0, 1.0, 1.0",
+					"AlphaThreshold": "0.1",
+					"Models":
+					{
+						"Filename": "palm tree 1.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "maps\Palm trees diffuse.tga",
+								"Index": "1"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "maps\Palm trees normal.tga",
+								"Index": "1"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Ground 1",
+					"Position": "0.0, 0.0, 0.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "50.0, 50.0, 50.0",
+					"Models":
+					{
+						"Filename": "ground_plane2.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "brick-floor-tileable_COLOR.jpg",
+								"Index": "0"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "brick-floor-tileable_NRM.png",
+								"Index": "0"
+							}
+						],
+						"Specular":
+						[
+							{
+								"Filename": "brick-floor-tileable_SPEC.jpg",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Dumpster 1",
+					"Position": "10.0, 0.0, 0.0",
+					"Rotation": "-90.0, 0.0, 90.0",
+					"Scale": "5.0, 5.0, 5.0",
+					"Models":
+					{
+						"Filename": "dumpster.3ds"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "Dumpster_DIFF_2048.tga",
+								"Index": "0"
+							}
+						],
+						"Normal":
+						[
+							{
+								"Filename": "Dumpster_NRM_2048.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 1",
+					"Position": "2.0, 0.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						"Normal":
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 2",
+					"Rotation": "0.0, 150.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						"Normal":
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						]
+					}
+				},
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 1",
+					"Position": "0.0, -20.0, 0.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 2",
+					"Position": "10.0, 3.0, 10.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 3",
+					"Position": "-10.0, 3.0, -10.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 4",
+					"Position": "60.0, 3.0, 60.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 5",
+					"Position": "-40.0, 3.0, -40.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 6",
+					"Position": "-20.0, 3.0, -20.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Direction": "0.0, 0.25, 1.0",
+					"Intensity": "10.1"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "0.0, -20.0, 0.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 2",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "10.0, 3.0, 10.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 3",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "-10.0, 3.0, -10.0",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Direction": "-1.0, 0.0, 0.0",
+					"Position": "60.0, 3.0, 60.0",
+					"CutoffAngle": "60",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 2",
+					"Color": "0.0, 1.0, 0.0",
+					"Position": "-40.0, 3.0, -40.0",
+					"Direction": "1.00000525, 0.999692798, 1.00030184",
+					"CutoffAngle": "65",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 3",
+					"Color": "1.0, 1.0, 1.0",
+					"Position": "-20.0, 3.0, -20.0",
+					"Direction": "1.0, 0.0, 1.0",
+					"CutoffAngle": "55",
+					"Intensity": "100.0"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sun model 1",
+					"Position": "0.0, 0.0, 0.0",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					}
+				}
+			]
+		},
+		"Scripting":
+		{
+			"Objects":
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "0.0, 20.0, 0.0",
+					"Speed": "5.0",
+					"SprintSpeed": "50.0",
+					"Keybindings":
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings":
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugVertSyncKey": "68"
+					}
+				},
+				{
+					"Type": "DebugMoveScript",
+					"Name": "Debug Move Script 1",
+					"Position": "0.0, 10.0, 0.0",
+					"Rotation": "0.0, 1.0, 0.0",
+					"Radius": "20.0",
+					"Speed": "20.0"
+				},
+				{
+					"Type": "WorldEditScript",
+					"Name": "World Edit Script 1",
+					"Speed": "2.5",
+					"SprintSpeed": "25.0",
+					"Keybindings":
+					{
+						"ForwardKey": "96",
+						"BackwardKey": "90",
+						"UpKey": "95",
+						"DownKey": "89",
+						"LeftKey": "92",
+						"RightKey": "94",
+						"NextKey": "85",
+						"PreviousKey": "84",
+						"SaveKey": "22",
+						"ModifierKey": "224"
+					}
+				},
+				{
+					"Type": "SolarTimeScript",
+					"Name": "Solar Time Script 1",
+					"Position": "0.0, 0.0, 0.0",
+					"OffsetPosition": "25.0",
+					"Hours": "12",
+					"Minutes": "0",
+					"Seconds": "0.0",
+					"Latitude": "54.0",
+					"Longitude": "24.0",
+					"DayOfYear": "1",
+					"TimeMultiplier": "1000.0"
+				}
+			]
+		}
+	},
+	"ObjectLinks":
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Skybox 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "DirectionalLight 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "sun model 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "PointLight 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "light 1"
+		},
+		{
+			"Subject": "PointLight 2",
+			"Observer": "light 2"
+		},
+		{
+			"Subject": "PointLight 3",
+			"Observer": "light 3"
+		},
+		{
+			"Subject": "SpotLight 1",
+			"Observer": "light 4"
+		},
+		{
+			"Subject": "SpotLight 2",
+			"Observer": "light 5"
+		},
+		{
+			"Subject": "SpotLight 3",
+			"Observer": "light 6"
+		}
+	]
+}

+ 295 - 0
Praxis3D/Data/Maps/default2.pmap

@@ -0,0 +1,295 @@
+{
+	"LoadInBackground": "true",
+	"Systems":
+	{
+		"Graphics":
+		{
+			"Scene":
+			{
+				"ModelPoolSize" : "10",
+				"ShaderPoolSize" : "10"
+			},
+			"Objects":
+			[
+				{
+					"Type": "ModelObject",
+					"Name": "Test1",
+					"OffsetPosition": "0.0, 0.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "5000.0, 5000.0, 5000.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						]
+//					},
+//					"Shaders":
+//					{
+//						"FragmentShader": "test2.pfrag",
+//						"VertexShader": "test2.pvert"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Water1",
+					"Position": "0.0, -50.0, 0.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "1000.0, 1000.0, 1000.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "terrain_plane.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "diffuse_and_heightmap_8bit_small.tif",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "terrain3.frag",
+//						"GeometryShader": "terrain2.geom",
+						"TessControlShader": "terrain3.tesc",
+						"TessEvaluationShader": "terrain3.tese",
+						"VertexShader": "terrain3.vert"
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Dumpster 1",
+					"Position": "10.0, 0.0, 0.0",
+					"Rotation": "-90.0, 0.0, 90.0",
+					"Scale": "5.0, 5.0, 5.0",
+					"Models":
+					{
+						"Filename": "dumpster.3ds"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "Dumpster_DIFF_2048.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 1",
+					"Position": "2.0, 0.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 2",
+					"Rotation": "0.0, 150.0, 0.0",
+					"Models":
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials":
+					{
+						
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Quake Map",
+					"Position": "0.0, 0.0, 0.0",
+					"Rotation": "-90.0, 0.0, 0.0",
+					"Scale": "1.0, 1.0, 1.0",
+					"Models":
+					{
+						"Filename": "lostworld.pk3"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "7"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "8"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "47"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "54"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "70"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "71"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "87"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "91"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "105"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "106"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "107"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "116"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "118"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "119"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "120"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "121"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "122"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": ""
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "136"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "137"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "138"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "139"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "140"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "147"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "174"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "175"
+							},
+							{
+								"Filename": "gkc18b.jpg",
+								"Index": "176"
+							}
+						],
+						"Default":
+						{
+							"Diffuse":
+							{
+								"Filename": "dark_block.jpg"
+							}
+						}
+					}
+				},
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				}
+			]
+		},
+		"Scripting":
+		{
+			"Objects":
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "0.0, 20.0, 0.0",
+					"Keybindings":
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings":
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugVertSyncKey": "68"
+					}
+				}
+			]
+		}
+	},
+	"ObjectLinks":
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Test1"
+		}
+	]
+}

+ 1062 - 0
Praxis3D/Data/Maps/default_lite - Copy (2).pmap

@@ -0,0 +1,1062 @@
+{
+	"LoadInBackground": "1",
+	"Systems": 
+	{
+		"Graphics": 
+		{
+			"Scene": 
+			{
+				"ModelPoolSize": "15",
+				"PointLightPoolSize": "10",
+				"SpotLightPoolSize": "10"
+			},
+			"Objects": 
+			[
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.0233232f, -0.706914f, -0.706914f",
+					"Intensity": "1.1f"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain Mesh 1",
+					"Position": "20.0f, 0.0f, 10.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.1f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "House3.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Woodx_tiled_difuse.png",
+								"Index": "1"
+							},
+							{
+								"Filename": "Bricks2_1_difuse.png",
+								"Index": "2"
+							},
+							{
+								"Filename": "KamennyDom1_Okenice_difuse.png",
+								"Index": "3"
+							},
+							{
+								"Filename": "WoodT_difuse.png",
+								"Index": "4"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV2_difuse.png",
+								"Index": "5"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV4_difuse.png",
+								"Index": "6"
+							},
+							{
+								"Filename": "SpodokDomu_difuse.png",
+								"Index": "7"
+							},
+							{
+								"Filename": "Floor_difuse.png",
+								"Index": "8"
+							},
+							{
+								"Filename": "Roof_difuse.png",
+								"Index": "9"
+							},
+							{
+								"Filename": "KamennyDom2_VrchTrojuholnik_difuse.png",
+								"Index": "10"
+							},
+							{
+								"Filename": "Doors1_difuse.png",
+								"Index": "11"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Woodx_tiled_normal.png",
+								"Index": "1"
+							},
+							{
+								"Filename": "Bricks2_1_normal.png",
+								"Index": "2"
+							},
+							{
+								"Filename": "KamennyDom1_Okenice_normal.png",
+								"Index": "3"
+							},
+							{
+								"Filename": "WoodT_normal.png",
+								"Index": "4"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV2_normal.png",
+								"Index": "5"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV4_normal.png",
+								"Index": "6"
+							},
+							{
+								"Filename": "SpodokDomu_normal.png",
+								"Index": "7"
+							},
+							{
+								"Filename": "Floor_normal.png",
+								"Index": "8"
+							},
+							{
+								"Filename": "Roof_normal.png",
+								"Index": "9"
+							},
+							{
+								"Filename": "KamennyDom2_VrchTrojuholnik_normal.png",
+								"Index": "10"
+							},
+							{
+								"Filename": "Doors1_normal.png",
+								"Index": "11"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						[
+							{
+								"Filename": "Woodx_tiled_height.png",
+								"Index": "1"
+							},
+							{
+								"Filename": "Bricks2_1_height.png",
+								"Index": "2"
+							},
+							{
+								"Filename": "KamennyDom1_Okenice_height.png",
+								"Index": "3"
+							},
+							{
+								"Filename": "WoodT_height.png",
+								"Index": "4"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV2_height.png",
+								"Index": "5"
+							},
+							{
+								"Filename": "KamennyDom2_PrednaStenaV4_height.png",
+								"Index": "6"
+							},
+							{
+								"Filename": "SpodokDomu_height.png",
+								"Index": "7"
+							},
+							{
+								"Filename": "Floor_height.png",
+								"Index": "8"
+							},
+							{
+								"Filename": "Roof_height.png",
+								"Index": "9"
+							},
+							{
+								"Filename": "KamennyDom2_VrchTrojuholnik_height.png",
+								"Index": "10"
+							},
+							{
+								"Filename": "Doors1_height.png",
+								"Index": "11"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 1",
+					"Position": "-27.8774f, 0.728829f, 43.7131f",
+					"Rotation": "0.0f, 180.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "WoodenCabinObj.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Textures\WoodCabinDif.jpg",
+								"Index": "1"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Textures\WoodCabinNM.jpg",
+								"Index": "1"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						[
+							{
+								"Filename": "WoodCabinNM.jpg",
+								"Index": "1"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Tree 1",
+					"Position": "-11.6021f, 0.704742f, -4.05555f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.1f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "palm tree 1.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "maps\Palm trees diffuse.tga",
+								"Index": "1"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "maps\Palm trees normal.tga",
+								"Index": "1"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						[
+							{
+								"Filename": "null",
+								"Index": "1"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Dumpster 1",
+					"Position": "15.0617f, 0.0f, -6.05827f",
+					"Rotation": "-90.0f, 0.0f, 90.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "5.0f, 5.0f, 5.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "dumpster.3ds"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Dumpster_DIFF_2048.tga",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Dumpster_NRM_2048.tga",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 1",
+					"Position": "2.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "nanosuit_arms_d.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_d.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_d.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_d.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_d.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_d.tga",
+								"Index": "7"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Test Object 2",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 150.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "Nanosuit.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "nanosuit_arms_d.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_d.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_d.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_d.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_d.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_d.tga",
+								"Index": "7"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_helmet_pt_d.tga",
+								"Index": "4"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						[
+							{
+								"Filename": "nanosuit_arms_n.tga",
+								"Index": "1"
+							},
+							{
+								"Filename": "nanosuit_hands_n.tga",
+								"Index": "2"
+							},
+							{
+								"Filename": "nanosuit_helmet_n.tga",
+								"Index": "3"
+							},
+							{
+								"Filename": "nanosuit_legs_n.tga",
+								"Index": "5"
+							},
+							{
+								"Filename": "nanosuit_torso_n.tga",
+								"Index": "6"
+							},
+							{
+								"Filename": "nanosuit_visor_n.tga",
+								"Index": "7"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 1",
+					"Position": "14.8806f, 10.0f, -13.3629f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 2",
+					"Position": "9.12898f, 7.98369f, 9.59357f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 3",
+					"Position": "-10.0f, 3.0f, -10.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 4",
+					"Position": "60.0f, 3.0f, 60.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 5",
+					"Position": "-40.0f, 3.0f, -40.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 6",
+					"Position": "-20.0f, 3.0f, -20.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sun model 1",
+					"Position": "-0.583079f, 17.6729f, 17.6729f",
+					"Rotation": "0.0233232f, -0.706914f, -0.706914f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Skybox 1",
+					"Position": "2.27048f, 15.3453f, -12.2715f",
+					"Rotation": "0.0f, -0.588181f, 18.5954f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "30000.0f, 30000.0f, 30000.0f",
+					"Lighting": "0",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					},
+					"Shaders": 
+					{
+						"FragmentShader": "skybox.frag",
+						"VertexShader": "skybox.vert"
+					}
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 1",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "14.8806f, 10.0f, -13.3629f"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 2",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "9.12898f, 7.98369f, 9.59357f"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 3",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "-10.0f, 3.0f, -10.0f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 1",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "0.999848f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "-1.0f, 0.0f, 0.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "60.0f, 3.0f, 60.0f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 2",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "0.999848f",
+					"Color": "0.0f, 1.0f, 0.0f",
+					"Direction": "0.577353f, 0.577173f, 0.577525f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-40.0f, 3.0f, -40.0f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 3",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "0.999848f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.707107f, 0.0f, 0.707107f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-20.0f, 3.0f, -20.0f"
+				}
+			]
+		},
+		"Scripting": 
+		{
+			"Scene": 
+			{
+			},
+			"Objects": 
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "2.27755f, 15.364f, -12.2987f",
+					"Angle": "18.5954f, -0.588181f",
+					"Speed": "5.0f",
+					"SprintSpeed": "50.0f",
+					"Keybindings": 
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings": 
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugVertSyncKey": "68",
+						"CloseKey": "41"
+					}
+				},
+				{
+					"Type": "DebugMoveScript",
+					"Name": "Debug Move Script 1",
+					"Position": "0.0f, 10.0f, 0.0f",
+					"Radius": "20.0f",
+					"Rotation": "0.0f, 1.0f, 0.0f",
+					"Speed": "20.0f"
+				},
+				{
+					"Type": "WorldEditScript",
+					"Name": "World Edit Script 1",
+					"Speed": "2.5f",
+					"SprintSpeed": "25.0f",
+					"Keybindings": 
+					{
+						"ForwardKey": "96",
+						"BackwardKey": "90",
+						"UpKey": "95",
+						"DownKey": "89",
+						"LeftKey": "92",
+						"RightKey": "94",
+						"NextKey": "85",
+						"PreviousKey": "84",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "SolarTimeScript",
+					"Name": "Solar Time Script 1",
+					"Hours": "12",
+					"Seconds": "0.0f",
+					"Latitude": "54.0f",
+					"Longitude": "54.0f",
+					"DayOfYear": "1",
+					"TimeMultiplier": "1000.0f",
+					"OffsetPosition": "25.0f"
+				}
+			]
+		}
+	},
+	"ObjectLinks": 
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Skybox 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "DirectionalLight 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "sun model 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "PointLight 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "light 1"
+		},
+		{
+			"Subject": "PointLight 2",
+			"Observer": "light 2"
+		},
+		{
+			"Subject": "PointLight 3",
+			"Observer": "light 3"
+		},
+		{
+			"Subject": "SpotLight 1",
+			"Observer": "light 4"
+		},
+		{
+			"Subject": "SpotLight 2",
+			"Observer": "light 5"
+		},
+		{
+			"Subject": "SpotLight 3",
+			"Observer": "light 6"
+		}
+	]
+}

+ 162 - 0
Praxis3D/Data/Maps/default_lite - Copy.pmap

@@ -0,0 +1,162 @@
+{
+	"LoadInBackground": "true",
+	"Systems":
+	{
+		"Graphics":
+		{
+			"Scene":
+			{
+				"ModelPoolSize" : "15",
+				"PointLightPoolSize" : "10",
+				"ShaderPoolSize" : "10",
+				"SpotLightPoolSize" : "10"
+			},
+			"Objects":
+			[
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0, 1.0, 1.0",
+					"Direction": "0.0, 0.25, 1.0",
+					"Intensity": "1.1"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Ground 1",
+					"Position": "0.0, 0.0, 0.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "50.0, 50.0, 50.0",
+					"Models":
+					{
+						"Filename": "ground_plane2.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "brick-floor-tileable_COLOR.jpg",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Skybox 1",
+					"OffsetPosition": "0.0, 0.0, 0.0",
+					"OffsetRotation": "0.0, 0.0, 0.0",
+					"Scale": "30000.0, 30000.0, 30000.0",
+					"Lighting": "false",
+					"Models":
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials":
+					{
+						"Diffuse":
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						]
+					},
+					"Shaders":
+					{
+						"FragmentShader": "skybox.frag",
+						"VertexShader": "skybox.vert"
+					}
+				}
+			]
+		},
+		"Scripting":
+		{
+			"Objects":
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "0.0, 20.0, 0.0",
+					"Speed": "5.0",
+					"SprintSpeed": "50.0",
+					"Keybindings":
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings":
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugVertSyncKey": "68"
+					}
+				},
+				{
+					"Type": "DebugMoveScript",
+					"Name": "Debug Move Script 1",
+					"Position": "0.0, 10.0, 0.0",
+					"Rotation": "0.0, 1.0, 0.0",
+					"Radius": "20.0",
+					"Speed": "20.0"
+				},
+				{
+					"Type": "WorldEditScript",
+					"Name": "World Edit Script 1",
+					"Speed": "2.5",
+					"SprintSpeed": "25.0",
+					"Keybindings":
+					{
+						"ForwardKey": "96",
+						"BackwardKey": "90",
+						"UpKey": "95",
+						"DownKey": "89",
+						"LeftKey": "92",
+						"RightKey": "94",
+						"NextKey": "85",
+						"PreviousKey": "84",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "SolarTimeScript",
+					"Name": "Solar Time Script 1",
+					"Position": "0.0, 0.0, 0.0",
+					"OffsetPosition": "25.0",
+					"Hours": "12",
+					"Minutes": "0",
+					"Seconds": "0.0",
+					"Latitude": "54.0",
+					"Longitude": "24.0",
+					"DayOfYear": "1",
+					"TimeMultiplier": "10000.0"
+				}
+			]
+		}
+	},
+	"ObjectLinks":
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Skybox 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "DirectionalLight 1"
+		}
+	]
+}

+ 905 - 0
Praxis3D/Data/Maps/default_lite.pmap

@@ -0,0 +1,905 @@
+{
+	"LoadInBackground": "0",
+	"Systems": 
+	{
+		"Graphics": 
+		{
+			"Scene": 
+			{
+				"ModelPoolSize": "15",
+				"PointLightPoolSize": "10",
+				"SpotLightPoolSize": "10"
+			},
+			"Objects": 
+			[
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.0340488f, -0.706697f, -0.706697f",
+					"Intensity": "0.1f"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain1",
+					"Position": "100.0f, 1.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "100.0f, 100.0f, 100.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "20.0f",
+					"Models": 
+					{
+						"Filename": "plane.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_alb.tga",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_n.tga",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_RMHAO.tga",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_g.tga",
+								"Index": "0"
+							}
+						],
+						"Height": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_h.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain2",
+					"Position": "-100.0f, 1.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "100.0f, 100.0f, 100.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "20.0f",
+					"Models": 
+					{
+						"Filename": "plane.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_2k_alb.tga",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_2k_n.tga",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_RMHAO3.tga",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_2k_g.tga",
+								"Index": "0"
+							}
+						],
+						"Height": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_2k_h.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Dumpster 1",
+					"Position": "15.0617f, 0.0f, -6.05827f",
+					"Rotation": "-90.0f, 0.0f, 90.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "5.0f, 5.0f, 5.0f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "dumpster.3ds"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Dumpster_DIFF_2048.tga",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Dumpster_NRM_2048.tga",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 1",
+					"Position": "0.0f, 2.0f, 10.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test1_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 2",
+					"Position": "0.0f, 2.0f, 5.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test2_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 3",
+					"Position": "0.0f, 2.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test3_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 4",
+					"Position": "0.0f, 2.0f, -5.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test4_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 5",
+					"Position": "0.0f, 2.0f, -10.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test5_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "sphere 6",
+					"Position": "0.0f, 2.0f, -15.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.1f, 0.1f, 0.1f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "2_second_Ball.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "test_albedo.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						[
+							{
+								"Filename": "test6_RM.png",
+								"Index": "0"
+							}
+						],
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 1",
+					"Position": "-16.1514f, 10.0f, -11.7956f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 2",
+					"Position": "9.12898f, 7.98369f, 9.59357f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 3",
+					"Position": "-6.94746f, 8.59811f, 7.54629f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 4",
+					"Position": "60.0f, 3.0f, 60.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 5",
+					"Position": "-40.0f, 3.0f, 1.96449f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "light 6",
+					"Position": "-20.0f, 3.0f, -20.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "0.5f, 0.5f, 0.5f",
+					"Lighting": "1",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+						},
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Skybox 1",
+					"Position": "2.32071f, 8.89734f, 7.00538f",
+					"Rotation": "0.0f, -0.95052f, 23.5525f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "30000.0f, 30000.0f, 30000.0f",
+					"Lighting": "0",
+					"AlphaThreshold": "0.0f",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "sphere_inverted.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "gRbWE9O.jpg",
+								"Index": "1"
+							}
+						],
+						"Normal": 
+						{
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+						}
+					},
+					"Shaders": 
+					{
+						"FragmentShader": "skybox.frag",
+						"VertexShader": "skybox.vert"
+					}
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 1",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "-16.1514f, 10.0f, -11.7956f"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 2",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "9.12898f, 7.98369f, 9.59357f"
+				},
+				{
+					"Type": "PointLight",
+					"Name": "PointLight 3",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Intensity": "100.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"Position": "-6.94746f, 8.59811f, 7.54629f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 1",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "60.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "-1.0f, 0.0f, 0.0f",
+					"Intensity": "200.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "60.0f, 3.0f, 60.0f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 2",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "60.0f",
+					"Color": "0.0f, 1.0f, 0.0f",
+					"Direction": "0.577353f, 0.577173f, 0.577525f",
+					"Intensity": "200.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-40.0f, 3.0f, 1.96449f"
+				},
+				{
+					"Type": "SpotLight",
+					"Name": "SpotLight 3",
+					"Attenuation": "0.0f, 0.0f, 1.0f",
+					"CutoffAngle": "60.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.707107f, 0.0f, 0.707107f",
+					"Intensity": "200.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-20.0f, 3.0f, -20.0f"
+				}
+			]
+		},
+		"Scripting": 
+		{
+			"Scene": 
+			{
+			},
+			"Objects": 
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "2.32533f, 8.9038f, 7.00543f",
+					"Angle": "23.5525f, -0.95052f",
+					"Speed": "1.0f",
+					"SprintSpeed": "50.0f",
+					"Keybindings": 
+					{
+						"ForwardKey": "26",
+						"BackwardKey": "22",
+						"LeftStrafeKey": "4",
+						"RightStrafeKey": "7",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "DebugUIScript",
+					"Name": "Debug UI Script 1",
+					"Keybindings": 
+					{
+						"DebugCaptureMouseKey": "67",
+						"DebugFullscreenKey": "66",
+						"DebugVertSyncKey": "68",
+						"CloseKey": "41"
+					}
+				},
+				{
+					"Type": "DebugMoveScript",
+					"Name": "Debug Move Script 1",
+					"Position": "0.0f, 10.0f, 0.0f",
+					"Radius": "20.0f",
+					"Rotation": "0.0f, 1.0f, 0.0f",
+					"Speed": "20.0f"
+				},
+				{
+					"Type": "WorldEditScript",
+					"Name": "World Edit Script 1",
+					"Speed": "2.5f",
+					"SprintSpeed": "25.0f",
+					"Keybindings": 
+					{
+						"ForwardKey": "96",
+						"BackwardKey": "90",
+						"UpKey": "95",
+						"DownKey": "89",
+						"LeftKey": "92",
+						"RightKey": "94",
+						"NextKey": "85",
+						"PreviousKey": "84",
+						"SprintKey": "225"
+					}
+				},
+				{
+					"Type": "SolarTimeScript",
+					"Name": "Solar Time Script 1",
+					"Hours": "12",
+					"Seconds": "0.0f",
+					"Latitude": "54.0f",
+					"Longitude": "54.0f",
+					"DayOfYear": "1",
+					"TimeMultiplier": "10.0f",
+					"OffsetPosition": "25.0f"
+				}
+			]
+		}
+	},
+	"ObjectLinks": 
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		},
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Skybox 1"
+		},
+		{
+			"Subject": "Solar Time Script 1",
+			"Observer": "DirectionalLight 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "PointLight 1"
+		},
+		{
+			"Subject": "Debug Move Script 1",
+			"Observer": "light 1"
+		},
+		{
+			"Subject": "PointLight 2",
+			"Observer": "light 2"
+		},
+		{
+			"Subject": "PointLight 3",
+			"Observer": "light 3"
+		},
+		{
+			"Subject": "SpotLight 1",
+			"Observer": "light 4"
+		},
+		{
+			"Subject": "SpotLight 2",
+			"Observer": "light 5"
+		},
+		{
+			"Subject": "SpotLight 3",
+			"Observer": "light 6"
+		}
+	]
+}

+ 287 - 0
Praxis3D/Data/Maps/test.pmap

@@ -0,0 +1,287 @@
+{
+	"LoadInBackground": "1",
+	"Systems": 
+	{
+		"Graphics": 
+		{
+			"Scene": 
+			{
+				"ModelPoolSize": "15",
+				"PointLightPoolSize": "10",
+				"SpotLightPoolSize": "10"
+			},
+			"Objects": 
+			{
+				{
+					"Type": "ModelObject",
+					"Name": "DirectionalLight 1",
+					"Color": "1, 1, 1",
+					"Direction": "0.847546, 0.375277, 0.375277",
+					"Intensity": "1.1f"
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 1",
+					"Position": "0.0, 0.0, 0.0",
+					"Rotation": "0.0, 0.0, 0.0",
+					"Scale": "0.5, 0.5, 0.5",
+					"Lighting": "1",
+					"AlphaThreshold": "0f",
+					"Models": 
+					{
+						"Filename": "WoodenCabinObj.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+							[
+								{
+									"Filename": "Textures\WoodCabinDif.jpg",
+									"Index": "1"
+								}
+							]
+						}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 1",
+					"Position": "15, -14, 140",
+					"Rotation": "0, 180, 0",
+					"Scale": "0.5, 0.5, 0.5",
+					"Lighting": "1",
+					"AlphaThreshold": "0f",
+					"Models": 
+					{
+						"Filename": "WoodenCabinObj.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+							[
+								{
+									"Filename": "Textures\WoodCabinDif.jpg",
+									"Index": "1"
+								}
+							]
+						},
+						"Normal": 
+						{
+							[
+								{
+									"Filename": "Textures\WoodCabinNM.jpg",
+									"Index": "1"
+								}
+							]
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+							[
+								{
+									"Filename": "WoodCabinNM.jpg",
+									"Index": "1"
+								}
+							]
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Wooden Cabin 3",
+					"Position": "0, 1, 0",
+					"Rotation": "0, 180, 0",
+					"Scale": "1, 1, 1",
+					"Lighting": "1",
+					"AlphaThreshold": "0f",
+					"Models": 
+					{
+						"Filename": "House_06.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						{
+							[
+								{
+									"Filename": "maps\corrugated_01_d.tga",
+									"Index": "1"
+								},
+								{
+									"Filename": "maps\stucco_03_d.tga",
+									"Index": "2"
+								},
+								{
+									"Filename": "maps\concrete_01_d.tga",
+									"Index": "3"
+								},
+								{
+									"Filename": "maps\wood_01_d.tga",
+									"Index": "4"
+								},
+								{
+									"Filename": "maps\brick_01_d.tga",
+									"Index": "5"
+								},
+								{
+									"Filename": "maps\plywood_01_d.tga",
+									"Index": "6"
+								}
+							]
+						},
+						"Normal": 
+						{
+							[
+								{
+									"Filename": "maps\corrugated_01_n.tga, maps\corrugated_01_n.tga",
+									"Index": "0"
+								},
+								{
+									"Filename": "maps\corrugated_01_n.tga, maps\corrugated_01_n.tga",
+									"Index": "1"
+								},
+								{
+									"Filename": "maps\concrete_01_n.tga",
+									"Index": "2"
+								},
+								{
+									"Filename": "maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "3"
+								},
+								{
+									"Filename": "maps\brick_01_n.tga, maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "4"
+								},
+								{
+									"Filename": "maps\plywood_01_n.tga, maps\brick_01_n.tga, maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "5"
+								}
+							]
+						},
+						"Emissive": 
+						{
+						},
+						"Specular": 
+						{
+						},
+						"Gloss": 
+						{
+						},
+						"Height": 
+						{
+							[
+								{
+									"Filename": "maps\corrugated_01_n.tga, maps\corrugated_01_n.tga",
+									"Index": "1"
+								},
+								{
+									"Filename": "maps\concrete_01_n.tga",
+									"Index": "3"
+								},
+								{
+									"Filename": "maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "4"
+								},
+								{
+									"Filename": "maps\brick_01_n.tga, maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "5"
+								},
+								{
+									"Filename": "maps\plywood_01_n.tga, maps\brick_01_n.tga, maps\wood_01_n.tga, maps\concrete_01_n.tga",
+									"Index": "6"
+								}
+							]
+						}
+					}
+				}
+			}
+		},
+		"Scripting": 
+		{
+			"Scene": 
+			{
+			},
+			"Objects": 
+			{
+				[
+					{
+						"Type": "ModelObject",
+						"Name": "Free Camera 1",
+						"Position": "0, 20, 0",
+						"Speed": "5f",
+						"SprintSpeed": "50f",
+						"Keybindings": 
+						{
+							"ForwardKey": "26",
+							"BackwardKey": "22",
+							"LeftStrafeKey": "4",
+							"RightStrafeKey": "7",
+							"SprintKey": "225"
+						}
+					},
+					{
+						"Type": "ModelObject",
+						"Name": "Debug UI Script 1",
+						"Keybindings": 
+						{
+							"DebugCaptureMouseKey": "67",
+							"DebugVertSyncKey": "68",
+							"CloseKey": "41"
+						}
+					},
+					{
+						"Type": "ModelObject",
+						"Name": "World Edit Script 1",
+						"Speed": "2.5f",
+						"SprintSpeed": "25f",
+						"Keybindings": 
+						{
+							"ForwardKey": "82",
+							"BackwardKey": "81",
+							"UpKey": "75",
+							"DownKey": "78",
+							"LeftKey": "80",
+							"RightKey": "79",
+							"NextKey": "55",
+							"PreviousKey": "54",
+							"SprintKey": "225"
+						}
+					},
+					{
+						"Type": "ModelObject",
+						"Name": "Solar Time Script 1",
+						"Hours": "12",
+						"Seconds": "0f",
+						"Latitude": "54f",
+						"Longitude": "54f",
+						"DayOfYear": "1",
+						"TimeMultiplier": "1000f",
+						"OffsetPosition": "25f"
+					}
+				]
+			}
+		}
+	},
+	"ObjectLinks": 
+	{
+		[
+			{
+				"Subject": "Free Camera 1",
+				"Observer": "Camera 1"
+			},
+			{
+				"Subject": "Solar Time Script 1",
+				"Observer": "DirectionalLight 1"
+			}
+		]
+	}
+}

+ 539 - 0
Praxis3D/Data/Shaders/clouds.frag

@@ -0,0 +1,539 @@
+#version 330 core
+
+uniform vec3 cameraTargetVec;
+uniform float elapsedTime;
+uniform vec4 testVec;
+uniform vec2 screenSize;
+
+//in vec2 texCoord;
+
+out vec4 fragColor;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+in vec3 viewDir;
+
+vec3 iResolution = vec3(1600, 900, 0.0);
+
+//void main() 
+//{
+//   fragColor = vec4(0.0, texCoord, 1.0);
+//}
+
+// "Wavescape" by dr2 - 2015
+// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
+
+// Water waves, including what the fish sees.
+
+// Acknowledgments: thanks for the following -
+//  Dave_H's multilayered clouds with nimitz's variable layer spacing
+//    (but depends on elevation rather than distance).
+//  Wave shapes from TDM; they seem a little more "energetic" than TekF's.
+//  Raymarching with binary subdivision, as used by Dave_H for mountains;
+//    TekF and TDM use one or the other, not both.
+//  Buoy based on TekF's, but raymarched for generality; shows aging effects
+//    below waterline.
+//  Foam idea from TekF.
+//  Noise functions from iq.
+
+const float pi = 3.14159;
+const vec4 cHashA4 = vec4 (0., 1., 57., 58.);
+const vec3 cHashA3 = vec3 (1., 57., 113.);
+const float cHashM = 43758.54;
+
+vec2 Hashv2f (float p)
+{
+  return fract (sin (p + cHashA4.xy) * cHashM);
+}
+
+vec4 Hashv4f (float p)
+{
+  return fract (sin (p + cHashA4) * cHashM);
+}
+
+vec4 Hashv4v3 (vec3 p)
+{
+  const vec3 cHashVA3 = vec3 (37.1, 61.7, 12.4);
+  const vec3 e = vec3 (1., 0., 0.);
+  return fract (sin (vec4 (dot (p + e.yyy, cHashVA3), dot (p + e.xyy, cHashVA3),
+     dot (p + e.yxy, cHashVA3), dot (p + e.xxy, cHashVA3))) * cHashM);
+}
+
+float Noiseff (float p)
+{
+  float i, f;
+  i = floor (p);  f = fract (p);
+  f = f * f * (3. - 2. * f);
+  vec2 t = Hashv2f (i);
+  return mix (t.x, t.y, f);
+}
+
+float Noisefv2 (vec2 p)
+{
+  vec2 i, f;
+  i = floor (p);  f = fract (p);
+  f = f * f * (3. - 2. * f);
+  vec4 t = Hashv4f (dot (i, cHashA3.xy));
+  return mix (mix (t.x, t.y, f.x), mix (t.z, t.w, f.x), f.y);
+}
+
+float Noisefv3a (vec3 p)
+{
+  vec3 i, f;
+  i = floor (p);  f = fract (p);
+  f *= f * (3. - 2. * f);
+  vec4 t1 = Hashv4v3 (i);
+  vec4 t2 = Hashv4v3 (i + vec3 (0., 0., 1.));
+  return mix (mix (mix (t1.x, t1.y, f.x), mix (t1.z, t1.w, f.x), f.y),
+              mix (mix (t2.x, t2.y, f.x), mix (t2.z, t2.w, f.x), f.y), f.z);
+}
+
+float Fbm1 (float p)
+{
+  float f, a;
+  f = 0.;  a = 1.;
+  for (int i = 0; i < 5; i ++) {
+    f += a * Noiseff (p);
+    a *= 0.5;  p *= 2.;
+  }
+  return f;
+}
+
+float Fbm3 (vec3 p)
+{
+  const mat3 mr = mat3 (0., 0.8, 0.6, -0.8, 0.36, -0.48, -0.6, -0.48, 0.64);
+  float f, a, am, ap;
+  f = 0.;  a = 0.5;
+  am = 0.5;  ap = 4.;
+  p *= 0.5;
+  for (int i = 0; i < 6; i ++) {
+    f += a * Noisefv3a (p);
+    p *= mr * ap;  a *= am;
+  }
+  return f;
+}
+
+float Fbmn (vec3 p, vec3 n)
+{
+  vec3 f = vec3 (0.);
+  float a = 1.;
+  for (int i = 0; i < 5; i ++) {
+    f += a * vec3 (Noisefv2 (p.yz), Noisefv2 (p.zx), Noisefv2 (p.xy));
+    a *= 0.5;  p *= 2.;
+  }
+  return dot (f, abs (n));
+}
+
+vec3 VaryNf (vec3 p, vec3 n, float f)
+{
+  vec3 e = vec3 (0.2, 0., 0.);
+  float s = Fbmn (p, n);
+  vec3 g = vec3 (Fbmn (p + e.xyy, n) - s,
+     Fbmn (p + e.yxy, n) - s, Fbmn (p + e.yyx, n) - s);
+  return normalize (n + f * (g - n * dot (n, g)));
+}
+
+float SmoothBump (float lo, float hi, float w, float x)
+{
+  return (1. - smoothstep (hi - w, hi + w, x)) * smoothstep (lo - w, lo + w, x);
+}
+
+float PrSphDf (vec3 p, float s)
+{
+  return length (p) - s;
+}
+
+float PrCylDf (vec3 p, float r, float h)
+{
+  return max (length (p.xy) - r, abs (p.z) - h);
+}
+
+int idObj;
+mat3 ballMat;
+vec3 qHit, ballPos, sunCol, sunDir, cloudDisp, waterDisp;
+float tCur, fCloud;
+const float dstFar = 300.;
+
+vec3 SkyGrndCol (vec3 ro, vec3 rd)
+{
+  vec3 p, q, cSun, skyBg, clCol, col;
+  float colSum, attSum, s, att, a, dDotS, ds;
+  const vec3 cCol1 = 0.5 * vec3 (0.15, 0.2, 0.4),
+     cCol2 = 0.5 * vec3 (0.25, 0.5, 0.7), gCol = 1.3 * vec3 (0.05, 0.08, 0.05);
+  const float cloudLo = 100., cloudRngI = 1./50., atFac = 0.06;
+  const int nLay = 30;
+  if (rd.y < 0.015 * Fbm1 (16. * rd.x)) col = gCol * (0.5 + 0.5 *
+     Noisefv2 (1000. * vec2 (5. * atan (rd.x, rd.z), rd.y)));
+  else {
+    fCloud = clamp (fCloud, 0., 1.);
+    dDotS = max (dot (rd, sunDir), 0.);
+    ro += cloudDisp;
+    p = ro;
+    p.xz += (cloudLo - p.y) * rd.xz / rd.y;
+    p.y = cloudLo;
+    ds = 1. / (cloudRngI * rd.y * (2. - rd.y) * float (nLay));
+    colSum = 0.;  attSum = 0.;
+    s = 0.;  att = 0.;
+    for (int j = 0; j < nLay; j ++) {
+      q = p + rd * s;
+      q.z *= 0.7;
+      att += atFac * max (fCloud - Fbm3 (0.02 * q), 0.);
+      a = (1. - attSum) * att;
+      colSum += a * (q.y - cloudLo) * cloudRngI;
+      attSum += a;  s += ds;
+      if (attSum >= 1.) break;
+    }
+    colSum += 0.5 * min ((1. - attSum) * pow (dDotS, 3.), 1.);
+    clCol = vec3 (1.) * colSum + 0.05 * sunCol;
+    cSun = sunCol * clamp ((min (pow (dDotS, 1500.) * 2., 1.) +
+       min (pow (dDotS, 10.) * 0.75, 1.)), 0., 1.);
+    skyBg = mix (cCol1, cCol2, 1. - rd.y);
+    col = clamp (mix (skyBg + cSun, 1.6 * clCol, attSum), 0., 1.);
+  }
+  return col;
+}
+
+vec3 SeaFloorCol (vec3 rd)
+{
+  vec2 p;
+  float w, f;
+  p = 5. * rd.xz / rd.y;
+  w = 1.;
+  f = 0.;
+  for (int j = 0; j < 4; j ++) {
+    f += w * Noisefv2 (p);
+    w *= 0.5;  p *= 2.;
+  }
+  return mix (vec3 (0.01, 0.04, 0.02), vec3 (0, 0.05, 0.05), 
+     smoothstep (0.4, 0.7, f));
+}
+
+float WaveHt (vec3 p)
+{
+  const mat2 qRot = mat2 (1.6, -1.2, 1.2, 1.6);
+  vec4 t4, ta4, v4;
+  vec2 q2, t2, v2;
+  float wFreq, wAmp, pRough, ht;
+  wFreq = 0.16;  wAmp = 0.6;  pRough = 5.;
+  q2 = p.xz + waterDisp.xz;
+  ht = 0.;
+  for (int j = 0; j < 5; j ++) {
+    t2 = 1.1 * tCur * vec2 (1., -1.);
+    t4 = vec4 (q2 + t2.xx, q2 + t2.yy) * wFreq;
+    t2 = vec2 (Noisefv2 (t4.xy), Noisefv2 (t4.zw));
+    t4 += 2. * vec4 (t2.xx, t2.yy) - 1.;
+    ta4 = abs (sin (t4));
+    v4 = (1. - ta4) * (ta4 + abs (cos (t4)));
+    v2 = pow (1. - pow (v4.xz * v4.yw, vec2 (0.65)), vec2 (pRough));
+    ht += (v2.x + v2.y) * wAmp;
+    q2 *= qRot;  wFreq *= 1.9;  wAmp *= 0.22;
+    pRough = 0.8 * pRough + 0.2;
+  }
+  return ht;
+}
+
+float WaveRay (vec3 ro, vec3 rd)
+{
+  vec3 p;
+  float dHit, h, s, sLo, sHi;
+  s = 0.;
+  sLo = 0.;
+  dHit = dstFar;
+  for (int j = 0; j < 150; j ++) {
+    p = ro + s * rd;
+    h = p.y - WaveHt (p);
+    if (h < 0.) break;
+    sLo = s;
+    s += max (0.2, h) + 0.005 * s;
+    if (s > dstFar) break;
+  }
+  if (h < 0.) {
+    sHi = s;
+    for (int j = 0; j < 7; j ++) {
+      s = 0.5 * (sLo + sHi);
+      p = ro + s * rd;
+      h = step (0., p.y - WaveHt (p));
+      sLo += h * (s - sLo);
+      sHi += (1. - h) * (s - sHi);
+    }
+    dHit = sHi;
+  }
+  return dHit;
+}
+
+float WaveOutRay (vec3 ro, vec3 rd)
+{
+  vec3 p;
+  float dHit, h, s, sLo, sHi;
+  s = 0.;
+  sLo = 0.;
+  dHit = dstFar;
+  ro.y *= -1.;
+  rd.y *= -1.;
+  for (int j = 0; j < 150; j ++) {
+    p = ro + s * rd;
+    h = p.y + WaveHt (p);
+    if (h < 0.) break;
+    sLo = s;
+    s += max (0.2, h) + 0.005 * s;
+    if (s > dstFar) break;
+  }
+  if (h < 0.) {
+    sHi = s;
+    for (int j = 0; j < 7; j ++) {
+      s = 0.5 * (sLo + sHi);
+      p = ro + s * rd;
+      h = step (0., p.y + WaveHt (p));
+      sLo += h * (s - sLo);
+      sHi += (1. - h) * (s - sHi);
+    }
+    dHit = sHi;
+  }
+  return dHit;
+}
+
+vec3 WaveNf (vec3 p, float d)
+{
+  vec2 e = vec2 (max (0.1, 5e-5 * d * d), 0.);
+  float h = WaveHt (p);
+  return normalize (vec3 (h - WaveHt (p + e.xyy), e.x, h - WaveHt (p + e.yyx)));
+}
+
+float ObjDf (vec3 p)
+{
+  vec3 q;
+  float dHit, d;
+  dHit = dstFar;
+  q = p;
+  q -= ballPos;
+  q *= ballMat;
+  d = PrSphDf (q, 2.);
+  if (d < dHit) { dHit = d;  idObj = 1;  qHit = q; }
+  q.y -= 3.;
+  d = PrCylDf (q.xzy, 0.05, 1.);
+  if (d < dHit) { dHit = d;  idObj = 2;  qHit = q; }
+  q.y -= 1.3;
+  d = PrCylDf (q.xzy, 0.15, 0.3);
+  if (d < dHit) { dHit = d;  idObj = 3;  qHit = q; }
+  return dHit;
+}
+
+float ObjRay (in vec3 ro, in vec3 rd)
+{
+  float d, dHit;
+  dHit = 0.;
+  for (int j = 0; j < 150; j ++) {
+    d = ObjDf (ro + dHit * rd);
+    dHit += d;
+    if (d < 0.002 || dHit > dstFar) break;
+  }
+  return dHit;
+}
+
+vec3 ObjNf (vec3 p)
+{
+  const vec3 e = vec3 (0.001, -0.001, 0.);
+  vec4 v = vec4 (ObjDf (p + e.xxx), ObjDf (p + e.xyy),
+     ObjDf (p + e.yxy), ObjDf (p + e.yyx));
+  return normalize (vec3 (v.x - v.y - v.z - v.w) + 2. * v.yzw);
+}
+
+vec3 ObjCol (vec3 n)
+{
+  vec3 col;
+  col = vec3 (0.);
+  if (idObj == 1) {
+    col = vec3 (1., 0.01, 0.);
+    if (abs (qHit.y) < 0.21) col =
+       (mod (floor (7. * (atan (qHit.x, qHit.z) / pi + 1.)), 2.) == 0.) ?
+       vec3 (1., 0.8, 0.08) : vec3 (0.04);
+    else if (qHit.y > 1.93) col = vec3 (0.15, 0.05, 0.);
+    else if (abs (qHit.y) < 0.25) col = vec3 (1., 0.8, 0.08);
+    else if (abs (abs (qHit.y) - 0.55) < 0.05) col = vec3 (1.);
+    else if (abs (abs (qHit.y) - 0.65) < 0.05) col = vec3 (0.04);
+    if (qHit.y < 0.) col = mix (col, vec3 (0.05, 0.2, 0.05), 
+       min (- 2. * qHit.y, 0.9));
+  } else if (idObj == 2) {
+    col = vec3 (0.6, 0.4, 0.2);
+  } else if (idObj == 3) {
+    if (abs (qHit.y) < 0.2) 
+       col = vec3 (0., 1., 0.) * (2. + 1.5 * cos (10. * tCur));
+    else col = vec3 (0.6, 0.4, 0.2);
+  }
+  return col;
+}
+
+float ObjSShadow (vec3 ro, vec3 rd)
+{
+  float sh, d, h;
+  sh = 1.;
+  d = 0.1;
+  for (int j = 0; j < 40; j ++) {
+    h = ObjDf (ro + rd * d);
+    sh = min (sh, 20. * h / d);
+    d += 0.1;
+    if (h < 0.001) break;
+  }
+  return clamp (sh, 0., 1.);
+}
+
+vec3 ObjRender (vec3 ro, vec3 rd)
+{
+  vec3 col, vn;
+  float dif, bk, sh, cc;
+  int idObjT;
+  idObjT = idObj;
+  vn = ObjNf (ro);
+  idObj = idObjT;
+  if (idObj == 1) {
+    vn = VaryNf (20. * qHit, vn, 0.3);
+    if (qHit.y < 0.) vn = mix (vn, VaryNf (12. * qHit, vn, 2.),
+      min (- 5. * qHit.y, 1.));
+  }
+  col = ObjCol (rd);
+  cc = 1. - smoothstep (0.3, 0.6, fCloud);
+  sh = ObjSShadow (ro, sunDir);
+  bk = max (dot (vn, - normalize (vec3 (sunDir.x, 0., sunDir.z))), 0.);
+  dif = max (dot (vn, sunDir), 0.);
+  return col * (0.2 + 0.1 * bk + max (0., dif) * (0.7 + 0.3 * cc * sh)) + 
+     0.3 * cc * sh * sunCol * pow (max (0., dot (sunDir, reflect (rd, vn))), 32.);
+}
+
+vec3 ShowScene (vec3 ro, vec3 rd)
+{
+  vec3 col, vn, rdd, refCol, uwatCol;
+  float dstHit, dstWat, dif, bk, sh, foamFac;
+  const float eta = 0.75, att = 0.5;
+  int idObjT;
+  bool doReflect;
+  if (ro.y > WaveHt (ro)) {
+    dstWat = WaveRay (ro, rd);
+    idObj = -1;
+    dstHit = ObjRay (ro, rd);
+    if (idObj < 0) dstHit = dstFar;
+    doReflect = (dstWat < dstFar && dstWat < dstHit);
+    if (doReflect) {
+      ro += rd * dstWat;
+      vn = WaveNf (ro, dstWat);
+      rdd = rd;
+      rd = reflect (rd, vn);
+      idObj = -1;
+      dstHit = ObjRay (ro, rd);
+      if (idObj < 0) dstHit = dstFar;
+    }
+    col = (dstHit < dstFar) ? ObjRender (ro + rd * dstHit, rd) :
+       SkyGrndCol (ro, rd);
+    if (doReflect) {
+      refCol = col;
+      rd = refract (rdd, vn, eta);
+      idObj = -1;
+      dstHit = ObjRay (ro, rd);
+      if (idObj < 0) dstHit = dstFar;
+      col = (dstHit < dstFar) ? ObjRender (ro + rd * dstHit, rd) *
+         exp (- att * dstHit) : SeaFloorCol (rd);
+      col = mix (col, 0.8 * refCol, pow (1. - abs (dot (rdd, vn)), 5.));
+      foamFac = pow (clamp (WaveHt (ro) +
+         0.004 * Fbm3 (256. * ro) - 0.65, 0., 1.), 8.);
+      col = mix (col, vec3 (1.), foamFac);
+    }
+  } else {
+    uwatCol = vec3 (0., 0.05, 0.05) +
+       step (0.4, Fbm1 (20. * tCur)) * vec3 (0.02, 0.02, 0.03);
+    col = uwatCol;
+    dstWat = WaveOutRay (ro, rd);
+    idObj = -1;
+    dstHit = ObjRay (ro, rd);
+    if (idObj < 0) dstHit = dstFar;
+    if (dstWat < dstFar && dstWat < dstHit) {
+      ro += rd * dstWat;
+      vn = - WaveNf (ro, dstWat);
+      rdd = refract (rd, vn, 1. / eta);
+      if (length (rdd) > 0.) rd = rdd;
+      else rd = reflect (rd, vn);
+      idObj = -1;
+      dstHit = ObjRay (ro, rd);
+      if (idObj < 0) dstHit = dstFar;
+      if (dstHit < dstFar) col = 0.9 * ObjRender (ro + rd * dstHit, rd);
+      else if (rd.y > 0.) col = mix (uwatCol, 0.9 * SkyGrndCol (ro, rd),
+         exp (- 0.07 * att * dstWat));
+    } else if (dstHit < dstFar) col = mix (uwatCol,
+       ObjRender (ro + rd * dstHit, rd), exp (- 0.07 * att * dstHit));
+  }
+  return col;
+}
+
+void BallPM ()
+{
+  const vec3 e = vec3 (1., 0., 0.);
+  float h[5], b;
+  ballPos = vec3 (0., 0., 0.);
+  h[0] = WaveHt (ballPos);
+  h[1] = WaveHt (ballPos + e.yyx);  h[2] = WaveHt (ballPos - e.yyx);
+  h[3] = WaveHt (ballPos + e);  h[4] = WaveHt (ballPos - e);
+  ballPos.y = 0.5 + (2. * h[0] + h[1] + h[2] + h[3] + h[4]) / 9.;
+  b = (h[1] - h[2]) / (4. * e.x);
+  ballMat[2] = normalize (vec3 (0., b, 1.));
+  b = (h[3] - h[4]) / (4. * e.x);
+  ballMat[1] = normalize (cross (ballMat[2], vec3 (1., b, 0.)));
+  ballMat[0] = cross (ballMat[1], ballMat[2]);
+}
+
+void main()
+{
+  vec2 uv = 2. * gl_FragCoord.xy / iResolution.xy - 1.;
+  uv.x *= iResolution.x / iResolution.y;
+  tCur = elapsedTime;
+  vec4 mPtr = vec4(cameraTargetVec, 0.0);
+  mPtr.xy = mPtr.xy / iResolution.xy - 0.5;
+  //mPtr.xyz = cameraTargetVec;
+  mat3 vuMat;
+  vec3 col, ro, rd;
+  vec2 vEl, vAz;
+  float el, az, zmFac, a, tPer, tSeq;
+  cloudDisp = 10. * tCur * vec3 (1., 0., 1.);
+  waterDisp = 0.5 * tCur * vec3 (-1., 0., 1.);
+  sunDir = normalize (vec3 (0.2, 0.5, 0.5));
+  sunCol = vec3 (1., 0.4, 0.3) + vec3 (0., 0.5, 0.2) * sunDir.y;
+  fCloud = 0.5 + 0.2 * sin (0.022 * 2. * pi * tCur);
+  zmFac = 3.5;
+  tPer = 35.;
+  if (mPtr.z <= 0.) {
+    az = 0.01 * tCur;
+    el = 0.2 * pi;
+    tSeq = mod (tCur, tPer);
+    if (mod (floor (tCur / tPer), 2.) == 0.) {
+      a = SmoothBump (10., 30., 5., tSeq);
+      zmFac -= 0.1 * a;
+      el -= 0.19 * pi * a;
+    } else {
+      a = SmoothBump (8., 26., 8., tSeq);
+      zmFac -= 0.05 * a;
+      el -= 0.55 * pi * a;
+    }
+  } else {
+    az = 1.1 * pi * mPtr.x;
+    el = 0.02 * pi - 0.7 * pi * mPtr.y;
+  }
+  vEl = vec2 (cos (el), sin (el));
+  vAz = vec2 (cos (az), sin (az));
+  rd = normalize (vec3 (uv, zmFac));
+  vuMat = mat3 (1., 0., 0., 0., vEl.x, - vEl.y, 0., vEl.y, vEl.x) *
+     mat3 (vAz.x, 0., vAz.y, 0., 1., 0., - vAz.y, 0., vAz.x);
+  rd = rd * vuMat;
+  ro = vec3 (0., 0., -20.) * vuMat;
+  ro.y += 2.;
+  BallPM ();
+  col = ShowScene (ro, rd);
+  
+  diffuseBuffer = vec4 (sqrt (clamp (col, 0., 1.)), 1.);
+}

+ 48 - 0
Praxis3D/Data/Shaders/clouds.vert

@@ -0,0 +1,48 @@
+#version 330 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+out vec3 viewDir;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+
+void main(void)
+{
+	viewDir = (modelViewMat * vec4(1.0, 0.0, 0.0, 0.0)).xyz;
+	
+
+	vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	eyeDir = normalize(-viewPosition).xyz;
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}
+
+//void main()
+//{
+//	texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+//	texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+//
+//	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+//}

+ 20 - 0
Praxis3D/Data/Shaders/finalPass.frag

@@ -0,0 +1,20 @@
+#version 450 core
+
+out vec4 outputColor;
+
+uniform sampler2D finalColorMap;
+uniform ivec2 screenSize;
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+void main(void)
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+		
+	// Write the color from the final framebuffer to the default framebuffer
+	outputColor = vec4(texture(finalColorMap, texCoord).xyz, 1.0);
+}

+ 10 - 0
Praxis3D/Data/Shaders/finalPass.vert

@@ -0,0 +1,10 @@
+#version 450 core
+
+void main(void) 
+{
+	// Determine texture coordinates
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+}

+ 24 - 0
Praxis3D/Data/Shaders/geomBillboard.frag

@@ -0,0 +1,24 @@
+#version 330 core
+
+struct DirectionalLight
+{
+    vec3 direction;
+};
+
+in	vec2 texCoord;
+out vec4 fragColor;
+
+uniform	DirectionalLight directionalLight;
+uniform sampler2D textureSampler;
+
+void main()
+{
+    fragColor = texture2D(textureSampler, texCoord);
+
+	//if(fragColor.r == 0 && fragColor.g == 0 && fragColor.r == 0)
+
+	if(fragColor.a == 0.0)
+	{
+		discard;
+	}
+}

+ 72 - 0
Praxis3D/Data/Shaders/geomBillboard.geom

@@ -0,0 +1,72 @@
+#version 330 core
+
+layout(points) in;
+layout(triangle_strip) out;
+layout(max_vertices = 4) out;
+
+uniform float billboardScale;
+uniform int depthType;
+uniform mat4 Model;
+uniform mat4 VP;
+
+mat4 modelMat;
+
+out vec2 texCoord;
+
+void main()
+{
+	// Get the directions to which the geometry will be generated
+	vec3 rightVec	= vec3(Model[0].x, Model[1].x, Model[2].x);
+	vec3 upVec		= vec3(Model[0].y, Model[1].y, Model[2].y);
+
+	modelMat[0].x = 1.0;		modelMat[1].y = 1.0;		modelMat[2].z = 1.0;	
+	modelMat[3].x = Model[3].x; modelMat[3].y = Model[3].y; modelMat[3].z = Model[3].z;	modelMat[3].w = Model[3].w;
+
+	// Get the point position
+	vec3 position = gl_in[0].gl_Position.xyz;
+	vec4 endPosition;
+
+	// Offset the geometry, so it is generated from the center
+	position += (rightVec * billboardScale / 2);
+	position -= (upVec * billboardScale / 2);
+
+	position -= (rightVec * billboardScale);
+	endPosition = (VP * modelMat * vec4(position, 1.0)).xyzw;
+	if(depthType == 1)
+		gl_Position = endPosition.xyzw;
+	if(depthType == 2)
+		gl_Position = endPosition.xyww;
+	texCoord = vec2(0.0, 0.0);
+	EmitVertex();
+
+	position += (upVec * billboardScale * 1.1);
+	endPosition = (VP * modelMat * vec4(position, 1.0)).xyzw;
+	if(depthType == 1)
+		gl_Position = endPosition.xyzw;
+	if(depthType == 2)
+		gl_Position = endPosition.xyww;
+	texCoord = vec2(0.0, 1.0);
+	EmitVertex();
+	
+	position -= (upVec * billboardScale * 1.1);
+	position += (rightVec * billboardScale);
+	endPosition = (VP * modelMat * vec4(position, 1.0)).xyzw;
+	if(depthType == 1)
+		gl_Position = endPosition.xyzw;
+	if(depthType == 2)
+		gl_Position = endPosition.xyww;
+	texCoord = vec2(1.0, 0.0);
+	EmitVertex();
+	
+	position += (upVec * billboardScale * 1.1);
+	endPosition = (VP * modelMat * vec4(position, 1.0)).xyzw;
+	if(depthType == 1)
+		gl_Position = endPosition.xyzw;
+	if(depthType == 2)
+		gl_Position = endPosition.xyww;
+	texCoord = vec2(1.0, 1.0);
+	EmitVertex();
+
+	// 'Flush' the geometry
+	EndPrimitive();
+}

+ 8 - 0
Praxis3D/Data/Shaders/geomBillboard.vert

@@ -0,0 +1,8 @@
+#version 330 core
+
+//layout(location = 0) in vec3 position;
+
+void main()
+{
+	
+}

+ 840 - 0
Praxis3D/Data/Shaders/geometryPass.frag

@@ -0,0 +1,840 @@
+#version 330 core
+
+// Some drivers require the following
+//precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 fragPos;
+in vec2 texCoord;
+in vec3 normal;
+in mat3 TBN;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+uniform float alphaThreshold;
+uniform float emissiveThreshold;
+uniform float parallaxHeightScale;
+uniform vec3 cameraPosVec;
+
+vec2 simpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir, float p_height)
+{
+	vec2 parallax = p_viewDir.xy / p_viewDir.z * (p_height * 0.1f);
+    return p_texCoords + parallax; 
+}
+
+vec2 steepParallaxMapping(in vec2 T, in vec3 V)//, out float parallaxHeight)
+{
+   // determine number of layers from angle between V and N
+   const float minLayers = 5;
+   const float maxLayers = 15;
+   float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), V)));
+
+   // height of each layer
+   float layerHeight = 1.0 / numLayers;
+   // depth of current layer
+   float currentLayerHeight = 0;
+   // shift of texture coordinates for each iteration
+   vec2 dtex = 0.1 * V.xy / V.z / numLayers;
+
+   // current texture coordinates
+   vec2 currentTextureCoords = T;
+
+   // get first depth from heightmap
+   float heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+
+   // while point is above surface
+   while(heightFromTexture > currentLayerHeight)
+   {
+      // to the next layer
+      currentLayerHeight += layerHeight;
+      // shift texture coordinates along vector V
+      currentTextureCoords -= dtex;
+      // get new depth from heightmap
+      heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+   }
+
+   // return results
+   //parallaxHeight = currentLayerHeight;
+   return currentTextureCoords;
+} 
+
+vec2 parallaxMapping(in vec2 T, in vec3 V)//, out float parallaxHeight)
+{
+   // determine optimal number of layers
+   const float minLayers = 15;
+   const float maxLayers = 25;
+   float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), V)));
+
+   // height of each layer
+   float layerHeight = 1.0 / numLayers;
+   // current depth of the layer
+   float curLayerHeight = 0.1;
+   // shift of texture coordinates for each layer
+   vec2 dtex = 0.015 * V.xy / V.z / numLayers;
+
+   // current texture coordinates
+   vec2 currentTextureCoords = T;
+
+   // depth from heightmap
+   float heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+
+   // while point is above the surface
+   while(heightFromTexture > curLayerHeight)
+   {
+      // to the next layer
+      curLayerHeight += layerHeight;
+      // shift of texture coordinates
+      currentTextureCoords -= dtex;
+      // new depth from heightmap
+      heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+   }
+
+   ///////////////////////////////////////////////////////////
+
+   // previous texture coordinates
+   vec2 prevTCoords = currentTextureCoords + dtex;
+
+   // heights for linear interpolation
+   float nextH = heightFromTexture - curLayerHeight;
+   float prevH = texture(heightTexture, prevTCoords).r
+                           - curLayerHeight + layerHeight;
+						   
+   // proportions for linear interpolation
+   float weight = nextH / (nextH - prevH);
+
+   // interpolation of texture coordinates
+   vec2 finalTexCoords = prevTCoords * weight + currentTextureCoords * (1.0 - weight);
+
+   // interpolation of depth values
+   //parallaxHeight = curLayerHeight + prevH * weight + nextH * (1.0 - weight);
+
+   // return result
+   return finalTexCoords;
+}
+
+vec2 reliefParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
+{    
+	//float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord + (viewDir.xy * v);
+	
+	// number of depth layers
+    const float minLayers = 10;
+    const float maxLayers = 20;
+    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), p_viewDir)));  
+    // calculate the size of each layer
+    float layerDepth = 1.0 / numLayers;
+    // depth of current layer
+    float currentLayerDepth = 0.0;
+    // the amount to shift the texture coordinates per layer (from vector P)
+    vec2 P = p_viewDir.xy / p_viewDir.z * 0.01;
+    vec2 deltaTexCoords = P / numLayers;
+  
+    // get initial values
+    vec2  currentTexCoords     = p_texCoords;
+    float currentDepthMapValue = texture(heightTexture, currentTexCoords).r;
+		  
+    while(currentLayerDepth < currentDepthMapValue)
+    {		
+        // shift texture coordinates along direction of P
+        currentTexCoords -= deltaTexCoords;
+        // get depthmap value at current texture coordinates
+        currentDepthMapValue = texture(heightTexture, currentTexCoords).r;  
+        // get depth of next layer
+        currentLayerDepth += layerDepth;  
+    }
+    
+	//return currentTexCoords;
+	
+    // -- parallax occlusion mapping interpolation from here on
+    // get texture coordinates before collision (reverse operations)
+    vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
+
+    // get depth after and before collision for linear interpolation
+    float afterDepth  = currentDepthMapValue - currentLayerDepth;
+    float beforeDepth = texture(heightTexture, prevTexCoords).r - currentLayerDepth + layerDepth;
+ 
+    // interpolation of texture coordinates
+    float weight = afterDepth / (afterDepth - beforeDepth);
+    vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
+
+	return finalTexCoords;
+}
+
+vec2 reliefParallaxMapping2(vec2 p_texCoords, vec3 p_viewDir)
+{   
+	// determine required number of layers
+	const float minLayers = 10;
+	const float maxLayers = 15;
+	float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), p_viewDir)));
+
+	// height of each layer
+	float layerHeight = 1.0 / numLayers;
+	// depth of current layer
+	float currentLayerHeight = 0;
+	// shift of texture coordinates for each iteration
+	vec2 dtex = 0.01 * p_viewDir.xy / p_viewDir.z / numLayers;
+
+	// current texture coordinates
+	vec2 currentTextureCoords = p_texCoords;
+
+	// depth from heightmap
+	float heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+
+	// while point is above surface
+	while(heightFromTexture > currentLayerHeight)
+	{
+	  // go to the next layer
+	  currentLayerHeight += layerHeight;
+	  // shift texture coordinates along V
+	  currentTextureCoords -= dtex;
+	  // new depth from heightmap
+	  heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+	}
+
+	///////////////////////////////////////////////////////////
+	// Start of Relief Parallax Mapping
+
+	// decrease shift and height of layer by half
+	vec2 deltaTexCoord = dtex / 2;
+	float deltaHeight = layerHeight / 2;
+
+	// return to the mid point of previous layer
+	currentTextureCoords += deltaTexCoord;
+	currentLayerHeight -= deltaHeight;
+
+	// binary search to increase precision of Steep Paralax Mapping
+	const int numSearches = 5;
+	for(int i = 0; i < numSearches; i++)
+	{
+	  // decrease shift and height of layer by half
+	  deltaTexCoord /= 2;
+	  deltaHeight /= 2;
+
+	  // new depth from heightmap
+	  heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+
+	  // shift along or agains vector V
+	  if(heightFromTexture > currentLayerHeight) // below the surface
+	  {
+		 currentTextureCoords -= deltaTexCoord;
+		 currentLayerHeight += deltaHeight;
+	  }
+	  else // above the surface
+	  {
+		 currentTextureCoords += deltaTexCoord;
+		 currentLayerHeight -= deltaHeight;
+	  }
+	}
+
+	// return results
+	//parallaxHeight = currentLayerHeight;    
+	return currentTextureCoords;
+}
+
+vec2 parallaxOcclusionMapping(vec2 p_texCoords, vec3 p_viewDir)
+{
+	// determine optimal number of layers
+   const float minLayers = 10;
+   const float maxLayers = 15;
+   float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), p_viewDir)));
+
+   // height of each layer
+   float layerHeight = 1.0 / numLayers;
+   // current depth of the layer
+   float curLayerHeight = 0.0;
+   // shift of texture coordinates for each layer
+   vec2 dtex = 0.012 * p_viewDir.xy / p_viewDir.z / numLayers;
+
+   // current texture coordinates
+   vec2 currentTextureCoords = p_texCoords;
+
+   // depth from heightmap
+   float heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+
+   // while point is above the surface
+   while(heightFromTexture > curLayerHeight)
+   {
+      // to the next layer
+      curLayerHeight += layerHeight;
+      // shift of texture coordinates
+      currentTextureCoords -= dtex;
+      // new depth from heightmap
+      heightFromTexture = texture(heightTexture, currentTextureCoords).r;
+   }
+
+   ///////////////////////////////////////////////////////////
+
+   // previous texture coordinates
+   vec2 prevTCoords = currentTextureCoords + dtex;
+
+   // heights for linear interpolation
+   float nextH = heightFromTexture - curLayerHeight;
+   float prevH = texture(heightTexture, prevTCoords).r
+                           - curLayerHeight + layerHeight;
+
+   // proportions for linear interpolation
+   float weight = nextH / (nextH - prevH);
+
+   // interpolation of texture coordinates
+   vec2 finalTexCoords = prevTCoords * weight + currentTextureCoords * (1.0 - weight);
+
+   // interpolation of depth values
+   //parallaxHeight = curLayerHeight + prevH * weight + nextH * (1.0 - weight);
+
+   // return result
+   return finalTexCoords;
+}
+
+vec2 parallaxOcclusionMapping2(vec2 p_texCoords, vec3 p_viewDir)
+{
+	float distanceToFrag = length(fragPos - cameraPosVec);
+	
+	if(distanceToFrag > 29.0)
+		return p_texCoords;
+	
+    // number of depth layers
+    const float minLayers = 10;
+    const float maxLayers = 15;
+    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), p_viewDir)));
+	
+	if(distanceToFrag > 15.0)
+		numLayers = min(((30.0 - distanceToFrag) / 30.0), 1.0) * numLayers;
+		
+    // calculate the size of each layer
+    float layerDepth = 1.0 / numLayers;
+    // depth of current layer
+    float currentLayerDepth = 0.0;
+    // the amount to shift the texture coordinates per layer (from vector P)
+    vec2 P = p_viewDir.xy / p_viewDir.z * 0.02;
+    vec2 deltaTexCoords = P / numLayers;
+  
+    // get initial values
+    vec2  currentTexCoords     = p_texCoords;
+    float currentDepthMapValue = texture(heightTexture, currentTexCoords).r;
+	float previousDepth = currentDepthMapValue;
+      
+    while(currentLayerDepth < currentDepthMapValue)
+    {
+        // shift texture coordinates along direction of P
+        currentTexCoords -= deltaTexCoords;
+        // get depthmap value at current texture coordinates
+        currentDepthMapValue = texture(heightTexture, currentTexCoords).r;  
+		
+		previousDepth = currentDepthMapValue;
+        // get depth of next layer
+        currentLayerDepth += layerDepth;  
+    }
+    
+    // -- parallax occlusion mapping interpolation from here on
+    // get texture coordinates before collision (reverse operations)
+    vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
+
+    // get depth after and before collision for linear interpolation
+    float afterDepth  = currentDepthMapValue - currentLayerDepth;
+    float beforeDepth = texture(heightTexture, prevTexCoords).r - currentLayerDepth + layerDepth;
+    //float beforeDepth = previousDepth - currentLayerDepth + layerDepth;
+ 
+    // interpolation of texture coordinates
+    float weight = afterDepth / (afterDepth - beforeDepth);
+    vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
+
+    return finalTexCoords;
+}
+
+/*vec2 distanceFragment(vec2 p_texCoords, vec3 p_viewDir)
+{
+//(v2fConnector v2f,
+
+  //uniform sampler2D colorTex,
+
+  //uniform sampler2D normalTex,
+
+  //uniform sampler3D distanceTex,
+
+  //uniform float3 normalizationFactor)
+
+//{
+  //f2fConnector f2f;
+
+  // Normalize the offset vector in texture space.
+  // The normalization factor ensures we are normalized with respect
+  // to a distance which is defined in terms of pixels.
+  float3 offset = normalize(p_viewDir);
+  //offset *= normalizationFactor;
+
+  float3 texCoord = p_texCoords;
+
+  // March a ray
+  for(int i = 0; i < 10; i++) 
+  {
+    float dist = texture(heightTexture, texCoord).r;
+    texCoord += dist * offset;
+  }
+
+  // Compute derivatives of unperturbed texcoords.
+  // This is because the offset texcoords will have discontinuities
+  // which lead to incorrect filtering.
+  vec2 dx = dFdx(p_texCoords);
+  vec2 dy = dFdy(p_texCoords);
+
+  // Do bump-mapped lighting in tangent space.
+  // 'normalTex' stores tangent-space normals remapped
+  // into the range [0, 1].
+  vec3 tanNormal = 2 * f3tex2D(normalTex, texCoord.xy, dx, dy) - 1;
+  vec3 tanLightVec = normalize(v2f.tanLightVec);
+  float diffuse = dot(tanNormal, tanLightVec);
+
+  // Multiply diffuse lighting by texture color
+  f2f.COL.rgb = diffuse * f3tex2D(colorTex, texCoord.xy, dx, dy);
+  f2f.COL.a = 1;
+
+  return f2f;
+}*/
+
+vec2 steepParallax(vec2 p_texCoords, vec3 p_viewDir)
+{
+	// We are at height bumpScale.  March forward until we hit a hair or the 
+    // base surface.  Instead of dropping down discrete y-voxels we should be
+    // marching in texels and dropping our y-value accordingly (TODO: fix)
+    float height = 1.0;
+
+    // Number of height divisions
+    float numSteps = 5;
+
+    /** Texture coordinate marched forward to intersection point */
+    vec2 offsetCoord = p_texCoords.xy;
+    vec4 NB = texture2D(heightTexture, offsetCoord);
+
+    vec3 tsE = normalize(p_viewDir);
+
+    // Increase steps at oblique angles
+    // Note: tsE.z = N dot V
+    numSteps = mix(numSteps*2, numSteps, p_viewDir.z);
+
+    // We have to negate tsE because we're walking away from the eye.
+    //vec2 delta = vec2(-_tsE.x, _tsE.y) * bumpScale / (_tsE.z * numSteps);
+    float step;
+    vec2 delta;
+
+
+    // Constant in z
+    step = 1.0 / numSteps;
+    delta = vec2(-p_viewDir.x, p_viewDir.y) * 1.0 / (p_viewDir.z * numSteps);
+
+        // Can also step along constant in xy; the results are essentially
+        // the same in each case.
+        // delta = 1.0 / (25.6 * numSteps) * vec2(-tsE.x, tsE.y);
+        // step = tsE.z * bumpScale * (25.6 * numSteps) / (length(tsE.xy) * 400);
+
+    while (NB.r < height) 
+	{
+        height -= step;
+        offsetCoord += delta;
+        NB = texture2D(heightTexture, offsetCoord);
+    }
+
+    height = NB.r;
+	
+	return offsetCoord;
+	
+    // Choose the color at the location we hit
+    //const vec3 color = texture2D(texture, offsetCoord).rgb;
+/*
+    tsL = normalize(tsL);
+
+    // Use the normals out of the bump map
+    vec3 tsN = NB.xyz * 2 - 1;
+
+    // Smooth normals locally along gradient to avoid making slices visible.
+    // The magnitude of the step should be a function of the resolution and
+    // of the bump scale and number of steps.
+//    tsN = normalize(texture2D(normalBumpMap, offsetCoord + vec2(tsN.x, -tsN.y) * mipScale).xyz * 2 - 1 + tsN);
+
+    const vec3 tsH = normalize(tsL + normalize(_tsE));
+
+    const float NdotL = max(0, dot(tsN, tsL));
+    const float NdotH = max(0, dot(tsN, tsH));
+    float spec = NdotH * NdotH;
+
+    vec3 lightColor = vec3(1.5, 1.5, 1) * 0.9;
+    vec3 ambient = vec3(0.4,0.4,0.6) * 1.4;
+
+    float selfShadow = 0;
+
+    // Don't bother checking for self-shadowing if we're on the
+    // back side of an object.
+    if (NdotL > 0) {
+
+        // Trace a shadow ray along the light vector.
+        const int numShadowSteps = mix(60,5,tsL.z);
+        step = 1.0 / numShadowSteps;
+        delta = vec2(tsL.x, -tsL.y) * bumpScale / (numShadowSteps * tsL.z);
+
+            // We start one iteration out to avoid shadow acne 
+            // (could start bumped a little without going
+            // a whole iteration).
+            height = NB.a + step * 0.1;
+
+            while ((NB.a < height) && (height < 1)) {
+                height += step;
+                offsetCoord += delta;
+                NB = texture2D(normalBumpMap, offsetCoord);
+            }
+
+            // We are in shadow if we left the loop because
+            // we hit a point
+            selfShadow = (NB.a < height);
+
+            // Shadows will make the whole scene darker, so up the light contribution
+            lightColor = lightColor * 1.2;
+        }
+        }
+
+        gl_FragColor.rgb = 
+            color * ambient + color * NdotL * selfShadow * lightColor;*/
+}
+
+vec2 steepParallax2(vec2 p_texCoords, vec3 p_viewDir)
+{
+	/*   // diffuse 
+    uniform sampler2D tex0;
+     //normal map and height map 
+    uniform sampler2D tex1;
+     
+    varying vec3 Normal;
+    varying vec3 Tangent;
+    varying vec3 Binormal;
+     
+    varying vec3 LightDirectionTS;
+    varying vec3 EyeDirectionTS;
+     
+    varying float Depth;
+     
+    vec3 GetNormal( vec2 coord )
+    {
+    vec3 ret = texture2D( tex1, coord ).xyz;
+    ret.xy = ret.xy * 2.0 - 1.0;
+     
+    ret.y *= -1.0;
+    return normalize( ret );
+    }
+     
+    float GetHeight( vec2 coord )
+    {
+    return texture2D( tex1, coord ).w;
+    }
+     
+    void main()
+    {*/
+	
+	float Depth = 1.0;
+	//vec3 LightDirection = normalize( LightDirectionTS );
+	vec3 EyeDirection = normalize( p_viewDir );
+	vec3 EyeRay = p_viewDir;
+	vec2 startCoord = p_texCoords;
+	vec2 newCoord = startCoord;
+ 
+	// Common for Parallax
+	vec2 ParallaxXY = ( EyeRay ).xy/-EyeRay.z * Depth;
+	
+	// Steep Parallax
+	float Step = 0.01;
+	vec2 dt = ParallaxXY * Step;
+	float Height =0.5;
+	float oldHeight = 0.5;
+	vec2 Coord = startCoord;
+	vec2 oldCoord = Coord;
+	float HeightMap = texture2D(heightTexture, Coord).r;
+	float oldHeightMap = HeightMap;
+ 
+	while( HeightMap < Height )
+	{
+		oldHeightMap = HeightMap;
+		oldHeight = Height;
+		oldCoord = Coord;
+ 
+		Height -= Step;
+		Coord += dt;
+		HeightMap = texture2D(heightTexture, Coord).r;
+	}
+	//if( Coord.s <= 0.0 || Coord.t <= 0.0 || Coord.s >= 1.0 || Coord.t >= 1.0 )
+	// discard;
+	Coord = (Coord + oldCoord) * 0.5;
+	if( Height < 0.0 )
+	{
+		Coord = oldCoord;
+		Height = 0.0;
+	}
+	// else // interpolation
+	// {
+	// float ds = (GetHeight( oldCoord ) - oldHeight) * StepXYlength / ( -Step - oldHeightMap + HeightMap );
+	// Coord = oldCoord + dt * ds;
+	// Height = oldHeight + -Step/StepXYlength * ds;
+	// }
+	newCoord = Coord;
+	return Coord;
+ 
+	/*vec4 colorMap = vec4( texture2D( tex0, newCoord ).xyz, 1.0 );
+	float heightMap = GetHeight( newCoord );
+	vec3 normalMap = GetNormal( newCoord );
+ 
+	// Ambient
+	vec4 ambient_color = (gl_FrontLightModelProduct.sceneColor) +
+	(colorMap * gl_LightSource[0].ambient * gl_FrontMaterial.ambient);
+ 
+	// Diffuse
+	float lambertTerm = max( dot( normalMap, LightDirection ), 0.0 );
+	vec4 diffuse_color = colorMap * gl_LightSource[0].diffuse * gl_FrontMaterial.diffuse * lambertTerm;
+ 
+	// Specular
+	vec3 reflectDirection = 2.0 * dot( normalMap, LightDirection ) * normalMap - LightDirection; // in Tangen Space
+ 
+	float specular = pow( max( dot(reflectDirection, EyeDirectionTS), 0.1 ), gl_FrontMaterial.shininess );
+	vec4 specular_color = gl_LightSource[0].specular * gl_FrontMaterial.specular * specular;
+ 
+	// if( newCoord.s < 0.0 || newCoord.t < 0.0 || newCoord.s > 1.0 || newCoord.t > 1.0 )
+	// {
+	// discard;
+	// }
+ 
+	gl_FragColor = vec4( ambient_color.xyz + diffuse_color.xyz + specular_color.xyz, 1.0 );*/
+}
+
+vec2 simpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
+{ 
+    //float height =  texture(heightTexture, p_texCoords).r;     
+    //return p_texCoords - (p_viewDir.xy ) * (height * 0.01);
+	
+	float height =  texture(heightTexture, p_texCoords).r;    
+    vec2 p = p_viewDir.xy  * (height * 0.02);
+    return p_texCoords - p;
+	
+}
+
+void main(void)
+{	
+	// Simple parallax mapping (offset texture coordinates based on height)
+	//float height = texture(heightTexture, texCoord).r;
+	//float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord + (viewDir.xy * v);
+	
+    //vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
+	
+    //vec3 viewDir2 = normalize((TBN * cameraPosVec) - (TBN * fragPos));
+   
+	//vec3 viewDir2 = normalize(TBN * (cameraPosVec - fragPos).xyz);
+	//viewDir2.x = -viewDir2.x;
+	//vec3 viewDir2 = normalize(cameraPosVec - fragPos);
+	//vec3 viewDir2 = normalize(TangentViewPos - TangentFragPos);
+	//vec3 viewDir2 = normalize(viewDir);
+	
+	//vec2 newCoords = parallaxMapping(viewDir, texCoord);
+	//vec2 newCoords = reliefParallaxMapping2(texCoord, viewDir);
+	//vec2 newCoords = texCoord;
+	
+	//vec2 newCoords = simpleParallaxMapping(texCoord, viewDir, texture(heightTexture, texCoord).r);
+	//vec3 et = viewDir;
+	
+	//float h = 0.04 * (1.0 - texture(heightTexture, texCoord).r) + 0.02;
+
+                                                       // now offset texture coordinates
+                                                       // with height
+    //vec2 tex = texCoord - et.xy * h;// / et.z;
+	
+	//float height = texture(heightTexture, texCoord.st).r;
+	//float v = height * 0.04 - 0.02;
+	//tex = texCoord + (viewDir.xy * v);
+	
+	/*const float numSteps  = 10.0;
+
+	//vec3 et = vec3 ( dot ( e, t ), dot ( e, b ), dot ( e, n ) );
+    float step   = 1.0 / numSteps;
+    vec2  dtex   = et.xy * 0.1 / ( numSteps * et.z );  // adjustment for one layer
+    float height = 1.0;                                  // height of the layer
+    vec2  tex    = texCoord.xy;                   // our initial guess
+    float h      = texture2D ( heightTexture, tex ).r;       // get height
+
+    if ( h < height )
+    {
+        height -= step;
+        tex    += dtex;
+        h       = texture2D ( heightTexture, tex ).r;
+
+        if ( h < height )
+        {
+            height -= step;
+            tex    += dtex;
+            h       = texture2D ( heightTexture, tex ).r;
+
+            if ( h < height )
+            {
+                height -= step;
+                tex    += dtex;
+                h       = texture2D ( heightTexture, tex ).r;
+
+                if ( h < height )
+                {
+                    height -= step;
+                    tex    += dtex;
+                    h       = texture2D ( heightTexture, tex ).r;
+
+                    if ( h < height )
+                    {
+                        height -= step;
+                        tex    += dtex;
+                        h       = texture2D ( heightTexture, tex ).r;
+                    }
+                }
+            }
+        }
+    }*/
+	
+	//vec2 newCoords = tex;
+	
+	/*float bumpScale = -10.0;
+     
+    // normalize the other tangent space vectors
+    vec3 viewVector = normalize(TangentViewPos - TangentFragPos);
+     
+    vec3 tsE = normalize(TangentViewPos);
+ 
+    const float numSteps = 30.0; // How many steps the UV ray tracing should take
+    float height = 1.0;
+    float step = 1.0 / numSteps;
+ 
+    vec2 offset = texCoord;
+    vec4 NB = texture2D(heightTexture, offset);
+ 
+    vec2 delta = vec2(tsE.x, tsE.y) * bumpScale / (tsE.z * numSteps);
+ 
+    // find UV offset
+    for (float i = 0.0; i < numSteps; i++) 
+	{
+        if (NB.a < height) 
+		{
+            height -= step;
+            offset += delta;
+            NB = texture2D(heightTexture, offset);
+        } 
+		else 
+		{
+            break;
+        }
+    }
+	
+	vec2 newCoords = offset;*/
+	
+    /*vec3 color = texture2D(base, offset).rgb;
+     
+     
+        vec3 normal = texture2D(map, offset).rgb * 2.0 - 1.0;
+ 
+    // calculate this pixel's lighting
+        float nxDir = max(0.0, dot(normal, lightVector));
+        vec3 ambient = ambientColor * color;
+ 
+        float specularPower = 0.0;
+        if(nxDir != 0.0)
+        {
+                vec3 halfVector = normalize(lightVector + viewVector);
+                float nxHalf = max(0.0, dot(normal, halfVector));
+                specularPower = pow(nxHalf, shininess);
+        }
+        vec3 specular = specularColor * specularPower;
+     
+     
+        vec3 pixel_color = ambient + (diffuseColor * nxDir * color) + specular;
+     
+    // find shadowing if enabled
+        if (shadow == 1.0) {
+            vec2 shadow_offset = offset;
+        vec3 tsH = normalize(lightVector + tsE);
+        float NdotL = max(0.0, dot(normal, lightVector));
+         
+        float selfShadow = 0.0;
+         
+        if (NdotL > 0.0) {
+             
+            const float numShadowSteps = 10.0;
+            step = 1.0 / numShadowSteps;
+            delta = vec2(lightVector.x, lightVector.y) * bumpScale / (numShadowSteps * lightVector.z);
+             
+            height = NB.a + step * .1;
+             
+            for (float i = 0.0; i < numShadowSteps; i++) {
+                if (NB.a < height && height < 1.0) {
+                    height += step;
+                    shadow_offset += delta;
+                    NB = texture2D(map, shadow_offset);
+                } else {
+                    break;
+                }
+            }
+             
+            selfShadow = float(NB.a < height);
+             
+        }
+         
+        if (selfShadow == 0.0) {
+        pixel_color *= .5;
+        }
+    }
+     
+    gl_FragColor = vec4(pixel_color, 1.0);
+     
+    if (offset.x < 0.0 || offset.x > 1.0 || offset.y < 0.0 || offset.y > 1.0) {
+        gl_FragColor.a = 0.0;
+    }*/
+	
+	vec3 viewDir = normalize(TBN * (cameraPosVec - fragPos).xyz * vec3(-1.0, 1.0, 1.0));
+	vec2 newCoords = texCoord;
+	
+	//if(distanceToFrag < 30.0)
+	newCoords = parallaxOcclusionMapping2(texCoord, viewDir);
+	//vec2 newCoords = texCoord;
+	
+	// Get diffuse color
+	vec4 diffuse = texture(diffuseTexture, newCoords).rgba;
+	
+	// Discard fragment if the diffuse alpha color value is smaller than alpha threshold
+	if(diffuse.a < alphaThreshold)
+		discard;
+	
+	// Get gloss and specular values and emissive color
+	//float gloss = texture(glossTexture, newCoords).r;
+	float gloss = texture(specularTexture, newCoords).g;
+	float specular = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	
+	// Apply emissive color only if it's above the threshold
+	if(emissiveColor.a > emissiveThreshold)
+	{
+		// Use emissive alpha channel as an intensity multiplier
+		emissiveColor *= emissiveColor.a;
+	}
+	else
+	{
+		emissiveColor = vec4(0.0);
+	}
+	
+	// Write diffuse color to the diffuse buffer
+	diffuseBuffer = diffuse;
+	// Write emissive color into the emissive buffer
+	emissiveBuffer = emissiveColor;
+	// Write fragment's position in world space	to the position buffer, write gloss value in alpha channel
+	positionBuffer = vec4(fragPos, gloss);
+	// Perform normal mapping and write the new normal to the normal buffer, write specular value in alpha channel
+	normalBuffer = vec4(TBN * normalize(texture(normalTexture, newCoords).rgb * 2.0 - 1.0), specular);
+	//normalBuffer = vec4(normalize(normal), specular);
+}

+ 36 - 0
Praxis3D/Data/Shaders/geometryPass.vert

@@ -0,0 +1,36 @@
+#version 330 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 fragPos;
+out vec2 texCoord;
+out vec3 normal;
+out mat3 TBN;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+uniform float textureTilingFactor;
+
+void main(void)
+{		
+	// Multiply position and normal by model matrix (to convert them into world space)
+    fragPos = vec3(modelMat * vec4(vertexPosition, 1.0));
+	normal = normalize(vec3(modelMat * vec4(vertexNormal, 0.0)));
+	
+	// Multiply texture coordinates by the tiling factor. The higher the factor, the denser the tiling
+	texCoord = textureCoord * textureTilingFactor;
+	
+	// Compute TBN matrix (method 1)
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+	
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}

+ 381 - 0
Praxis3D/Data/Shaders/geometryPassInf.frag

@@ -0,0 +1,381 @@
+#version 450 core
+
+#define HEIGHT_SCALE_THRESHOLD 0.001
+
+// Some drivers require the following
+//precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in mat3 TBN;
+in vec3 fragPos;
+in vec3 normal;
+in vec3 tangentFragPos;
+in vec3 tangentCameraPos;
+in float parallaxScale;
+
+/* Current combined texture channels:
+	Red: Roughness
+	Green: Metalness
+	Blue: Height
+	Alpha: Ambient Occlusion
+*/
+uniform sampler2D combinedTexture;
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D emissiveTexture;
+
+uniform float alphaThreshold;
+uniform float emissiveThreshold;
+uniform float parallaxHeightScale;
+uniform float textureTilingFactor;
+
+/*
+vec2 steepParallaxMapping(vec2 texCoords, vec3 viewDir)
+{ 
+    // number of depth layers
+    const float minLayers = 10;
+    const float maxLayers = 20;
+    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));  
+    // calculate the size of each layer
+    float layerDepth = 1.0 / numLayers;
+    // depth of current layer
+    float currentLayerDepth = 0.0;
+    // the amount to shift the texture coordinates per layer (from vector P)
+    vec2 P = viewDir.xy / viewDir.z * height_scale; 
+    vec2 deltaTexCoords = P / numLayers;
+  
+    // get initial values
+    vec2  currentTexCoords     = texCoords;
+    float currentDepthMapValue = texture(depthMap, currentTexCoords).r;
+      
+    while(currentLayerDepth < currentDepthMapValue)
+    {
+        // shift texture coordinates along direction of P
+        currentTexCoords -= deltaTexCoords;
+        // get depthmap value at current texture coordinates
+        currentDepthMapValue = texture(depthMap, currentTexCoords).r;  
+        // get depth of next layer
+        currentLayerDepth += layerDepth;  
+    }
+    
+    return currentTexCoords;
+}*/
+
+float getHeight(const vec2 p_texCoords)
+{
+	return texture(combinedTexture, p_texCoords).b;
+}
+float getHeight(const vec2 p_texCoords, const vec2 p_dPdx, const vec2 p_dPdy)
+{
+	return textureGrad(combinedTexture, p_texCoords, p_dPdx , p_dPdy).b;
+}
+
+float getRoughness(const vec2 p_texCoords)
+{
+	return texture(combinedTexture, p_texCoords).r;
+}
+float getMetalness(const vec2 p_texCoords)
+{
+	return texture(combinedTexture, p_texCoords).g;
+}
+float getAmbientOcclusion(const vec2 p_texCoords)
+{
+	return texture(combinedTexture, p_texCoords).a;
+}
+
+vec2 reliefParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
+{   
+	// determine required number of layers
+	const float minLayers = 10;
+	const float maxLayers = 15;
+	float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), p_viewDir)));
+
+	// height of each layer
+	float layerHeight = 1.0 / numLayers;
+	// depth of current layer
+	float currentLayerHeight = 0;
+	// shift of texture coordinates for each iteration
+	vec2 dtex = 0.01 * p_viewDir.xy / p_viewDir.z / numLayers;
+
+	// current texture coordinates
+	vec2 currentTextureCoords = p_texCoords;
+
+	// depth from heightmap
+	float heightFromTexture = getHeight(currentTextureCoords);
+
+	// while point is above surface
+	while(heightFromTexture > currentLayerHeight)
+	{
+	  // go to the next layer
+	  currentLayerHeight += layerHeight;
+	  // shift texture coordinates along V
+	  currentTextureCoords -= dtex;
+	  // new depth from heightmap
+	  heightFromTexture = getHeight(currentTextureCoords);
+	}
+
+	///////////////////////////////////////////////////////////
+	// Start of Relief Parallax Mapping
+
+	// decrease shift and height of layer by half
+	vec2 deltaTexCoord = dtex / 2;
+	float deltaHeight = layerHeight / 2;
+
+	// return to the mid point of previous layer
+	currentTextureCoords += deltaTexCoord;
+	currentLayerHeight -= deltaHeight;
+
+	// binary search to increase precision of Steep Paralax Mapping
+	const int numSearches = 5;
+	for(int i = 0; i < numSearches; i++)
+	{
+	  // decrease shift and height of layer by half
+	  deltaTexCoord /= 2;
+	  deltaHeight /= 2;
+
+	  // new depth from heightmap
+	  heightFromTexture = getHeight(currentTextureCoords);
+
+	  // shift along or agains vector V
+	  if(heightFromTexture > currentLayerHeight) // below the surface
+	  {
+		 currentTextureCoords -= deltaTexCoord;
+		 currentLayerHeight += deltaHeight;
+	  }
+	  else // above the surface
+	  {
+		 currentTextureCoords += deltaTexCoord;
+		 currentLayerHeight -= deltaHeight;
+	  }
+	}
+
+	return currentTextureCoords;
+}
+
+
+	//float distanceToFrag = length(fragPos - cameraPosVec);
+	
+	//if(distanceToFrag > 29.0)
+	//	return p_texCoords;
+	
+    // number of depth layers
+	
+	//if(distanceToFrag > 5.0)
+	//	numLayers = min(((20.0 - distanceToFrag) / 20.0), 1.0) * numLayers;
+		
+
+vec2 parallaxMappingNew(vec2 T, vec3 V)
+{
+	vec2 finalTexCoords = T;
+
+	// Calculate the parallax offset vector max length.
+	// This is equivalent to the tangent of the angle between the
+	// viewer position and the fragment location.
+	float fParallaxLimit = -length( V.xy ) / V.z;
+
+	// Scale the parallax limit according to heightmap scale.
+	fParallaxLimit *= 0.04f;						
+
+	// Calculate the parallax offset vector direction and maximum offset.
+	vec2 vOffsetDir = normalize( V.xy );
+	vec2 vMaxOffset = vOffsetDir * fParallaxLimit;
+
+	// Calculate the geometric surface normal vector, the vector from
+	// the viewer to the fragment, and the vector from the fragment
+	// to the light.
+	vec3 N = normalize( normal );
+	vec3 E = normalize( V );
+
+	// Calculate how many samples should be taken along the view ray
+	// to find the surface intersection.  This is based on the angle
+	// between the surface normal and the view vector.
+	float nNumSamples2 = mix( 20, 10, dot( E, N ) );
+	int nNumSamples = int(nNumSamples2);
+	
+	// Specify the view ray step size.  Each sample will shift the current
+	// view ray by this amount.
+	float fStepSize = 1.0 / nNumSamples;
+
+	// Calculate the texture coordinate partial derivatives in screen
+	// space for the tex2Dgrad texture sampling instruction.
+	vec2 dx = dFdx( T );
+	vec2 dy = dFdy( T );
+
+	// Initialize the starting view ray height and the texture offsets.
+	float fCurrRayHeight = 1.0;	
+	vec2 vCurrOffset = vec2( 0, 0 );
+	vec2 vLastOffset = vec2( 0, 0 );
+
+	float fLastSampledHeight = 1;
+	float fCurrSampledHeight = 1;
+
+	int nCurrSample = 0;
+
+	while ( nCurrSample < nNumSamples )
+	{
+		// Sample the heightmap at the current texcoord offset.  The heightmap 
+		// is stored in the alpha channel of the height/normal map.
+		//fCurrSampledHeight = tex2Dgrad( NH_Sampler, IN.texcoord + vCurrOffset, dx, dy ).a;
+		fCurrSampledHeight = getHeight(T + vCurrOffset, dx, dy); //NormalHeightMap.SampleGrad( LinearSampler, IN.texcoord + vCurrOffset, dx, dy ).a;
+
+		// Test if the view ray has intersected the surface.
+		if ( fCurrSampledHeight > fCurrRayHeight )
+		{
+			// Find the relative height delta before and after the intersection.
+			// This provides a measure of how close the intersection is to 
+			// the final sample location.
+			float delta1 = fCurrSampledHeight - fCurrRayHeight;
+			float delta2 = ( fCurrRayHeight + fStepSize ) - fLastSampledHeight;
+			float ratio = delta1/(delta1+delta2);
+
+			// Interpolate between the final two segments to 
+			// find the true intersection point offset.
+			vCurrOffset = (ratio) * vLastOffset + (1.0-ratio) * vCurrOffset;
+			
+			// Force the exit of the while loop
+			nCurrSample = nNumSamples + 1;	
+		}
+		else
+		{
+			// The intersection was not found.  Now set up the loop for the next
+			// iteration by incrementing the sample count,
+			nCurrSample++;
+
+			// take the next view ray height step,
+			fCurrRayHeight -= fStepSize;
+			
+			// save the current texture coordinate offset and increment
+			// to the next sample location, 
+			vLastOffset = vCurrOffset;
+			vCurrOffset += fStepSize * vMaxOffset;
+
+			// and finally save the current heightmap height.
+			fLastSampledHeight = fCurrSampledHeight;
+		}
+	}
+
+	// Calculate the final texture coordinate at the intersection point.
+	vec2 vFinalCoords = T + vCurrOffset;
+
+	// Use the final texture coordinates to get the normal vector, then 
+	// expand it from [0,1] to [-1,1] range.
+	//vec4 vFinalNormal = NormalHeightMap.Sample( LinearSampler, vFinalCoords ); //.a;
+		
+	// Sample the colormap at the final intersection point.
+	//vec4 vFinalColor = ColorMap.Sample( LinearSampler, vFinalCoords );
+
+	// Expand the final normal vector from [0,1] to [-1,1] range.
+	//vFinalNormal = vFinalNormal * 2.0f - 1.0f;
+
+	// Shade the fragment based on light direction and normal.
+	//vec3 vAmbient = vFinalColor.rgb * 0.1f;
+	//vec3 vDiffuse = vFinalColor.rgb * max( 0.0f, dot( L, vFinalNormal.xyz ) ) * 0.5f;
+	//vFinalColor.rgb = vAmbient + vDiffuse;
+
+	//OUT.color = vFinalColor;
+
+	return vFinalCoords;
+}
+
+vec2 parallaxOcclusionMapping(vec2 texCoords, vec3 viewDir)
+{
+	// number of depth layers
+    const float minLayers = 15;
+    const float maxLayers = 20;
+    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));  
+    // calculate the size of each layer
+    float layerDepth = 1.0 / numLayers;
+    // depth of current layer
+    float currentLayerDepth = 0.0;
+    // the amount to shift the texture coordinates per layer (from vector P)
+    vec2 P = viewDir.xy / viewDir.z * 0.02; 
+    vec2 deltaTexCoords = P / numLayers;
+  
+    // get initial values
+    vec2  currentTexCoords     = texCoords;
+    float currentDepthMapValue = getHeight(currentTexCoords);
+      
+    while(currentLayerDepth < currentDepthMapValue)
+    {
+        // shift texture coordinates along direction of P
+        currentTexCoords -= deltaTexCoords;
+        // get depthmap value at current texture coordinates
+        currentDepthMapValue = getHeight(currentTexCoords);  
+        // get depth of next layer
+        currentLayerDepth += layerDepth;  
+    }
+    
+    // -- parallax occlusion mapping interpolation from here on
+    // get texture coordinates before collision (reverse operations)
+    vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
+
+    // get depth after and before collision for linear interpolation
+    float afterDepth  = currentDepthMapValue - currentLayerDepth;
+    float beforeDepth = getHeight(prevTexCoords) - currentLayerDepth + layerDepth;
+ 
+    // interpolation of texture coordinates
+    float weight = afterDepth / (afterDepth - beforeDepth);
+    vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
+
+    return finalTexCoords;
+}
+
+vec2 simpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
+{ 
+    //float height =  texture(heightTexture, p_texCoords).r;     
+    //return p_texCoords - (p_viewDir.xy ) * (height * 0.01);
+	
+	float height =  getHeight(p_texCoords);    
+    vec2 p = p_viewDir.xy  * (height * 0.02);
+    return p_texCoords - p;
+}
+
+void main(void)
+{		
+	vec3 viewDir = normalize(tangentCameraPos - tangentFragPos);
+	vec2 newCoords = fragPos.xz / textureTilingFactor;
+	
+	//float distanceToFrag = length(fragPos - cameraPosVec);
+	
+	//if(distanceToFrag < 19.0)
+		//newCoords = parallaxOcclusionMapping2(newCoords, viewDir);
+	//vec2 newCoords = texCoord;
+	
+	// Get diffuse color
+	vec4 diffuse = texture(diffuseTexture, newCoords).rgba;
+	
+	// Discard fragment if the diffuse alpha color value is smaller than alpha threshold
+	//if(diffuse.a < alphaThreshold)
+	//	discard;
+	
+	// Get roughness and metalness values, and emissive color
+	float roughness = getRoughness(newCoords);
+	float metalness = getMetalness(newCoords);
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	
+	// Apply emissive color only if it's above the threshold
+	if(emissiveColor.a > emissiveThreshold)
+	{
+		// Use emissive alpha channel as an intensity multiplier
+		emissiveColor *= emissiveColor.a;
+	}
+	else
+	{
+		emissiveColor = vec4(0.0);
+	}
+		
+	// Write diffuse color to the diffuse buffer
+	diffuseBuffer = diffuse;
+	// Write emissive color into the emissive buffer
+	emissiveBuffer = emissiveColor;
+	// Write fragment's position in world space	to the position buffer, write roughness value in alpha channel
+	positionBuffer = vec4(fragPos, roughness);
+	// Perform normal mapping and write the new normal to the normal buffer, write metalness value in alpha channel
+	normalBuffer = vec4(TBN * normalize(texture(normalTexture, newCoords).rgb * 2.0 - 1.0), metalness);
+}

+ 41 - 0
Praxis3D/Data/Shaders/geometryPassInf.vert

@@ -0,0 +1,41 @@
+#version 450 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 fragPos;
+out vec3 normal;
+out vec3 tangentFragPos;
+out vec3 tangentCameraPos;
+out mat3 TBN;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 viewMat;
+uniform mat4 projMat;
+uniform vec3 cameraPosVec;
+
+void main(void)
+{		
+	// Multiply position and normal by model matrix (to convert them into world space)
+    fragPos = vec3(modelMat * vec4(vertexPosition, 1.0));
+	normal = normalize(mat3(modelMat) * vertexNormal);
+				
+	// Compute TBN matrix
+    vec3 T = normalize(mat3(modelMat) * vertexTangent);
+    vec3 B = normalize(mat3(modelMat) * vertexBitangent);
+	
+	TBN = transpose(inverse(mat3(T, B, normal)));
+	mat3 TBN2 = transpose((mat3(T, B, normal)));
+	
+	tangentCameraPos = TBN2 * cameraPosVec;
+	tangentFragPos = TBN2 * fragPos;
+		
+	gl_Position = projMat * viewMat * vec4(fragPos, 1.0);
+}

+ 57 - 0
Praxis3D/Data/Shaders/geometryPassTest.frag

@@ -0,0 +1,57 @@
+#version 330 core
+
+// Some drivers require the following
+precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+void main(void)
+{
+	//emissiveBuffer	= normal;
+
+	//vec4 emissiveTexture = texture(emissiveTexture, texCoord).rgba;
+	//if(emissiveTexture.a < 0.5)
+	//{
+	//	emissiveTexture = vec4(0.0);
+	//}
+	//emissiveBuffer	= emissiveTexture * emissiveTexture.a;
+	//diffuseBuffer	= texture(diffuseTexture, texCoord).rgba;
+	//texCoordBuffer	= vec4(texCoord, 0.0, 0.0);
+	//positionBuffer	= vec4(worldPos, 0.0);
+	//normalBuffer	= vec4(normalize(normal), 0.0);
+	
+	float height = texture(heightTexture, texCoord.st).r;
+	float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord+(eyeDir.xy*v);
+	vec2 newCoords = texCoord;
+	
+	float specularPower = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	if(emissiveColor.a < 0.3)
+	{
+		emissiveColor = vec4(0.0);
+	}
+	emissiveBuffer	= emissiveColor * emissiveColor.a;
+
+	diffuseBuffer	= texture(diffuseTexture, newCoords).rgba;	// Write the color from model's texture
+	positionBuffer	= vec4(worldPos, 0.0);						// Write fragment's position in world space
+	normalBuffer = vec4(TBN * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), specularPower);
+}

+ 35 - 0
Praxis3D/Data/Shaders/geometryPassTest.vert

@@ -0,0 +1,35 @@
+#version 330 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+
+void main(void)
+{
+	vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	eyeDir = normalize(-viewPosition).xyz;
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}

+ 147 - 0
Praxis3D/Data/Shaders/lightPass 2.frag

@@ -0,0 +1,147 @@
+#version 330
+
+#define MAX_NUM_POINT_LIGHTS 20
+#define MAX_NUM_SPOT_LIGHTS 10
+
+layout(location = 0) out vec4 emissiveBuffer;
+layout(location = 1) out vec4 finalBuffer;
+
+//in vec2 texCoord;
+
+uniform sampler2D positionMap;
+uniform sampler2D diffuseMap;
+uniform sampler2D normalMap;
+
+uniform ivec2 screenSize;
+
+uniform mat4 modelViewMat;
+uniform mat4 viewMat;
+uniform vec3 cameraPosVec;
+
+struct DirectionalLight
+{
+    vec3 m_color;
+    vec3 m_direction;
+    float m_intensity;
+};
+
+struct PointLight
+{
+    vec3 m_color;
+    vec3 m_position;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+    float m_intensity;
+};
+struct SpotLight
+{
+    vec3 m_color;
+    vec3 m_position;
+    vec3 m_direction;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+	
+    float m_intensity;
+    float m_cutoffAngle;
+};
+
+// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
+// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
+layout (std140) uniform PointLights
+{
+	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
+};
+layout (std140) uniform SpotLights
+{
+	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
+};
+
+uniform DirectionalLight directionalLight;
+	
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+    //return gl_FragCoord.xy / vec2(1600, 900);
+}
+
+void main(void) 
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	vec3 varNormal = texture(normalMap, texCoord).xyz;
+	
+	//vec3 varEyeDir = vec3(viewMat[2].xyz);
+	vec3 worldPos = texture(positionMap, texCoord).xyz;
+	
+//vec3 varEyeDir = normalize(worldPos - cameraPosVec);
+	vec3 varEyeDir = normalize(cameraPosVec - worldPos);
+	//vec3 varEyeDir = vec3(viewMat[3].xyz);
+	//vec3 varEyeDir = vec4(vec4(1.0, 0.0, 0.0, 1.0) * viewMat).xyz;
+	
+	//vec3 lightDirection = normalize(vec3(0.5, 0.0, 0.5));
+	//vec3 lightDirection = normalize(vec3(0.0, 1.0, 0.5));
+	vec3 lightDirection = directionalLight.m_direction;
+	
+	// set important material values
+    float roughnessValue = 0.3; // 0 : smooth, 1: rough
+    float F0 = 1.8; // fresnel reflectance at normal incidence
+    float k = 0.01; // fraction of diffuse reflection (specular reflection = 1 - k)
+    //vec3 lightColor = directionalLight.m_color;
+	vec3 lightColor = spotLights[1].m_color;
+    
+    // interpolating normals will change the length of the normal, so renormalize the normal.
+    vec3 normal = normalize(varNormal);
+    
+    // do the lighting calculation for each fragment.
+    float NdotL = max(dot(normal, lightDirection), 0.0);
+    
+    float specular = 0.0;
+    if(NdotL > 0.0)
+    {
+        vec3 eyeDir = normalize(varEyeDir);
+
+        // calculate intermediary values
+        vec3 halfVector = normalize(lightDirection + eyeDir);
+        float NdotH = max(dot(normal, halfVector), 0.0); 
+        float NdotV = max(dot(normal, eyeDir), 0.0); // note: this could also be NdotL, which is the same value
+        float VdotH = max(dot(eyeDir, halfVector), 0.0);
+        float mSquared = roughnessValue * roughnessValue;
+        
+        // geometric attenuation
+        float NH2 = 2.0 * NdotH;
+        float g1 = (NH2 * NdotV) / VdotH;
+        float g2 = (NH2 * NdotL) / VdotH;
+        float geoAtt = min(1.0, min(g1, g2));
+     
+        // roughness (or: microfacet distribution function)
+        // beckmann distribution function
+        float r1 = 1.0 / ( 4.0 * mSquared * pow(NdotH, 4.0));
+        float r2 = (NdotH * NdotH - 1.0) / (mSquared * NdotH * NdotH);
+        float roughness = r1 * exp(r2);
+        
+        // fresnel
+        // Schlick approximation
+        float fresnel = pow(1.0 - VdotH, 5.0);
+        fresnel *= (1.0 - F0);
+        fresnel += F0;
+        
+        specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * 3.14);
+    }
+
+	vec3 color = pow(texture(diffuseMap, texCoord).xyz, vec3(2.2));
+    vec3 finalValue = lightColor * NdotL * (k + specular * (1.0 - k));
+	
+	finalBuffer = vec4(pow(color * finalValue, vec3(1.0 / 2.2)), 1.0);
+	//finalBuffer = vec4(directionalLight.m_color, 1.0);
+	//finalBuffer = vec4(normalize(lightDirection + varEyeDir), 1.0);
+
+	// Get diffuse color (full-bright) from diffuse buffer
+	//vec3 color = texture(diffuseMap, texCoord).xyz;
+
+	//finalBuffer = vec4(pow(color, vec3(1.0 / 2.2)), 1.0);
+}

+ 597 - 0
Praxis3D/Data/Shaders/lightPass.frag

@@ -0,0 +1,597 @@
+#version 330
+
+#define MAX_NUM_POINT_LIGHTS 20
+#define MAX_NUM_SPOT_LIGHTS 10
+
+layout(location = 0) out vec4 emissiveBuffer;
+layout(location = 1) out vec4 finalBuffer;
+
+//in vec2 texCoord;
+
+struct DirectionalLight
+{
+    vec3 m_color;
+    vec3 m_direction;
+    float m_intensity;
+};
+
+struct PointLight
+{
+    vec3 m_color;
+    vec3 m_position;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+    float m_intensity;
+};
+struct SpotLight
+{
+    vec3 m_color;
+    vec3 m_position;
+    vec3 m_direction;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+	
+    float m_intensity;
+    float m_cutoffAngle;
+};
+
+uniform sampler2D positionMap;
+uniform sampler2D diffuseMap;
+uniform sampler2D normalMap;
+
+uniform ivec2 screenSize;
+
+uniform mat4 modelViewMat;
+uniform mat4 viewMat;
+uniform vec3 cameraPosVec;
+
+uniform int numPointLights;
+uniform int numSpotLights;
+
+uniform DirectionalLight directionalLight;
+
+// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
+// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
+layout (std140) uniform PointLights
+{
+	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
+};
+layout (std140) uniform SpotLights
+{
+	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
+};
+
+vec3 worldPos;
+vec3 normal;
+vec3 fragmentToEye;
+float roughnessSqrt;
+
+// set important material values
+float roughnessVar = 0.1; // 0.1	// 0 : smooth, 1: rough
+//float roughnessVar = 0.8;
+float F0 = 0.02;//0.171968833;//1.0; 			// fresnel reflectance at normal incidence
+vec3 F0vec;
+//float k = 0.01; 				// fraction of diffuse reflection (specular reflection = 1 - k)
+float k = 0.8; //0.5
+float metallic;
+
+float ref_at_norm_incidence = F0;
+vec3 viewer;
+
+float saturate(float p_value)
+{
+	return clamp(p_value, 0.0f, 1.0f);
+}
+
+float G1V(float p_dotNV, float p_k)
+{
+    return 1.0f / (p_dotNV * (1.0f - p_k) + p_k);
+}
+
+float MicroFacetDistr_GGX(vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
+{
+	float NdotH = dot(p_normal, p_halfVec);
+	if(NdotH > 0.0)
+	{
+		float NdotHSqrt = NdotH * NdotH;
+		float microfacetDstrb = NdotHSqrt * p_roughnessSqrt + (1.0 - NdotHSqrt);
+		return (NdotH * p_roughnessSqrt) / (3.14 * microfacetDstrb * microfacetDstrb);
+	}
+	else
+		return 0.0;
+}
+
+float GeometryAtten_GGX(vec3 p_fragToEye, vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
+{
+    float VdotH = clamp(dot(p_fragToEye, p_halfVec), 0.0, 1.0);
+    float VdotN = clamp(dot(p_fragToEye, p_normal), 0.0, 1.0);
+	
+	float geoFactor = (VdotH / VdotN);
+	if(geoFactor > 0.0)
+	{
+		float VdotHSqrt = VdotH * VdotH;
+		float geoAtt = (1.0 - VdotHSqrt) / VdotHSqrt;
+		return 2.0 / (1.0 + sqrt(1.0 + p_roughnessSqrt * geoAtt));
+	}
+	else
+		return 0.0;
+}
+
+vec3 Fresnel_Schlick(float p_cosT, vec3 p_F0)
+{
+	return F0 + (vec3(1.0) - F0) * pow(1.0 - p_cosT, 5.0);
+}
+
+vec3 calcLightColor2(vec3 p_lightColor, vec3 p_lightDirection)
+{
+	//float NoV = saturate( dot(normal, V) );
+	float NoV = abs( dot(normal, fragmentToEye) ) + 0.00001;//1e-5;
+
+	// Diffuse_Lambert
+	//Shared.DiffuseMul = DiffuseColor * (1.0 / 3.14);
+
+	// D_GGX, Vis_SmithJointApprox
+	float m = roughnessVar * roughnessVar;
+	float m2 = m * m;
+	float SpecularMul = (0.5 / 3.14) * m2;
+	float VisMad = ( 2 * NoV * ( 1 - m ) + m, NoV * m );
+	
+	// F_Schlick
+	//SpecularMul *= saturate( 50.0 * k );
+	return vec3(1.0);
+}
+
+vec3 calcLightColor3(vec3 p_lightColor, vec3 p_lightDirection)
+{
+	// Compute any aliases and intermediary values
+    // -------------------------------------------
+	
+	vec3 half_vector = normalize(fragmentToEye - p_lightDirection );
+    float NdotL        = clamp( dot( normal, -p_lightDirection ), 0.0, 1.0 );
+    float NdotH        = clamp( dot( normal, half_vector ), 0.0, 1.0 );
+    float NdotV        = clamp( dot( normal, viewer ), 0.0, 1.0 );
+    float VdotH        = clamp( dot( viewer, half_vector ), 0.0, 1.0 );
+    float r_sq         = roughnessVar * roughnessVar;
+ 
+    // Evaluate the geometric term
+    // --------------------------------
+    float geo_numerator   = 2.0f * NdotH;
+    float geo_denominator = VdotH;
+ 
+    float geo_b = (geo_numerator * NdotV ) / geo_denominator;
+    float geo_c = (geo_numerator * NdotL ) / geo_denominator;
+    float geo   = min( 1.0f, min( geo_b, geo_c ) );
+ 
+    // Now evaluate the roughness term
+    // -------------------------------
+    float roughness;
+    
+	float roughness_a = 1.0f / ( 4.0f * r_sq * pow( NdotH, 4 ) );
+	float roughness_b = NdotH * NdotH - 1.0f;
+	float roughness_c = r_sq * NdotH * NdotH;
+	roughness = roughness_a * exp( roughness_b / roughness_c );
+ 
+    // Next evaluate the Fresnel value
+    // -------------------------------
+    float fresnel = pow( 1.0f - VdotH, 5.0f );
+    fresnel *= ( 1.0f - F0 );
+    fresnel += F0;
+ 
+    // Put all the terms together to compute
+    // the specular term in the equation
+    // -------------------------------------
+    float Rs_numerator   = ( fresnel * geo * roughness );
+    float Rs_denominator  = NdotV * NdotL;
+    float Rs             = Rs_numerator/ Rs_denominator;
+  
+    float specular = (fresnel * geo * roughness) / (NdotV * NdotL);
+    // Put all the parts together to generate
+    // the final colour
+    // --------------------------------------
+	
+	return p_lightColor * NdotL * specular;
+	
+    //float3 final = max(0.0f, NdotL) * (cSpecular * Rs + cDiffuse);
+ 
+    // Return the result
+    // -----------------
+    //return float4( final, 1.0f );
+}
+
+vec3 computePBRLighting(vec3 lightPos, vec3 lightColor, vec3 position, vec3 N, vec3 V, vec3 albedo, float roughness, vec3 F0) 
+{
+
+	float alpha = roughness*roughness;
+	vec3 L = normalize(lightPos - position);
+	vec3 H = normalize (V + L);
+
+	float dotNL = clamp (dot (N, L), 0.0, 1.0);
+	float dotNV = clamp (dot (N, V), 0.0, 1.0);
+	float dotNH = clamp (dot (N, H), 0.0, 1.0);
+	float dotLH = clamp (dot (L, H), 0.0, 1.0);
+
+	float D, vis;
+	vec3 F;
+
+	// NDF : GGX
+	float alphaSqr = alpha*alpha;
+	float pi = 3.1415926535;
+	float denom = dotNH * dotNH *(alphaSqr - 1.0) + 1.0;
+	D = alphaSqr / (pi * denom * denom);
+
+	// Fresnel (Schlick)
+	float dotLH5 = pow (1.0 - dotLH, 5.0);
+	F = F0 + (1.0 - F0)*(dotLH5);
+
+	// Visibility term (G) : Smith with Schlick's approximation
+	float k = alpha / 2.0;
+	vis = G1V (dotNL, k) * G1V (dotNV, k);
+
+	vec3 specular = /*dotNL **/ D * F * vis;
+
+	vec3 ambient = vec3(.01);
+
+	float invPi = 0.31830988618;
+	vec3 diffuse = (albedo * invPi);
+
+
+	return (diffuse + specular) * lightColor * dotNL ;
+}
+
+float SpecGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0)
+{
+	float SqrRoughness = roughness*roughness;
+
+	vec3 H = normalize(V+L);
+
+	float NdotL = clamp(dot(N,L),0.0,1.0);
+	float NdotV = clamp(dot(N,V),0.0,1.0);
+	float NdotH = clamp(dot(N,H),0.0,1.0);
+	float LdotH = clamp(dot(L,H),0.0,1.0);
+
+	// Geom term
+	float RoughnessPow4 = SqrRoughness*SqrRoughness;
+	float pi = 3.14159;
+	float denom = NdotH * NdotH *(RoughnessPow4-1.0) + 1.0;
+	float D = RoughnessPow4/(pi * denom * denom);
+
+	// Fresnel term 
+	float LdotH5 = 1.0-LdotH;
+    LdotH5 = LdotH5*LdotH5*LdotH5*LdotH5*LdotH5;
+	float F = F0 + (1.0-F0)*(LdotH5);
+
+	// Vis term 
+	float k = SqrRoughness/2.0;
+	float Vis = G1V(NdotL,k)*G1V(NdotV,k);
+
+	float specular = NdotL * D * F * Vis;
+    
+	return specular;
+}
+
+vec3 LightingFuncGGX_REF(vec3 N, vec3 V, vec3 L, float roughness, vec3 F0)
+{
+    float alpha = roughness;//roughness*roughness;
+
+    vec3 H = normalize(V+L);
+
+    float dotNL = saturate(dot(N,L));
+    float dotNV = saturate(dot(N,V));
+    float dotNH = saturate(dot(N,H));
+    float dotLH = saturate(dot(L,H));
+
+    //float F, D, vis;
+	float D, vis;
+	vec3 F;
+
+    // D
+    float alphaSqr = alpha*alpha;
+    float pi = 3.14159f;
+    float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f;
+    D = alphaSqr/(pi * denom * denom);
+
+    // F
+    float dotLH5 = pow(1.0f - dotLH, 5);
+    //F = F0 + (1.0-F0)*(dotLH5);
+	F = F0 + (1.0f - F0) * (dotLH5);
+
+    // V
+    float k2 = alpha / 2.0f;
+    vis = G1V(dotNL, k2) * G1V(dotNV, k2);
+	
+	float metallic2 = (1.0f - metallic);// / 2.0f;
+	
+	vec3 specular;
+	
+	//if(metallic == 1.0f)
+	//{
+		specular = dotNL * D * F * vis;
+	//}
+	//else
+	//{
+		//specular = (dotNL) * (metallic2 + D * F * vis * (1.0 - metallic2));
+	//}
+	
+    //vec3 specular = (dotNL) * (metallic2 + D * F * vis * (1.0 - metallic2));
+	
+	//specular = ((1.0 - dotNL) * (1.0 - metallic2)) + D * F * vis;
+	//specular = ((1.0 - dotNL) * (1.0 - metallic2)) + (metallic2 + D * F * vis * (1.0 - metallic2));
+	
+    return specular;
+}
+
+vec3 calcLight(vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, vec3 p_F0, float p_roughnessSqrt)
+{	
+	vec3 lightDir = -p_lightDirection;
+
+	float spec = SpecGGX(p_normal, p_fragToEye, lightDir, roughnessVar, F0);
+    float dif = dot(p_normal, lightDir);
+	//float dif = dot(p_normal, -p_lightDirection);
+	
+	// Fresnel
+    float NdotV = clamp(dot(p_normal, p_fragToEye), 0.0, 1.0);
+	NdotV = pow(1.0 - NdotV, 5.0);    
+	float Fresnel = metallic + (1.0 - metallic) * (NdotV);
+
+    // Tint lights
+    vec3 SpecColor = spec * p_lightColor;
+    vec3 DiffColor = dif * p_lightColor * (1.0 - Fresnel);
+    
+    // Add GI
+    //const float	cAmbientMin = 0.04;    
+    //float		ambient = cAmbientMin * (IsInSphere);    
+    //vec3		ColorAmbient = vec3(ambient,ambient,ambient);
+    //vec3		GIReflexion = GetGIReflexion ( normal, roughness );
+    
+    
+    //ColorAmbient = GIReflexion * cAmbientMin;
+        
+    //vec3 lightSum = max(((DiffColor + SpecColor) * (1.0 - cAmbientMin)), vec3(0.0, 0.0, 0.0));
+    //return ( lightSum + ColorAmbient + ( Fresnel * GIReflexion ) ) * IsInSphere;
+	
+    vec3 lightSum = max(((DiffColor + SpecColor)), vec3(0.0, 0.0, 0.0));
+    return lightSum;
+	
+	/*
+
+    float dotNL = saturate(dot(p_normal, -p_lightDirection));
+	
+	//spec = LightingFuncGGX_REF(p_normal, p_fragToEye, -p_lightDirection, p_roughnessSqrt, F0vec);
+	
+    vec3 H = normalize(p_fragToEye + (-p_lightDirection));
+    float dotLH = saturate(dot(-p_lightDirection,H));
+    float dotLH5 = pow(1.0f - dotLH, 5);
+    //F = F0 + (1.0-F0)*(dotLH5);
+	vec3 F = F0vec + (1.0f - F0vec) * (dotLH5);
+	
+	float metallic2 = 1.0 - metallic;
+	
+	float diffuse = (1.0 - dotNL) * (1.0 - metallic);
+		//return p_lightColor * NdotL * (k + specular * (1.0 - k));
+	
+	//return p_lightColor * spec;*/
+
+	/*/ Calculate the specular contribution
+    float3 ks = 0;
+    float3 specular = GGX_Specular(specularCubemap, normal, viewVector, roughness, F0, ks );
+    float3 kd = (1 - ks) * (1 - metallic);
+    // Calculate the diffuse contribution
+    float3 irradiance = texCUBE(diffuseCubemap_Sampler, normal ).rgb;
+    float3 diffuse = materialColour * irradiance;
+
+    return float4( kd * diffuse + /*ks* / specular, 1);  */   
+	
+	//vec3 reflectionVec = reflect(-p_fragToEye, p_normal);
+    /*vec3 radiance = vec3(0);
+	
+	//float NdotV = clamp(dot(p_normal, p_fragToEye), 0.0, 1.0);
+	float NdotL = clamp(dot(p_normal, -p_lightDirection), 0.0, 1.0);
+	
+	//if(NdotL > 0.0)
+	{
+       // vec3 halfVector = normalize(p_lightDirection - p_fragToEye);
+        vec3 halfVector = normalize(p_fragToEye + p_lightDirection);
+		float NdotH = clamp(dot(p_normal, halfVector), 0.0, 1.0);
+		float VdotH = clamp(dot(p_fragToEye, halfVector), 0.0, 1.0);
+		
+		float cosT = clamp(dot(p_lightDirection, p_normal), 0.0, 1.0); // NdotL
+		float sinT = sqrt(1.0 - (cosT * cosT));
+		
+		vec3 fresnel = Fresnel_Schlick(VdotH, p_F0);
+		float geometry = GeometryAtten_GGX(p_fragToEye, p_normal, halfVector, p_roughnessSqrt) * GeometryAtten_GGX(p_lightDirection, p_normal, halfVector, p_roughnessSqrt);
+		//float geometry = GeometryAtten_GGX(p_lightDirection, p_normal, halfVector, p_roughnessSqrt);
+		float denominator = clamp(4.0 * (NdotV * NdotH + 0.05), 0.0, 1.0);
+		
+		//kS += fresnel;
+		
+		radiance = p_lightColor * geometry * fresnel * sinT / denominator;
+	}
+	
+	return radiance;*/
+}
+
+vec3 calcLightColor(vec3 p_lightColor, vec3 p_lightDirection)
+{
+    // Get angle between normal and light direction
+    //float NdotL = max(dot(normal, -p_lightDirection), 0.0);
+	float NdotL = clamp( dot( normal, -p_lightDirection ), 0.0, 1.0 );
+    
+    float specular = 0.0;
+    if(NdotL > 0.0)
+    {
+        // Calculate neccessary values
+        vec3 halfVector = normalize(fragmentToEye - p_lightDirection );
+		
+		float NdotH = clamp( dot( normal, halfVector ), 0.0, 1.0 );
+		float NdotV = clamp( dot( normal, fragmentToEye ), 0.0, 1.0 );
+		float VdotH = clamp( dot( fragmentToEye, halfVector ), 0.0, 1.0 );
+		
+		/*float NdotL = max(dot(normal, -p_lightDirection), 0.0);
+        float NdotH = max(dot(normal, halfVector), 0.0); 
+        float NdotV = max(dot(normal, fragmentToEye), 0.0);
+		float VdotH = max(dot(fragmentToEye, halfVector), 0.0);*/
+        
+        // Calculate geometric attenuation (self shadowing of microfacets)
+        float NH2 = 2.0 * NdotH;
+        float g1 = (NH2 * NdotV) / VdotH;
+        float g2 = (NH2 * NdotL) / VdotH;
+        float geoAtt = min(1.0, min(g1, g2));
+     	
+		// Calculate microfacet distributions (roughness)
+		// Using Beckmann distribution function
+        float r1 = 1.0 / ( 4.0 * roughnessSqrt * pow(NdotH, 4.0));
+        float r2 = (NdotH * NdotH - 1.0) / (roughnessSqrt * NdotH * NdotH);
+        float microfacetDstrb = r1 * exp(r2);
+        
+		// Calculate fresnel effect (using Schlick's approximation)
+        float fresnel = pow(1.0 - VdotH, 5.0);
+        fresnel *= (1.0 - F0);
+        fresnel += F0;
+        
+		// Calculate specular component
+        specular = max((fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14), 0.0);
+        //specular = (fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14);
+		
+		//F0:"NdotL * (cSpecular * Rs + cDiffuse * (1-f0))"
+		
+		// Combine specular and diffuse components
+		return p_lightColor * NdotL * (k + specular * (1.0 - k));
+		//return p_lightColor * NdotL * specular;//(k + specular * (1.0 - k));
+    }
+	else
+	{
+		return vec3(0.0, 0.0, 0.0);
+	}
+}
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+void main(void) 
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	// Get diffuse color (full-bright) from diffuse buffer and gamma-correct it
+	vec3 diffuseColor = pow(texture(diffuseMap, texCoord).xyz, vec3(2.2));
+	
+	// Get pixel's position in world space and gloss value from position buffer
+	vec4 positionAndGloss = texture(positionMap, texCoord);
+	// Get normal in world space and specular value from normal buffer 
+	vec4 normalAndSpecular = normalize(texture(normalMap, texCoord));
+	
+	// Extract world position
+	worldPos = positionAndGloss.xyz;
+	// Extract normal and normalize it to minimize floating point approximation errors
+	normal = normalize(normalAndSpecular.xyz);
+	
+	// ior = from 1.2 to 10.0
+	
+	float ior = 40.5;
+	metallic = positionAndGloss.w;
+	
+	F0 = abs((1.0 - ior) / (1.0 + ior));
+	F0 = F0 * F0;
+	F0vec = vec3(F0);
+	F0vec = mix(vec3(F0), diffuseColor, metallic);
+	
+	float reflectance = 0.5;
+	//F0vec = 0.16 * reflectance * reflectance * (1.0 - metallic) + diffuseColor * metallic;
+	
+	//float roughness = 1.0 - smoothness*smoothness;
+    //vec3 F0 = 0.16*reflectance*reflectance * (1.0-metalMask) + baseColor*metalMask;
+    //vec3 albedo = baseColor;
+	
+	//float3 F0 = abs ((1.0 - ior) / (1.0 + ior));
+	//F0 = F0 * F0;
+	//F0 = lerp(F0, materialColour.rgb, metallic);
+	
+	//k = 1.0 - normalAndSpecular.w;
+	//k = 0.2;
+	//roughnessVar = 0.1;// - ((positionAndGloss.w + normalAndSpecular.w) / 2.0);
+	
+	ref_at_norm_incidence = F0;
+	viewer = fragmentToEye;
+	
+	fragmentToEye = normalize(cameraPosVec - worldPos);
+	//fragmentToEye = normalize(worldPos - cameraPosVec);
+	roughnessVar = texture(normalMap, texCoord).w;
+	//roughnessSqrt = roughnessVar;// * roughnessVar;
+
+	//k = 1.0 - positionAndGloss.w;
+	//roughnessVar = (normalAndSpecular.w + positionAndGloss.w) / 2.0;
+	roughnessSqrt = roughnessVar * roughnessVar;
+	
+	// Declare final color of the fragment and add directional light to it
+	vec3 finalLightColor = calcLight(normal, fragmentToEye, directionalLight.m_color, normalize(directionalLight.m_direction), vec3(F0), roughnessSqrt) * directionalLight.m_intensity;// + directionalLight.m_color * 0.005;
+	//vec3 finalLightColor = vec3(0.0);
+	
+	for(int i = 0; i < numPointLights; i++)
+	{		
+		// Get light direction, extract length from it and normalize for usage as direction vector
+		vec3 lightDirection = worldPos - pointLights[i].m_position;
+		//vec3 lightDirection =  pointLights[i].m_position - worldPos;
+		float lightDistance = length(lightDirection);
+		lightDirection = normalize(lightDirection);
+		
+		// Add up constant, linear and quadratic attenuation
+		float attenuation = pointLights[i].m_attenConstant + 
+							pointLights[i].m_attenLinear * lightDistance + 
+							pointLights[i].m_attenQuad * lightDistance * lightDistance;
+						 
+		// Light color multiplied by intensity and divided by attenuation
+		
+		//finalLightColor += (calcLightColor(pointLights[i].m_color, lightDirection) * pointLights[i].m_intensity) / attenuation;
+		finalLightColor += (calcLight(normal, fragmentToEye, pointLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * pointLights[i].m_intensity) / attenuation;
+		//finalLightColor += (computePBRLighting(pointLights[i].m_position, pointLights[i].m_color, worldPos, normal, fragmentToEye, diffuseColor, roughnessVar, F0vec) * pointLights[i].m_intensity) / attenuation;
+	}
+	
+	for(int i = 0; i < numSpotLights; i++)
+	{			
+		// Calculate direction from position of light to current pixel
+		vec3 lightToFragment = normalize(worldPos - spotLights[i].m_position);
+		
+		// Get dot product of light direction and direction of light to pixel, and use it as a factor for light strength
+		float spotLightFactor = dot(lightToFragment, spotLights[i].m_direction);
+		
+		// Early bail if pixel is outside of the cone of spot light
+		if(spotLightFactor > spotLights[i].m_cutoffAngle)
+		{
+			// Get light direction, extract length from it and normalize for usage as direction vector
+			vec3 lightDirection = worldPos - spotLights[i].m_position;
+			//vec3 lightDirection =  spotLights[i].m_position - worldPos;
+			float lightDistance = length(lightDirection);
+			lightDirection = normalize(lightDirection);
+			
+			// Add up constant, linear and quadratic attenuation
+			float attenuation = spotLights[i].m_attenConstant + 
+								spotLights[i].m_attenLinear * lightDistance + 
+								spotLights[i].m_attenQuad * lightDistance * lightDistance;
+			
+			// Light color multiplied by intensity
+			//finalLightColor += (calcLight(normal, fragmentToEye, pointLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * pointLights[i].m_intensity) / attenuation;
+			vec3 lightColor = (calcLight(normal, fragmentToEye, spotLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * spotLights[i].m_intensity);
+			//vec3 lightColor = (calcLightColor(spotLights[i].m_color, lightDirection) * spotLights[i].m_intensity);
+			
+			// Light restriction from cone
+			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
+			
+			finalLightColor += (lightColor / attenuation) * coneAttenuation; 
+		}
+	}
+	
+	// Multiply the diffuse color by the amount of light the current pixel receives and gamma correct it
+	finalBuffer = vec4(pow(diffuseColor * finalLightColor, vec3(1.0 / 2.2)), 1.0);
+	//finalBuffer = vec4(metallic, metallic, metallic, 1.0);
+	//finalBuffer = vec4(pow(mix(diffuseColor, vec3(0.0, 0.0, 0.0), 1.0 - metallic) * finalLightColor, vec3(1.0 / 2.2)), 1.0);
+	//finalBuffer = vec4(pow(finalLightColor, vec3(1.0 / 2.2)), 1.0);
+	//finalBuffer = vec4(pow(diffuseColor, vec3(1.0 / 2.2)), 1.0);
+	//finalBuffer = vec4(texture(normalMap, texCoord).xyz, 1.0);
+	//finalBuffer = vec4(roughnessVar, roughnessVar, roughnessVar, 1.0);
+}

+ 10 - 0
Praxis3D/Data/Shaders/lightPass.vert

@@ -0,0 +1,10 @@
+#version 330
+
+void main(void) 
+{
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+
+	//gl_Position = vec4(vertexPosition, 1.0);
+}

+ 13 - 0
Praxis3D/Data/Shaders/skybox.frag

@@ -0,0 +1,13 @@
+#version 330
+
+layout(location = 1) out vec4 finalBuffer;
+
+in vec2 texCoord;
+
+uniform sampler2D diffuseTexture;
+
+void main(void) 
+{
+	// Write the color from model's texture
+	finalBuffer	= texture(diffuseTexture, texCoord).rgba;
+}

+ 15 - 0
Praxis3D/Data/Shaders/skybox.vert

@@ -0,0 +1,15 @@
+#version 330
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 2) in vec2 textureCoord;
+
+out vec2 texCoord;
+
+uniform mat4 MVP;
+
+void main(void) 
+{
+	texCoord = textureCoord;
+	
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}

+ 45 - 0
Praxis3D/Data/Shaders/terrain.frag

@@ -0,0 +1,45 @@
+#version 330 core
+
+// Some drivers require the following
+precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+in float depth;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+void main(void)
+{	
+	float height = texture(heightTexture, texCoord.st).r;
+	float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord+(eyeDir.xy*v);
+	vec2 newCoords = texCoord;
+	
+	float specularPower = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	if(emissiveColor.a < 0.3)
+	{
+		emissiveColor = vec4(0.0);
+	}
+	emissiveBuffer	= emissiveColor * emissiveColor.a;
+
+	diffuseBuffer	= texture(diffuseTexture, newCoords).rgba;	// Write the color from model's texture
+	positionBuffer	= vec4(worldPos, 0.0);						// Write fragment's position in world space
+	normalBuffer = vec4(TBN * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), specularPower);
+}

+ 91 - 0
Praxis3D/Data/Shaders/terrain.tesc

@@ -0,0 +1,91 @@
+#version 400 core
+
+// Produce 4 vertices of output
+layout(vertices = 4) out;
+	
+uniform mat4 MVP;
+uniform vec2 screenSize;
+
+float LODFactor = 1.0;
+
+// Transforms a vertex from worls space to normal space
+vec4 toNormalSpace(vec4 p_vertex)
+{
+	vec4 resultVec = MVP * p_vertex;
+	resultVec /= resultVec.w;
+	return resultVec;
+}
+
+// Transforms a vertex from normal space to screen space
+vec2 toScreenSpace(vec4 p_vertex)
+{
+	return (clamp(p_vertex.xy, -1.3, 1.3) + 1) * (screenSize * 0.5);
+}
+
+// Checks if a vertex is outside of view furstum
+bool isOffscreen(vec4 p_vertex)
+{
+	if(p_vertex.z < 0.5)
+	{
+		return true;
+	}
+	
+	return any(lessThan(p_vertex.xy, vec2(-1.7)) || greaterThan(p_vertex.xy, vec2(1.7)));
+}
+
+// Calculate LOD of an edge, by using distance between two points in screen space
+float calcLevel(vec2 p_ver0, vec2 p_vert1)
+{
+	return clamp(distance(p_ver0, p_vert1) / LODFactor, 1, 64);
+}
+
+void main(void)
+{
+	//gl_TessLevelInner[0] = 0;
+	//gl_TessLevelInner[1] = 0;
+	
+	//gl_TessLevelOuter[0] = 0;
+	//gl_TessLevelOuter[1] = 0;
+	//gl_TessLevelOuter[2] = 0;
+	//gl_TessLevelOuter[3] = 0;
+	
+	if(gl_InvocationID == 0)
+	{
+		vec4 vert0 = toNormalSpace(gl_in[0].gl_Position);
+		vec4 vert1 = toNormalSpace(gl_in[1].gl_Position);
+		vec4 vert2 = toNormalSpace(gl_in[2].gl_Position);
+		vec4 vert3 = toNormalSpace(gl_in[3].gl_Position);
+		
+		// If all of the vertices are offscreen, set the inner and outer tessellation levels to 0
+		if(all(bvec4(isOffscreen(vert0), isOffscreen(vert1), isOffscreen(vert2), isOffscreen(vert3))))
+		{
+			gl_TessLevelInner[0] = 0;
+			gl_TessLevelInner[1] = 0;
+			
+			gl_TessLevelOuter[0] = 0;
+			gl_TessLevelOuter[1] = 0;
+			gl_TessLevelOuter[2] = 0;
+			gl_TessLevelOuter[3] = 0;
+		}
+		else
+		{
+			vec2 screeSpaceVert0 = toScreenSpace(vert0);
+			vec2 screeSpaceVert1 = toScreenSpace(vert1);
+			vec2 screeSpaceVert2 = toScreenSpace(vert2);
+			vec2 screeSpaceVert3 = toScreenSpace(vert3);
+			
+			float edgeLevelVert0 = calcLevel(screeSpaceVert1, screeSpaceVert2);
+			float edgeLevelVert1 = calcLevel(screeSpaceVert0, screeSpaceVert1);
+			float edgeLevelVert2 = calcLevel(screeSpaceVert3, screeSpaceVert0);
+			float edgeLevelVert3 = calcLevel(screeSpaceVert2, screeSpaceVert3);
+			
+			gl_TessLevelInner[0] = mix(edgeLevelVert1, edgeLevelVert2, 0.5);
+			gl_TessLevelInner[1] = mix(edgeLevelVert0, edgeLevelVert3, 0.5);
+			
+			gl_TessLevelOuter[0] = edgeLevelVert0;
+			gl_TessLevelOuter[1] = edgeLevelVert1;
+			gl_TessLevelOuter[2] = edgeLevelVert2;
+			gl_TessLevelOuter[3] = edgeLevelVert3;
+		}
+	}
+}

+ 24 - 0
Praxis3D/Data/Shaders/terrain.tese

@@ -0,0 +1,24 @@
+#version 400 core
+
+layout(quads, fractional_odd_spacing, ccw) in;
+
+out vec2 texCoord;
+out float depth;
+
+uniform mat4 MVP;
+
+uniform sampler2D diffuseTexture;
+
+void main(void)
+{
+        float u = gl_TessCoord.x;
+        float v = gl_TessCoord.y;
+
+        vec4 a = mix(gl_in[1].gl_Position, gl_in[0].gl_Position, u);
+        vec4 b = mix(gl_in[2].gl_Position, gl_in[3].gl_Position, u);
+        vec4 position = mix(a, b, v);
+        texCoord = position.xy;
+        float height = texture(diffuseTexture, texCoord).r;
+        gl_Position = MVP * vec4(1.0, 1.0, 1.0, 1.0);
+        depth = gl_Position.z;
+}

+ 45 - 0
Praxis3D/Data/Shaders/terrain.vert

@@ -0,0 +1,45 @@
+#version 400 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+//out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D heightTexture;
+
+void main(void)
+{
+	vec2 texCoord = vertexPosition.xy;
+	//texCoord = textureCoord;
+	float height = texture(diffuseTexture, vertexPosition.xy).a;
+	vec4 displaced = vec4(vertexPosition.x, vertexPosition.y, vertexPosition.z, 1.0);
+	//vec4 displaced = vec4(vertexPosition.x, vertexPosition.y, vertexPosition.z, 1.0);
+	
+	vec4 viewPosition = projMat * modelViewMat * displaced;
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * displaced).xyz;
+	//texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	eyeDir = normalize(-viewPosition).xyz;
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	gl_Position = MVP * displaced;
+	//gl_Position = displaced;
+}

+ 77 - 0
Praxis3D/Data/Shaders/terrain2.frag

@@ -0,0 +1,77 @@
+#version 330 core
+
+// Some drivers require the following
+precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+in float depth;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+out vec4 FragColor;
+in vec3 gFacetNormal;
+in vec3 gTriDistance;
+in vec3 gPatchDistance;
+in float gPrimitive;
+
+/*void main(void)
+{	
+	float height = texture(heightTexture, texCoord.st).r;
+	float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord+(eyeDir.xy*v);
+	vec2 newCoords = texCoord;
+	
+	float specularPower = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	if(emissiveColor.a < 0.3)
+	{
+		emissiveColor = vec4(0.0);
+	}
+	emissiveBuffer	= emissiveColor * emissiveColor.a;
+
+	diffuseBuffer	= texture(diffuseTexture, newCoords).rgba;	// Write the color from model's texture
+	positionBuffer	= vec4(worldPos, 0.0);						// Write fragment's position in world space
+	normalBuffer = vec4(TBN * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), specularPower);
+}*/
+
+float amplify(float d, float scale, float offset)
+{
+    d = scale * d + offset;
+    d = clamp(d, 0, 1);
+    d = 1 - exp2(-2*d*d);
+    return d;
+}
+
+void main()
+{
+    vec3 N = normalize(gFacetNormal);
+    vec3 L = vec3(1.0, 5.0, 1.0);
+    float df = abs(dot(N, L));
+    vec3 color = vec3(0.5, 0.5, 0.5) + df * vec3(0.0, 0.5, 0.0);
+
+    float d1 = min(min(0.1, 0.1), 0.1);
+    float d2 = min(min(gPatchDistance.x, gPatchDistance.y), gPatchDistance.z);
+    color = amplify(d1, 40, -0.5) * amplify(d2, 60, -0.5) * color;
+
+    diffuseBuffer = vec4(color, 1.0);
+	
+	vec2 newCoords = texCoord;
+	//diffuseBuffer = vec4(texCoord, 0.0, 1.0);
+	//diffuseBuffer	= texture(diffuseTexture, newCoords).rgba;	// Write the color from model's texture
+}

+ 35 - 0
Praxis3D/Data/Shaders/terrain2.geom

@@ -0,0 +1,35 @@
+#version 400 core
+
+uniform mat4 MVP;
+//uniform mat3 NormalMatrix;
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+in vec3 tePosition[3];
+in vec3 tePatchDistance[3];
+
+out vec3 gFacetNormal;
+out vec3 gPatchDistance;
+out vec3 gTriDistance;
+
+void main()
+{
+    vec3 A = tePosition[2] - tePosition[0];
+    vec3 B = tePosition[1] - tePosition[0];
+    gFacetNormal = (MVP * vec4(normalize(cross(A, B)), 1.0)).xyz;
+    
+    gPatchDistance = tePatchDistance[0];
+    gTriDistance = vec3(1, 0, 0);
+    gl_Position = gl_in[0].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[1];
+    gTriDistance = vec3(0, 1, 0);
+    gl_Position = gl_in[1].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[2];
+    gTriDistance = vec3(0, 0, 1);
+    gl_Position = gl_in[2].gl_Position; EmitVertex();
+
+    EndPrimitive();
+}

+ 31 - 0
Praxis3D/Data/Shaders/terrain2.tesc

@@ -0,0 +1,31 @@
+#version 400 core
+
+// Produce 4 vertices of output
+//layout(vertices = 4) out;
+	
+//uniform mat4 MVP;
+//uniform vec2 screenSize;
+
+//float LODFactor = 1.0;
+
+layout(vertices = 3) out;
+
+in vec3 vPosition[];
+out vec3 tcPosition[];
+
+#define ID gl_InvocationID
+
+void main()
+{
+	float TessLevelInner = 64;
+	float TessLevelOuter = 64;
+
+    tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];
+    if (ID == 0) 
+	{
+        gl_TessLevelInner[0] = TessLevelInner;
+        gl_TessLevelOuter[0] = TessLevelOuter;
+        gl_TessLevelOuter[1] = TessLevelOuter;
+        gl_TessLevelOuter[2] = TessLevelOuter;
+    }
+}

+ 29 - 0
Praxis3D/Data/Shaders/terrain2.tese

@@ -0,0 +1,29 @@
+#version 400 core
+
+//layout(quads, fractional_odd_spacing, ccw) in;
+
+//out vec2 texCoord;
+//out float depth;
+
+//uniform mat4 MVP;
+
+layout(triangles, equal_spacing, cw) in;
+
+in vec3 tcPosition[];
+out vec3 tePosition;
+out vec3 tePatchDistance;
+
+uniform mat4 MVP;
+//uniform mat4 Modelview;
+
+void main()
+{
+    vec3 p0 = gl_TessCoord.x * tcPosition[0];
+    vec3 p1 = gl_TessCoord.y * tcPosition[1];
+    vec3 p2 = gl_TessCoord.z * tcPosition[2];
+	
+    tePatchDistance = gl_TessCoord;
+    tePosition = normalize(p0 + p1 + p2);
+	
+    gl_Position = MVP * vec4(tePosition, 1);
+}

+ 34 - 0
Praxis3D/Data/Shaders/terrain2.vert

@@ -0,0 +1,34 @@
+#version 400 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+//layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+//layout(location = 3) in vec3 vertexTangent;
+//layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+//out vec3 worldPos;
+out vec2 texCoord;
+//out vec3 normal;
+//out vec3 eyeDir;
+//out mat3 TBN;
+
+//uniform mat4 MVP;
+//uniform mat4 modelMat;
+//uniform mat4 modelViewMat;
+//uniform mat4 projMat;
+
+//uniform sampler2D diffuseTexture;
+//uniform sampler2D heightTexture;
+
+//in vec4 Position;
+//in vec4 Position;
+out vec3 vPosition;
+
+void main()
+{
+	//texCoord = textureCoord;
+	texCoord = vertexPosition.xy;
+    vPosition = vertexPosition.xyz;
+}

+ 169 - 0
Praxis3D/Data/Shaders/terrain3.frag

@@ -0,0 +1,169 @@
+#version 410 core
+
+// Some drivers require the following
+precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+
+in vec3 tangent;
+in vec3 bitangent;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+
+void main(void)
+{
+	//emissiveBuffer	= normal;
+
+	//vec4 emissiveTexture = texture(emissiveTexture, texCoord).rgba;
+	//if(emissiveTexture.a < 0.5)
+	//{
+	//	emissiveTexture = vec4(0.0);
+	//}
+	//emissiveBuffer	= emissiveTexture * emissiveTexture.a;
+	//diffuseBuffer	= texture(diffuseTexture, texCoord).rgba;
+	//texCoordBuffer	= vec4(texCoord, 0.0, 0.0);
+	//positionBuffer	= vec4(worldPos, 0.0);
+	//normalBuffer	= vec4(normalize(normal), 0.0);
+	
+	//vec2 newTexCoord = vec2(worldPos.x, worldPos.z);
+	vec2 newCoords = texCoord;
+	
+	float height = texture(normalTexture, texCoord).a * 1.0;
+	
+	vec3 off = vec3(1 / 4096.0, 1 / 4096.0, 0.0);
+	float hL = texture(normalTexture, texCoord - off.xz).a;
+	float hR = texture(normalTexture, texCoord + off.xz).a;
+	float hD = texture(normalTexture, texCoord - off.zy).a;
+	float hU = texture(normalTexture, texCoord + off.zy).a;
+	
+	vec3 vva = vec3(0.0, 1.0, (hU - height) * 1.0);
+	vec3 vvb = vec3(1.0, 0.0, (hR - height) * 1.0);
+	vec3 vvc = vec3(0.0, -1.0, (hD - height) * 1.0);
+	vec3 vvd = vec3(-1.0, 0.0, (hL - height) * 1.0);
+	
+	//vec3 vva = vec3(0.0, (hU - height), 1.0);
+	//vec3 vvb = vec3(1.0, (hR - height), 0.0);
+	//vec3 vvc = vec3(0.0, (hD - height), -1.0);
+	//vec3 vvd = vec3(-1.0, (hL - height), 0.0);
+		
+	//cross products of each vector yields the normal of each tri - return the average normal of all 4 tris
+	vec3 average_n = ( cross(vva, vvb) + cross(vvb, vvc) + cross(vvc, vvd) + cross(vvd, vva) ) / -4;
+	//vec3 normal2 = normalize(average_n);
+	
+	vec2 uv =  texCoord;
+	vec2 du = vec2(1/4096.0, 0);
+	vec2 dv = vec2(0, 1/4096.0);
+	float dhdu = (2/4096.0) * (texture(normalTexture, uv+du).a - texture(normalTexture, uv-du).a);
+	float dhdv = (2/4096.0) * (texture(normalTexture, uv+dv).a - texture(normalTexture, uv-dv).a);
+	//vec3 normal2 = normalize(normal + tangent * dhdu + bitangent * dhdv);
+	
+	vec3 Norm = vec3(0.0, 1.0, 0.0);
+	Norm.x = hL - hR;
+	Norm.y = hD - hU;
+	Norm.z = 1.0;
+	//vec3 normal2 = normalize(N);
+	
+	//ivec2 xy = ivec2(-1, 0);
+	//ivec2 zy = ivec2(1, 0);
+	//ivec2 yx = ivec2(0, -1);
+	//ivec2 yz = ivec2(0, 1);
+
+	vec4 wave = texture(normalTexture, texCoord);
+	float s11 = wave.a * 200.0;
+	
+	vec2 size = vec2(2.0, 0.0);
+	const ivec3 off2 = ivec3(-1, 0, 1);
+	
+	float s01 = textureOffset(normalTexture, texCoord, off2.xy).a * 200.0;
+	float s21 = textureOffset(normalTexture, texCoord, off2.zy).a * 200.0;
+	float s10 = textureOffset(normalTexture, texCoord, off2.yx).a * 200.0;
+	float s12 = textureOffset(normalTexture, texCoord, off2.yz).a * 200.0;
+	//vec3 va = normalize(vec3(size.xy, s21 - s01));
+	//vec3 vb = normalize(vec3(size.yx, s12 - s10));
+	vec3 va = normalize(vec3(size.x, s21-s01, size.y)); 
+	vec3 vb = normalize(vec3(size.y, s12-s10, -size.x));
+	//vec3 va = normalize(vec3(size.y, s21 - s01, size.x));
+	//vec3 vb = normalize(vec3(size.x, s12 - s10, size.y));
+	vec4 bump = vec4(cross(va, vb), s11);
+	
+	vec3 normal2 = bump.xyz;
+		
+	//normal = normalize(N);
+	//mat3 normalMat = transpose(inverse(mat3(modelMatrix)));
+	//TBN = mat3(normalMat * VertexTangent, normalMat * TextureBiTangent, normalMat * VertexNormal);
+	//normal = (modelMat * vec4(normal, 0.0)).xyz;
+	
+	mat3 normalMat = transpose(inverse(mat3(modelMat)));
+	//mat3 TBN2 = mat3(normalMat * tangent, normalMat * bitangent, normalMat * normal);
+	//mat3 TBN2 = mat3(normalMat * tangent, normalMat * bitangent, normalMat * normalize(vec3(0.0, 1.0, 0.0)));
+	
+	vec3 T = normalize(vec3(modelMat * vec4(tangent,   0.0)));
+	vec3 B = normalize(vec3(modelMat * vec4(bitangent, 0.0)));
+	vec3 N = normalize(vec3(modelMat * vec4(normal2,    0.0)));
+	mat3 TBN2 = mat3(T, B, N);
+	
+	float h0 = texture(normalTexture, texCoord + vec2(1 / 4096.0, 0.0)).a;
+	float h1 = texture(normalTexture, texCoord - vec2(1 / 4096.0, 0.0)).a;
+	
+	//mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	//vec3 normal = (modelMat * vec4(0.0, 1.0, 0.0, 0.0)).xyz;
+	//vec3 normal2 = vec3(0.0, 1.0, 0.0);
+	//vec3 normal3 = vec3(texture(normalTexture, newCoords).r, texture(normalTexture, newCoords).b, texture(normalTexture, newCoords).g);
+	
+	//vec3 tangent = vec3(0.0, 0.0, 1.0);
+	//vec3 bitangent = cross(tangent, normal);
+	//tangent = cross(normal, bitangent);
+	
+	//normalMatrix = mat3(normalMatrix * tangent, normalMatrix * bitangent, normalMatrix * normal);
+	
+	//float height = texture(heightTexture, texCoord.st).r;
+	//float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord+(eyeDir.xy*v);
+	
+	vec3 normalMap = normalize((2.0 * texture(normalTexture, texCoord).rbg - 1.0));
+	//normalMap = normalize(cross(normalMap, normalize(vec3(0.0, 0.0, 1.0))));
+	normalMap = normalize(vec4(normalMap, 0.0) * vec4(0.0, -1.0, 0.0, 1.0)).xyz;
+	
+	float specularPower = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	if(emissiveColor.a < 0.3)
+	{
+		emissiveColor = vec4(0.0);
+	}
+	emissiveBuffer	= emissiveColor * emissiveColor.a;
+
+	diffuseBuffer	= vec4(texture(diffuseTexture, newCoords).rbg, 0.0);	// Write the color from model's texture
+	positionBuffer	= vec4(worldPos, 0.0);						// Write fragment's position in world space
+	//normalBuffer = vec4(0.0, 1.0, 0.0, 1.0);
+	//normalBuffer = vec4(normalize(texture(normalTexture, newCoords).rbg), 0.0);
+	//normalBuffer = vec4(normalize(vec3(texture(normalTexture, newCoords).r, texture(normalTexture, newCoords).b, texture(normalTexture, newCoords).g)), 0.0);
+	//normalBuffer = vec4(((modelMat * vec4(normal2, 0.0)).xyz), 0.0);
+	//normalBuffer = vec4(modelMat * vec4(normal2, 0.0));
+	//normalBuffer = normalize(modelMat * vec4(texture(normalTexture, texCoord).rbg, 0.0));
+	//normalBuffer = normalize(modelMat * vec4(normalMap, 0.0));
+	//normalBuffer = vec4(h0 - h1) * 100.0;
+	//normalBuffer = vec4(va, 0.0);
+	//normalBuffer = vec4(TBN * normalize(normal2), 0.0);
+	//normalBuffer = modelMat * vec4(normalize(vec3(texture(normalTexture, newCoords).r, texture(normalTexture, newCoords).g, texture(normalTexture, newCoords).b)), 0.0);
+	normalBuffer = normalize(vec4(TBN2 * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), 0.0));
+	//normalBuffer = vec4(normalMatrix * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), 0.0);
+}

+ 108 - 0
Praxis3D/Data/Shaders/terrain3.geom

@@ -0,0 +1,108 @@
+#version 410 core
+
+/*uniform mat4 MVP;
+//uniform mat3 NormalMatrix;
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+in vec3 tePosition[3];
+in vec3 tePatchDistance[3];
+
+out vec3 gFacetNormal;
+out vec3 gPatchDistance;
+out vec3 gTriDistance;
+
+void main()
+{
+    vec3 A = tePosition[2] - tePosition[0];
+    vec3 B = tePosition[1] - tePosition[0];
+    gFacetNormal = (MVP * vec4(normalize(cross(A, B)), 1.0)).xyz;
+    
+    gPatchDistance = tePatchDistance[0];
+    gTriDistance = vec3(1, 0, 0);
+    gl_Position = gl_in[0].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[1];
+    gTriDistance = vec3(0, 1, 0);
+    gl_Position = gl_in[1].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[2];
+    gTriDistance = vec3(0, 0, 1);
+    gl_Position = gl_in[2].gl_Position; EmitVertex();
+
+    EndPrimitive();
+}*/
+
+layout(triangles) in;
+//layout (triangle_strip) out;
+//layout (max_vertices = 3) out;
+layout(triangle_strip, max_vertices = 3) out;
+//layout(line_strip, max_vertices=6) out;
+
+in vec3 worldPos_TE[];
+in vec2 texCoord_TE[];
+in vec3 normal_TE[];
+in vec3 eyeDir_TE[];
+in mat3 TBN_TE[];
+in vec3 tangent_TE[];
+in vec3 bitangent_TE[];
+
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+out vec3 tangent;
+out vec3 bitangent;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+
+void main(void)
+{
+	vec3 n = normalize(-cross(worldPos_TE[1].xyz - worldPos_TE[0].xyz, worldPos_TE[2].xyz - worldPos_TE[0].xyz));
+	//vec3 n = cross(gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz, gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz);
+	
+    int i;
+    for (i = 0; i < gl_in.length(); i++)
+    {
+		worldPos = worldPos_TE[i];
+		texCoord = texCoord_TE[i];
+		normal = normal_TE[i];
+		normal = n;
+		eyeDir = eyeDir_TE[i];
+		TBN = TBN_TE[i];
+		//tangent = tangent_TE[i];
+		//bitangent = bitangent_TE[i];
+		
+		vec3 t; 
+		vec3 b; 
+		vec3 c1 = cross(n, vec3(0.0, 0.0, 1.0)); 
+		vec3 c2 = cross(n, vec3(0.0, 1.0, 0.0)); 
+		if (length(c1) > length(c2))
+			t = c1;
+		else
+			t = c2;
+		t = normalize(t);
+		b = normalize(cross(n, t));
+		
+		tangent = t;
+		bitangent = b;
+	
+        gl_Position = gl_in[i].gl_Position;
+        EmitVertex();
+		
+       /* vec3 P = gl_in[i].gl_Position.xyz;
+        vec3 N = normal_TE[i].xyz;
+        
+        gl_Position = MVP * vec4(P, 1.0);
+        EmitVertex();
+        
+        gl_Position = MVP * vec4(P + N * 10.0, 1.0);
+        EmitVertex();
+		EndPrimitive();*/
+    }
+    EndPrimitive();
+}

+ 163 - 0
Praxis3D/Data/Shaders/terrain3.tesc

@@ -0,0 +1,163 @@
+#version 410 core
+
+layout(vertices = 3) out;
+
+//in vec3 vPosition[];
+//out vec3 tcPosition[];
+
+//#define ID gl_InvocationID
+
+in vec3 worldPos[];
+in vec2 texCoord[];
+in vec3 normal[];
+in vec3 eyeDir[];
+in mat3 TBN[];
+
+out vec3 worldPos_CTRL[];
+out vec2 texCoord_CTRL[];
+out vec3 normal_CTRL[];
+out vec3 eyeDir_CTRL[];
+out mat3 TBN_CTRL[];
+
+//in vec2 posV[];
+
+//out vec2 posTC[];
+
+uniform mat4 MVP;
+uniform mat4 modelViewMat;
+uniform vec3 cameraPosVec;
+uniform ivec2 screenSize;
+
+uniform sampler2D diffuseTexture;
+
+float TerrainHeightOffset = 100.0;
+float patchSize = 1.0;
+int scaleFactor = 2;
+int pixelsPerEdge = 1;
+float gridSpacing = 160.0;
+float heightStep = 0.1;
+
+float height(float u, float v)
+{
+
+	return (texture(diffuseTexture, vec2(u,v)).a  * heightStep);
+}
+
+// Checks if the segment between two vector points is at least partially inside the view frustum
+bool insideViewFrustum(vec4 p_vec0, vec4 p_vec1)
+{
+	if ((p_vec0.x < -p_vec0.w && p_vec1.x < -p_vec1.w) || (p_vec0.x > p_vec0.w && p_vec1.x > p_vec1.w) ||
+		(p_vec0.z < -p_vec0.w && p_vec1.z < -p_vec1.w) || (p_vec0.z > p_vec0.w && p_vec1.z > p_vec1.w))
+		return false;
+	else
+		return true;
+}
+
+// Calculates a sphere size in screen space of the two vector points
+float screenSphereSize(vec4 p_vec0, vec4 p_vec1)
+{
+	vec4 viewCenter = (p_vec0 + p_vec1) * 0.5;
+	vec4 viewUp = viewCenter;
+	viewUp.y += distance(p_vec0, p_vec1);
+	vec4 vec0Proj = viewCenter;
+	vec4 vec1Proj = viewUp;
+	
+	// Get normalized deviced coordinates
+	vec4 vec0NDC, vec1NDC;
+	vec0NDC = vec0Proj/vec0Proj.w;
+	vec1NDC = vec1Proj/vec1Proj.w;
+	
+	return(clamp(length((vec1NDC.xy - vec0NDC.xy) * screenSize * 0.5) / (pixelsPerEdge), 1.0, patchSize));
+}
+
+void main()
+{
+	int scaleF;
+	if (scaleFactor == 0)
+		scaleF = 1;
+	else
+		scaleF = scaleFactor;
+
+	vec2 iLevel;
+	vec4 oLevel;
+
+	vec4 posTransV[4];
+	vec2 pAux;
+	vec2 posTCAux[4];
+
+	ivec2 tSize = textureSize(diffuseTexture, 0) * scaleF;// * 2;
+	float div = patchSize / tSize.x;
+
+	//posTC[gl_InvocationID] = posV[gl_InvocationID];
+
+	vec2 position0 = vec2(worldPos[0].x, worldPos[0].z);
+	
+	posTCAux[0] = position0;
+	posTCAux[1] = position0 + vec2(0.0, div);
+	posTCAux[2] = position0 + vec2(div,0.0);
+	posTCAux[3] = position0 + vec2(div,div);
+	
+	
+	pAux = posTCAux[0] * tSize * gridSpacing;
+	posTransV[0] = MVP * vec4(pAux[0], height(posTCAux[0].x,posTCAux[0].y), pAux[1], 1.0);
+
+	pAux = posTCAux[1] * tSize * gridSpacing;
+	posTransV[1] = MVP * vec4(pAux[0], height(posTCAux[1].x,posTCAux[1].y), pAux[1], 1.0);
+
+	pAux = posTCAux[2] * tSize * gridSpacing;
+	posTransV[2] = MVP * vec4(pAux[0], height(posTCAux[2].x,posTCAux[2].y), pAux[1], 1.0);
+
+	pAux = posTCAux[3] * tSize * gridSpacing;
+	posTransV[3] = MVP * vec4(pAux[0], height(posTCAux[3].x,posTCAux[3].y), pAux[1], 1.0);
+	
+			
+				
+	// Screen size based LOD
+	oLevel = vec4(screenSphereSize(posTransV[gl_InvocationID], posTransV[gl_InvocationID+1]),
+				screenSphereSize(posTransV[gl_InvocationID+0], posTransV[gl_InvocationID+2]),
+				screenSphereSize(posTransV[gl_InvocationID+2], posTransV[gl_InvocationID+3]),
+				screenSphereSize(posTransV[gl_InvocationID+3], posTransV[gl_InvocationID+1]));
+	iLevel = vec2(max(oLevel[1] , oLevel[3]) , max(oLevel[0] , oLevel[2]) );
+		
+	//gl_TessLevelOuter[0] = oLevel[0];
+	//gl_TessLevelOuter[1] = oLevel[1];
+//gl_TessLevelOuter[2] = oLevel[2];
+	//gl_TessLevelOuter[3] = oLevel[3];
+	//gl_TessLevelInner[0] = iLevel[0];
+//gl_TessLevelInner[1] = iLevel[1];
+	
+	gl_TessLevelOuter[0] = 64;
+	gl_TessLevelOuter[1] = 64;
+	gl_TessLevelOuter[2] = 64;
+	gl_TessLevelOuter[3] = 64;
+	gl_TessLevelInner[0] = 64;
+	gl_TessLevelInner[1] = 64;
+	
+	worldPos_CTRL[gl_InvocationID] = worldPos[gl_InvocationID];
+	texCoord_CTRL[gl_InvocationID] = texCoord[gl_InvocationID];
+	normal_CTRL[gl_InvocationID] = normal[gl_InvocationID];
+	eyeDir_CTRL[gl_InvocationID] = eyeDir[gl_InvocationID];
+	TBN_CTRL[gl_InvocationID] = TBN[gl_InvocationID];
+	
+	/*
+	//float TessLevelInner = 64;
+	//float TessLevelOuter = 64;
+    //tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];
+	
+	float distanceVert0 = distance(cameraPosVec, worldPos_CTRL[0]);
+	float distanceVert1 = distance(cameraPosVec, worldPos_CTRL[1]);
+	float distanceVert2 = distance(cameraPosVec, worldPos_CTRL[2]);
+	
+	//gl_TessLevelOuter[0] = calcTessLevel(distanceVert1, distanceVert2);
+	//gl_TessLevelOuter[1] = calcTessLevel(distanceVert2, distanceVert0);
+	//gl_TessLevelOuter[2] = calcTessLevel(distanceVert0, distanceVert1);
+	//gl_TessLevelInner[0] = gl_TessLevelOuter[2];
+	
+		// Outer tessellation level
+	gl_TessLevelOuter[0] = calcDistance(gl_in[3].gl_Position, gl_in[0].gl_Position, texCoord[3], texCoord[0]);
+	gl_TessLevelOuter[1] = calcDistance(gl_in[0].gl_Position, gl_in[1].gl_Position, texCoord[0], texCoord[1]);
+	gl_TessLevelOuter[2] = calcDistance(gl_in[1].gl_Position, gl_in[2].gl_Position, texCoord[1], texCoord[2]);
+	gl_TessLevelOuter[3] = calcDistance(gl_in[2].gl_Position, gl_in[3].gl_Position, texCoord[2], texCoord[3]);
+	gl_TessLevelInner[0] = gl_TessLevelOuter[2];*/
+}
+

+ 98 - 0
Praxis3D/Data/Shaders/terrain3.tese

@@ -0,0 +1,98 @@
+#version 410 core
+
+layout(triangles, fractional_odd_spacing, cw) in;
+
+//uniform mat4 MVP;
+//uniform mat4 Modelview;
+
+in vec3 worldPos_CTRL[];
+in vec2 texCoord_CTRL[];
+in vec3 normal_CTRL[];
+in vec3 eyeDir_CTRL[];
+in mat3 TBN_CTRL[];
+
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+
+uniform mat4 MVP;
+uniform mat4 viewProjMat;
+
+uniform mat4 modelMat;
+uniform mat4 projMat;
+
+float TerrainHeightOffset = 100.0;
+float patchSize = 100.0;
+int scaleFactor = 2;
+int pixelsPerEdge = 1;
+float gridSpacing = 160.0;
+float heightStep = 0.1;
+
+#define sizeFactor 1.0 / patchSize
+
+float height(float u, float v)
+{
+	return (texture(diffuseTexture, vec2(u,v)).a  * heightStep);
+}
+
+vec2 interpolate(vec2 p_vec0, vec2 p_vec1, vec2 p_vec2)
+{
+	return vec2(gl_TessCoord.x) * p_vec0 + vec2(gl_TessCoord.y) * p_vec1 + vec2(gl_TessCoord.z) * p_vec2;
+}
+
+vec3 interpolate(vec3 p_vec0, vec3 p_vec1, vec3 p_vec2)
+{
+	return vec3(gl_TessCoord.x) * p_vec0 + vec3(gl_TessCoord.y) * p_vec1 + vec3(gl_TessCoord.z) * p_vec2;
+}
+
+void main()
+{
+	vec2 position0 = vec2(worldPos_CTRL[0].x, worldPos_CTRL[0].z);
+
+	ivec2 tSize = textureSize(diffuseTexture, 0) * scaleFactor;
+	float div = tSize.x * sizeFactor;
+
+	// Compute texture coordinates
+//	uvTE.s = (posTC[0].x + uv.s/div) ;
+//	uvTE.t = (posTC[0].y + uv.t/div) ;
+	//texCoord = position0.xy + gl_TessCoord.st/div;
+
+	// compute vertex position [0 .. tSize * gridSpacing]
+	vec4 res;
+	res.x = texCoord.s * tSize.x * gridSpacing;
+	res.z = texCoord.t * tSize.y * gridSpacing;
+	res.y = height(texCoord.s, texCoord.t);
+	res.w = 1.0;
+	
+
+	worldPos = interpolate(worldPos_CTRL[0], worldPos_CTRL[1], worldPos_CTRL[2]);
+	//texCoord = interpolate(texCoord_CTRL[0], texCoord_CTRL[1], texCoord_CTRL[2]);
+	//vec4 test = viewProjMat * vec4(worldPos, 1.0);
+	texCoord = vec2(worldPos.x, worldPos.z) * 0.0003 + 0.5;
+	normal = normalize(interpolate(normal_CTRL[0], normal_CTRL[1], normal_CTRL[2]));
+	eyeDir = interpolate(eyeDir_CTRL[0], eyeDir_CTRL[1], eyeDir_CTRL[2]);
+	//TBN = interpolate(TBN_CTRL[0], TBN_CTRL[1], TBN_CTRL[2]);
+
+	//float height = texture(diffuseTexture, texCoord).a;
+	//worldPos += normal * height * 3000.0;
+	worldPos.y += texture(diffuseTexture, texCoord).a * 10000.0;;
+	
+	//gl_Position = viewProjMat * res;
+	gl_Position = viewProjMat * vec4(worldPos, 1.0);
+	//gl_Position = projMat * vec4(worldPos, 1.0);
+	
+    //vec3 p0 = gl_TessCoord.x * tcPosition[0];
+    //vec3 p1 = gl_TessCoord.y * tcPosition[1];
+    //vec3 p2 = gl_TessCoord.z * tcPosition[2];
+	
+    //tePatchDistance = gl_TessCoord;
+    //tePosition = normalize(p0 + p1 + p2);
+	
+    //gl_Position = MVP * vec4(tePosition, 1);
+}

+ 53 - 0
Praxis3D/Data/Shaders/terrain3.vert

@@ -0,0 +1,53 @@
+#version 410 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+out vec3 tangent;
+out vec3 bitangent;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+
+void main(void)
+{
+	//temp_tangent = ( 0, 0, 1 )
+	//bitangent = cross( temp_tangent, normal )
+	//tangent = cross( normal, bitangent )
+	//float3x3 TBN = float3x3( tangent, bitangent, normal )
+
+	
+	//vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	mat3 normalMatrix = transpose(inverse(mat3(modelViewMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	//eyeDir = normalize(-viewPosition).xyz;
+	
+	vec3 tangent = vec3(0.0, 0.0, 1.0);
+	vec3 bitangent = cross(tangent, vertexNormal);
+	tangent = cross(normal, bitangent);
+	
+	TBN = mat3(normalMatrix * tangent, normalMatrix * bitangent, normalMatrix * vertexNormal);
+	//TBN = normalMatrix;
+
+	tangent = vertexTangent;
+	bitangent = vertexBitangent;
+	
+	//gl_Position = viewPosition;
+	//gl_Position = vec4(vertexPosition, 1.0);
+}

+ 59 - 0
Praxis3D/Data/Shaders/terrain4.frag

@@ -0,0 +1,59 @@
+#version 410 core
+
+// Some drivers require the following
+precision highp float;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+uniform sampler2D emissiveTexture;
+uniform sampler2D glossTexture;
+uniform sampler2D heightTexture;
+
+void main(void)
+{
+	//emissiveBuffer	= normal;
+
+	//vec4 emissiveTexture = texture(emissiveTexture, texCoord).rgba;
+	//if(emissiveTexture.a < 0.5)
+	//{
+	//	emissiveTexture = vec4(0.0);
+	//}
+	//emissiveBuffer	= emissiveTexture * emissiveTexture.a;
+	//diffuseBuffer	= texture(diffuseTexture, texCoord).rgba;
+	//texCoordBuffer	= vec4(texCoord, 0.0, 0.0);
+	//positionBuffer	= vec4(worldPos, 0.0);
+	//normalBuffer	= vec4(normalize(normal), 0.0);
+	
+	//vec2 newTexCoord = vec2(worldPos.x, worldPos.z);
+	vec2 newCoords = texCoord;
+	
+	//float height = texture(heightTexture, texCoord.st).r;
+	//float v = height * 0.04 - 0.02;
+	//vec2 newCoords = texCoord+(eyeDir.xy*v);
+	
+	float specularPower = texture(specularTexture, newCoords).r;
+	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
+	if(emissiveColor.a < 0.3)
+	{
+		emissiveColor = vec4(0.0);
+	}
+	emissiveBuffer	= emissiveColor * emissiveColor.a;
+
+	diffuseBuffer	= vec4(texture(diffuseTexture, newCoords).rgb, 0.0);	// Write the color from model's texture
+	positionBuffer	= vec4(worldPos, 0.0);						// Write fragment's position in world space
+	normalBuffer = vec4(TBN * normalize((2.0 * (texture(normalTexture, newCoords).rgb) - 1.0)), specularPower);
+}

+ 35 - 0
Praxis3D/Data/Shaders/terrain4.geom

@@ -0,0 +1,35 @@
+#version 410 core
+
+uniform mat4 MVP;
+//uniform mat3 NormalMatrix;
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+in vec3 tePosition[3];
+in vec3 tePatchDistance[3];
+
+out vec3 gFacetNormal;
+out vec3 gPatchDistance;
+out vec3 gTriDistance;
+
+void main()
+{
+    vec3 A = tePosition[2] - tePosition[0];
+    vec3 B = tePosition[1] - tePosition[0];
+    gFacetNormal = (MVP * vec4(normalize(cross(A, B)), 1.0)).xyz;
+    
+    gPatchDistance = tePatchDistance[0];
+    gTriDistance = vec3(1, 0, 0);
+    gl_Position = gl_in[0].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[1];
+    gTriDistance = vec3(0, 1, 0);
+    gl_Position = gl_in[1].gl_Position; EmitVertex();
+
+    gPatchDistance = tePatchDistance[2];
+    gTriDistance = vec3(0, 0, 1);
+    gl_Position = gl_in[2].gl_Position; EmitVertex();
+
+    EndPrimitive();
+}

+ 136 - 0
Praxis3D/Data/Shaders/terrain4.tesc

@@ -0,0 +1,136 @@
+#version 410 core
+
+layout(vertices = 3) out;
+
+//in vec3 vPosition[];
+//out vec3 tcPosition[];
+
+//#define ID gl_InvocationID
+
+in vec3 worldPos[];
+in vec2 texCoord[];
+in vec3 normal[];
+in vec3 eyeDir[];
+in mat3 TBN[];
+
+in vec3 tangent[];
+in vec3 bitangent[];
+
+out vec3 worldPos_CTRL[];
+out vec2 texCoord_CTRL[];
+out vec3 normal_CTRL[];
+out vec3 eyeDir_CTRL[];
+out mat3 TBN_CTRL[];
+
+out vec3 tangent_CTRL[];
+out vec3 bitangent_CTRL[];
+
+//in vec2 posV[];
+
+//out vec2 posTC[];
+
+uniform mat4 modelViewMat;
+uniform vec3 cameraPosVec;
+
+uniform sampler2D diffuseTexture;
+
+float TerrainHeightOffset = 100.0;
+float patchSize = 10.0;
+
+float calcTessLevel(float p_distance0, float p_distance1)
+{
+	float averageDistance = (p_distance0 + p_distance1) / 2.0;
+		
+	if(averageDistance <= 20.0)
+	{
+		return 10.0;
+	}
+		
+	if(averageDistance <= 200.0)
+	{
+		return 7.0;
+	}
+		
+		return 1.0;
+}
+
+float calcDistance(vec4 p0, vec4 p1, vec2 t0, vec2 t1)
+{
+	float samp = texture(diffuseTexture, t0).a;
+	p0.y = samp * TerrainHeightOffset;
+	samp = texture(diffuseTexture, t1).a;
+	p1.y = samp * TerrainHeightOffset;
+
+	vec4 view0 = modelViewMat * p0;
+	vec4 view1 = modelViewMat * p1;
+
+	float MinDepth = 10.0;
+	float MaxDepth = 100000.0;
+
+	float d0 = clamp( (abs(p0.z) - MinDepth) / (MaxDepth - MinDepth), 0.0, 1.0);
+	float d1 = clamp( (abs(p1.z) - MinDepth) / (MaxDepth - MinDepth), 0.0, 1.0);
+
+	float t = mix(64, 2, (d0 + d1) * 0.5);
+
+	if (t <= 2.0)
+	{ 
+		return 2.0;
+	}
+	if (t <= 4.0)
+	{ 
+		return 4.0;
+	}
+	if (t <= 8.0)
+	{ 
+		return 8.0;
+	}
+	if (t <= 16.0)
+	{ 
+		return 16.0;
+	}
+	if (t <= 32.0)
+	{ 
+		return 32.0;
+	}
+	
+	return 20.0;
+}
+
+void main()
+{	
+	worldPos_CTRL[gl_InvocationID] = worldPos[gl_InvocationID];
+	texCoord_CTRL[gl_InvocationID] = texCoord[gl_InvocationID];
+	normal_CTRL[gl_InvocationID] = normal[gl_InvocationID];
+	eyeDir_CTRL[gl_InvocationID] = eyeDir[gl_InvocationID];
+	TBN_CTRL[gl_InvocationID] = TBN[gl_InvocationID];
+	
+	tangent_CTRL[gl_InvocationID] = tangent[gl_InvocationID];
+	bitangent_CTRL[gl_InvocationID] = bitangent[gl_InvocationID];
+
+	//float TessLevelInner = 64;
+	//float TessLevelOuter = 64;
+    //tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];
+	
+	float distanceVert0 = distance(cameraPosVec, worldPos_CTRL[0]);
+	float distanceVert1 = distance(cameraPosVec, worldPos_CTRL[1]);
+	float distanceVert2 = distance(cameraPosVec, worldPos_CTRL[2]);
+	
+	//gl_TessLevelOuter[0] = calcTessLevel(distanceVert1, distanceVert2);
+	//gl_TessLevelOuter[1] = calcTessLevel(distanceVert2, distanceVert0);
+	//gl_TessLevelOuter[2] = calcTessLevel(distanceVert0, distanceVert1);
+	//gl_TessLevelInner[0] = gl_TessLevelOuter[2];
+	
+		// Outer tessellation level
+	//gl_TessLevelOuter[0] = calcDistance(gl_in[3].gl_Position, gl_in[0].gl_Position, texCoord[3], texCoord[0]);
+	//gl_TessLevelOuter[1] = calcDistance(gl_in[0].gl_Position, gl_in[1].gl_Position, texCoord[0], texCoord[1]);
+	//gl_TessLevelOuter[2] = calcDistance(gl_in[1].gl_Position, gl_in[2].gl_Position, texCoord[1], texCoord[2]);
+	//gl_TessLevelOuter[3] = calcDistance(gl_in[2].gl_Position, gl_in[3].gl_Position, texCoord[2], texCoord[3]);
+	//gl_TessLevelInner[0] = gl_TessLevelOuter[2];
+	
+	gl_TessLevelOuter[0] = 7.0;
+	gl_TessLevelOuter[1] = 7.0;
+	gl_TessLevelOuter[2] = 7.0;
+	gl_TessLevelOuter[3] = 7.0;
+	gl_TessLevelInner[0] = 7.0;
+}
+

+ 216 - 0
Praxis3D/Data/Shaders/terrain4.tese

@@ -0,0 +1,216 @@
+#version 410 core
+
+layout(triangles, fractional_odd_spacing, cw) in;
+
+//uniform mat4 MVP;
+//uniform mat4 Modelview;
+
+in vec3 worldPos_CTRL[];
+in vec2 texCoord_CTRL[];
+in vec3 normal_CTRL[];
+in vec3 eyeDir_CTRL[];
+in mat3 TBN_CTRL[];
+
+in vec3 tangent_CTRL[];
+in vec3 bitangent_CTRL[];
+
+out vec3 worldPos_TE;
+out vec2 texCoord_TE;
+out vec3 normal_TE;
+out vec3 eyeDir_TE;
+out mat3 TBN_TE;
+
+out vec3 tangent_TE;
+out vec3 bitangent_TE;
+
+uniform sampler2D diffuseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D specularTexture;
+
+uniform mat4 viewProjMat;
+
+uniform mat4 modelMat;
+uniform mat4 projMat;
+
+vec2 interpolate(vec2 p_vec0, vec2 p_vec1, vec2 p_vec2)
+{
+	return vec2(gl_TessCoord.x) * p_vec0 + vec2(gl_TessCoord.y) * p_vec1 + vec2(gl_TessCoord.z) * p_vec2;
+}
+
+vec3 interpolate(vec3 p_vec0, vec3 p_vec1, vec3 p_vec2)
+{
+	return vec3(gl_TessCoord.x) * p_vec0 + vec3(gl_TessCoord.y) * p_vec1 + vec3(gl_TessCoord.z) * p_vec2;
+}
+
+void main()
+{
+	worldPos_TE = interpolate(worldPos_CTRL[0], worldPos_CTRL[1], worldPos_CTRL[2]);
+	texCoord_TE = interpolate(texCoord_CTRL[0], texCoord_CTRL[1], texCoord_CTRL[2]);
+	//vec4 test = modelMat * vec4(worldPos_TE, 1.0);
+	//texCoord_TE = vec2(test.x, test.z) * 0.000002 + 0.5;
+	
+	normal_TE = normalize(interpolate(normal_CTRL[0], normal_CTRL[1], normal_CTRL[2]));
+	eyeDir_TE = interpolate(eyeDir_CTRL[0], eyeDir_CTRL[1], eyeDir_CTRL[2]);
+	//TBN_TE = interpolate(TBN_CTRL[0], TBN_CTRL[1], TBN_CTRL[2]);
+	
+	tangent_TE = interpolate(tangent_CTRL[0], tangent_CTRL[1], tangent_CTRL[2]);
+	bitangent_TE = interpolate(bitangent_CTRL[0], bitangent_CTRL[1], bitangent_CTRL[2]);
+
+	float height = texture(normalTexture, texCoord_TE).a;
+	//worldPos_TE.y += height * 200.0;
+	
+	float TEXHEIGHT = 2048.0;
+	float TEXWIDTH = 2048.0;
+	
+	float h0 = texture(normalTexture, texCoord_TE + vec2(1/TEXWIDTH, 0.0)).a;
+	float h1 = texture(normalTexture, texCoord_TE - vec2(1/TEXWIDTH, 0.0)).a;
+	float h2 = texture(normalTexture, texCoord_TE + vec2(0.00, 1/TEXHEIGHT)).a;
+	float h3 = texture(normalTexture, texCoord_TE - vec2(0.00, 1/TEXHEIGHT)).a;
+	
+	float hh0 = texture(normalTexture, texCoord_CTRL[0]).a;
+	float hh1 = texture(normalTexture, texCoord_CTRL[1]).a;
+	float hh2= texture(normalTexture, texCoord_CTRL[2]).a;
+	
+	const ivec3 off2 = ivec3(-1, 0, 1);
+	float s01 = textureOffset(normalTexture, texCoord_TE, off2.xy).a;
+	float s21 = textureOffset(normalTexture, texCoord_TE, off2.zy).a;
+	float s10 = textureOffset(normalTexture, texCoord_TE, off2.yx).a;
+	float s12 = textureOffset(normalTexture, texCoord_TE, off2.yz).a;
+	
+	vec3 pos0 = vec3(worldPos_TE.x, worldPos_TE.y + h0 * 200.0, worldPos_TE.z);
+	vec3 pos1 = vec3(worldPos_TE.x, worldPos_TE.y + h1 * 200.0, worldPos_TE.z);
+	vec3 pos2 = vec3(worldPos_TE.x, worldPos_TE.y + h2 * 200.0, worldPos_TE.z);
+	vec3 pos3 = vec3(worldPos_TE.x, worldPos_TE.y + h3 * 200.0, worldPos_TE.z);
+	
+	vec3 posss0 = worldPos_TE + normal_TE * h0 * 1.0;// - vec3(0.0, 0.0, 0.5);
+	vec3 posss1 = worldPos_TE + normal_TE * h1 * 1.0;// + vec3(0.0, 0.0, 0.5);
+	vec3 posss2 = worldPos_TE + normal_TE * h2 * 1.0;// - vec3(0.5, 0.0, 0.0);
+	vec3 posss3 = worldPos_TE + normal_TE * h3 * 1.0;// + vec3(0.5, 0.0, 0.0);
+	
+	vec3 poss0 = vec3(worldPos_CTRL[0].x, worldPos_CTRL[0].y + hh0 * 200.0, worldPos_CTRL[0].z);
+	vec3 poss1 = vec3(worldPos_CTRL[1].x, worldPos_CTRL[1].y + hh1 * 200.0, worldPos_CTRL[1].z);
+	vec3 poss2 = vec3(worldPos_CTRL[2].x, worldPos_CTRL[2].y + hh2 * 200.0, worldPos_CTRL[2].z);
+	
+	vec3 wpos0 = (worldPos_TE + vec3(-1.0, 0.0, 0.0)) + (normal_TE * s01 * 200.0);
+	vec3 wpos1 = (worldPos_TE + vec3(1.0, 0.0, 0.0)) + (normal_TE * s10 * 200.0);
+	vec3 wpos2 = (worldPos_TE + vec3(0.0, 0.0, 1.0)) + (normal_TE * s21 * 200.0);
+	vec3 wpos3 = (worldPos_TE + vec3(0.0, 0.0, -1.0)) + (normal_TE * s12 * 200.0);
+	
+	//float SCALE = displace_ratio;
+	worldPos_TE += normal_TE * height * 200.0;
+	gl_Position = viewProjMat * vec4(worldPos_TE, 1.0);
+
+	vec2 uv =  texCoord_TE;
+	vec2 du = vec2(1/TEXWIDTH, 0);
+	vec2 dv = vec2(0, 1/TEXHEIGHT);
+	float dhdu = (2/TEXWIDTH) * (texture(normalTexture, uv+du).a - texture(normalTexture, uv-du).a);
+	float dhdv = (2/TEXHEIGHT) * (texture(normalTexture, uv+dv).a - texture(normalTexture, uv-dv).a);
+	//normal_TE = normalize(normal_TE + tangent_TE * dhdu + bitangent_TE * dhdv);
+
+	//vec3 tangent_TE = vec3(0.0, 0.0, 1.0);
+	//vec3 bitangent_TE = cross(tangent_TE, normal_TE);
+	//tangent_TE = cross(normal_TE, bitangent_TE);
+	
+	
+	
+	//normal_TE = normalize(cross(wpos1 - wpos0, wpos2 - wpos0));
+	normal_TE = normalize(cross(normalize(wpos0 - wpos1), normalize(wpos2 - wpos3)));
+	//normal_TE = wpos0 - wpos1;
+	normal_TE = vec3(s01 - s10) * 100.0;
+		
+	//normal_TE = normalize(cross(pos0 + pos1, pos2 + pos3));
+	
+	//normal_TE = normalize(cross(poss0 * poss1, poss0 * poss2));
+	
+	//normal_TE = normalize(cross(pos0, pos1));
+	
+	vec3 norm0 = normalize(cross(pos0, pos1));
+	vec3 norm1 = normalize(cross(pos2, pos3));
+	
+	//normal_TE = normalize(cross(norm0, norm1));
+	//normal_TE = norm0;
+	
+	
+	
+
+	// You can actually calculate it without a cross product, 
+	// by using the "finite difference method" (or at least I think it is called in this way).
+
+	// Actually it is fast enough that I use it to calculate the normals on the fly in a vertex shader.
+
+	// # P.xy store the position for which we want to calculate the normals
+	// # height() here is a function that return the height at a point in the terrain
+
+	// read neightbor heights using an arbitrary small offset
+	
+	
+	//float hL = texture(normalTexture, texCoord_TE - vec2(1/TEXWIDTH, 0.0)).a * 200.0;
+	//float hR = texture(normalTexture, texCoord_TE + vec2(1/TEXWIDTH, 0.0)).a * 200.0;
+	//float hD = texture(normalTexture, texCoord_TE - vec2(0.0, 1/TEXHEIGHT)).a * 200.0;
+	//float hU = texture(normalTexture, texCoord_TE + vec2(0.0, 1/TEXHEIGHT)).a * 200.0;
+	
+	//height *= 200.0f;
+	
+	//vec3 off = vec3(1.0, 1.0, 0.0);
+	//float hL = height(P.xy - off.xz);
+	//float hR = height(P.xy + off.xz);
+	//float hD = height(P.xy - off.zy);
+	//float hU = height(P.xy + off.zy);
+
+	vec3 off = vec3(0.01, 0.01, 0.0);
+	float hL = texture(normalTexture, texCoord_TE - off.xz).a * 200.0;
+	float hR = texture(normalTexture, texCoord_TE + off.xz).a * 200.0;
+	float hD = texture(normalTexture, texCoord_TE - off.zy).a * 200.0;
+	float hU = texture(normalTexture, texCoord_TE + off.zy).a * 200.0;
+	
+	// deduce terrain normal_TE
+	vec3 N = vec3(0.0, 1.0, 0.0);
+	N.x = hL - hR;
+	N.y = hD - hU;
+	N.z = 2.0;
+	//normal_TE = normalize(N);
+
+	//Returns a normal_TE from a grid of heights
+	//float3 computeNormals( float h_A, float h_B, float h_C, float h_D, float h_N, float heightScale )
+	//{
+		//To make it easier we offset the points such that n is "0" height
+		vec3 va = vec3(0.0, 1.0, (hU - height));
+		vec3 vb = vec3(1.0, 0.0, (hR - height));
+		vec3 vc = vec3(0.0, -1.0, (hD - height));
+		vec3 vd = vec3(-1.0, 0.0, (hL - height));
+		
+		//cross products of each vector yields the normal_TE of each tri - return the average normal_TE of all 4 tris
+		vec3 average_n = ( cross(va, vb) + cross(vb, vc) + cross(vc, vd) + cross(vd, va) ) / -4;
+		//normal_TE = normalize(average_n);
+		//return normalize( average_n );
+	//}
+	
+	//normal_TE = normalize(( cross(posss0, posss1) + cross(posss1, posss2) + cross(posss2, posss3) + cross(posss3, posss0) ) / -4);
+	
+	//tangent_TE = vec3(0.0, 0.0, 1.0);
+	//bitangent_TE = cross(tangent_TE, normal_TE);
+	//tangent_TE = cross(normal_TE, bitangent_TE);
+	
+	mat3 normalMat = transpose(inverse(mat3(modelMat)));
+	TBN_TE = mat3(normalMat * tangent_TE, normalMat * bitangent_TE, normalMat * normal_TE);
+	
+	//vec3 normm0 = normalize(cross(hL, hR));
+	//vec3 normm1 = normalize(cross(hD, hU));
+	
+	//normal_TE = normalize(cross(normm0, normm1));
+	
+	// Dir = (B - A) x (C - A)
+	// Norm = Dir / len(Dir)
+	
+    //worldPos_TE = gl_Position.xyz;
+	//gl_Position = projMat * vec4(worldPos_TE, 1.0);
+	
+    //vec3 p0 = gl_TessCoord.x * tcPosition[0];
+    //vec3 p1 = gl_TessCoord.y * tcPosition[1];
+    //vec3 p2 = gl_TessCoord.z * tcPosition[2];
+	
+    //tePatchDistance = gl_TessCoord;
+    //tePosition = normalize(p0 + p1 + p2);
+	
+    //gl_Position = MVP * vec4(tePosition, 1);
+}

+ 35 - 0
Praxis3D/Data/Shaders/terrain4.vert

@@ -0,0 +1,35 @@
+#version 410 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+
+void main(void)
+{
+	//vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	//mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	//eyeDir = normalize(-viewPosition).xyz;
+	//TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	//gl_Position = MVP * vec4(vertexPosition, 1.0);
+}

+ 19 - 0
Praxis3D/Data/Shaders/test.frag

@@ -0,0 +1,19 @@
+//#version 150
+
+//out vec4 outColor;
+
+//void main()
+//{
+//   outColor = vec4(1.0, 1.0, 1.0, 1.0);
+//}
+
+#version 330 core
+
+in vec2 texCoord;
+
+out vec4 fragColor;
+
+void main() 
+{
+   fragColor = vec4(0.0, 1.0, 0.0, 1.0);
+}

+ 40 - 0
Praxis3D/Data/Shaders/test.vert

@@ -0,0 +1,40 @@
+//#version 150
+//
+//in vec2 position;
+//
+//void main()
+//{
+//    gl_Position = vec4(0.5, 0.5, 0.0, 1.0);
+//}
+
+#version 330 core
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec2 vertexUV;
+layout(location = 2) in vec3 vertexNormal;
+
+//in vec2 position;
+//in vec3 color;
+
+out vec3 Color;
+out vec2 texCoord;
+
+void main() 
+{
+	texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+	texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+
+	//vec3 pos = vertexPosition;
+	//pos.z -= 10.0;
+
+	//Color = vec3(vertexPosition.xy, 0.0);
+	//gl_Position =  vec4(pos, 1.0);
+	
+
+  // Color = color;
+   //gl_Position = vec4(position, 0.0, 1.0);
+  // Color = vec3(position, 0.0);
+  // gl_Position = vec4(1.0, 1.0, 0.0, 1.0);
+}

+ 232 - 0
Praxis3D/Data/Shaders/test2.frag

@@ -0,0 +1,232 @@
+#version 330 core
+
+//uniform vec3 cameraPosVec;
+uniform vec3 cameraTargetVec;
+uniform float elapsedTime;
+uniform vec4 testVec;
+
+//in vec2 texCoord;
+
+out vec4 fragColor;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+in vec3 viewDir;
+in vec3 position;
+
+//void main() 
+//{
+//   fragColor = vec4(0.0, texCoord, 1.0);
+//}
+
+vec3      iResolution = vec3(1600, 900, 0);           // viewport resolution (in pixels)
+float     iGlobalTime = elapsedTime;           // shader playback time (in seconds)
+uniform float     iChannelTime[4];       // channel playback time (in seconds)
+uniform vec3      iChannelResolution[4]; // channel resolution (in pixels)
+vec4      iMouse = vec4(0.0, 0.0, 0.0, 0.0);                // mouse pixel coords. xy: current (if MLB down), zw: click
+uniform sampler2D iChannel0;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel1;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel2;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel3;			 // input channel. XX = 2D/Cube
+uniform vec4      iDate;                 // (year, month, day, time in seconds)
+uniform float     iSampleRate;           // sound sample rate (i.e., 44100)
+
+
+const int NUM_STEPS = 8;
+const float PI	 	= 3.1415;
+const float EPSILON	= 1e-3;
+float EPSILON_NRM	= 0.1 / iResolution.x;
+
+// sea
+const int ITER_GEOMETRY = 3;
+const int ITER_FRAGMENT = 5;
+const float SEA_HEIGHT = 0.6;
+const float SEA_CHOPPY = 4.0;
+const float SEA_SPEED = 0.8;
+const float SEA_FREQ = 0.16;
+const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
+const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
+float SEA_TIME = iGlobalTime * SEA_SPEED;
+mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
+
+// math
+mat3 fromEuler(vec3 ang) {
+	vec2 a1 = vec2(sin(ang.x),cos(ang.x));
+    vec2 a2 = vec2(sin(ang.y),cos(ang.y));
+    vec2 a3 = vec2(sin(ang.z),cos(ang.z));
+    mat3 m;
+    m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
+	m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
+	m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
+	return m;
+}
+float hash( vec2 p ) {
+	float h = dot(p,vec2(127.1,311.7));	
+    return fract(sin(h)*43758.5453123);
+}
+float noise( in vec2 p ) {
+    vec2 i = floor( p );
+    vec2 f = fract( p );	
+	vec2 u = f*f*(3.0-2.0*f);
+    return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), 
+                     hash( i + vec2(1.0,0.0) ), u.x),
+                mix( hash( i + vec2(0.0,1.0) ), 
+                     hash( i + vec2(1.0,1.0) ), u.x), u.y);
+}
+
+// lighting
+float diffuse(vec3 n,vec3 l,float p) {
+    return pow(dot(n,l) * 0.4 + 0.6,p);
+}
+float specular(vec3 n,vec3 l,vec3 e,float s) {    
+    float nrm = (s + 8.0) / (3.1415 * 8.0);
+    return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
+}
+
+// sky
+vec3 getSkyColor(vec3 e) {
+    e.y = max(e.y,0.0);
+    vec3 ret;
+    ret.x = pow(1.0-e.y,2.0);
+    ret.y = 1.0-e.y;
+    ret.z = 0.6+(1.0-e.y)*0.4;
+    return ret;
+}
+
+// sea
+float sea_octave(vec2 uv, float choppy) {
+    uv += noise(uv);        
+    vec2 wv = 1.0-abs(sin(uv));
+    vec2 swv = abs(cos(uv));    
+    wv = mix(wv,swv,wv);
+    return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
+}
+
+float map(vec3 p) {
+    float freq = SEA_FREQ;
+    float amp = SEA_HEIGHT;
+    float choppy = SEA_CHOPPY;
+    vec2 uv = p.xz; uv.x *= 0.75;
+    
+    float d, h = 0.0;    
+    for(int i = 0; i < ITER_GEOMETRY; i++) {        
+    	d = sea_octave((uv+SEA_TIME)*freq,choppy);
+    	d += sea_octave((uv-SEA_TIME)*freq,choppy);
+        h += d * amp;        
+    	uv *= octave_m; freq *= 1.9; amp *= 0.22;
+        choppy = mix(choppy,1.0,0.2);
+    }
+    return p.y - h;
+}
+
+float map_detailed(vec3 p) {
+    float freq = SEA_FREQ;
+    float amp = SEA_HEIGHT;
+    float choppy = SEA_CHOPPY;
+    vec2 uv = p.xz; uv.x *= 0.75;
+    
+    float d, h = 0.0;    
+    for(int i = 0; i < ITER_FRAGMENT; i++) {        
+    	d = sea_octave((uv+SEA_TIME)*freq,choppy);
+    	d += sea_octave((uv-SEA_TIME)*freq,choppy);
+        h += d * amp;        
+    	uv *= octave_m; freq *= 1.9; amp *= 0.22;
+        choppy = mix(choppy,1.0,0.2);
+    }
+    return p.y - h;
+}
+
+vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {  
+    float fresnel = 1.0 - max(dot(n,-eye),0.0);
+    fresnel = pow(fresnel,3.0) * 0.65;
+        
+    vec3 reflected = getSkyColor(reflect(eye,n));    
+    vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; 
+    
+    vec3 color = mix(refracted,reflected,fresnel);
+    
+    float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
+    color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
+    
+    color += vec3(specular(n,l,eye,60.0));
+    
+    return color;
+}
+
+// tracing
+vec3 getNormal(vec3 p, float eps) {
+    vec3 n;
+    n.y = map_detailed(p);    
+    n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
+    n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
+    n.y = eps;
+    return normalize(n);
+}
+
+float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {  
+    float tm = 0.0;
+    float tx = 1000.0;    
+    float hx = map(ori + dir * tx);
+    if(hx > 0.0) return tx;   
+    float hm = map(ori + dir * tm);    
+    float tmid = 0.0;
+    for(int i = 0; i < NUM_STEPS; i++) {
+        tmid = mix(tm,tx, hm/(hm-hx));                   
+        p = ori + dir * tmid;                   
+    	float hmid = map(p);
+		if(hmid < 0.0) {
+        	tx = tmid;
+            hx = hmid;
+        } else {
+            tm = tmid;
+            hm = hmid;
+        }
+    }
+    return tmid;
+}
+
+// main
+void main() 
+{
+	vec2 uv = gl_FragCoord.xy / iResolution.xy;
+    uv = uv * 2.0 - 1.0;
+    uv.x *= iResolution.x / iResolution.y;    
+    float time = iGlobalTime * 0.3 + iMouse.x*0.01;
+        
+    // ray
+    //vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);   
+	vec3 ang = -cameraTargetVec;
+    //vec3 ori = vec3(0.0,3.5,5.0);
+	vec3 ori = vec3(-position.x, position.y, -position.z);
+    vec3 dir = normalize(vec3(uv.xy,-2.0)); 
+	//dir.z += length(uv) * 0.00015;
+    dir = normalize(dir) * fromEuler(ang);
+    
+	//dir = cameraTargetVec;
+	
+    // tracing
+    vec3 p;
+    heightMapTracing(ori,dir,p);
+    vec3 dist = p - ori;
+    vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
+    vec3 light = normalize(vec3(0.0,1.0,0.8)); 
+             
+    // color
+    vec3 color = mix(
+        getSkyColor(dir),
+        getSeaColor(p,n,light,dir,dist),
+    	pow(smoothstep(0.0,-0.05,dir.y+0.08),0.3));
+        
+    // post
+	diffuseBuffer = vec4(pow(color,vec3(0.75)), 1.0);
+}

+ 55 - 0
Praxis3D/Data/Shaders/test2.vert

@@ -0,0 +1,55 @@
+#version 330 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+out vec3 position;
+
+out vec3 viewDir;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+uniform vec3 cameraPosVec;
+
+void main(void)
+{
+	viewDir = (modelViewMat * vec4(1.0, 0.0, 0.0, 0.0)).xyz;
+	
+	//vec4 cs_position = (modelViewMat * vec4(vertexPosition, 1.0)).xyz;
+    //position = (modelViewMat * vec4(cameraPosVec, 1.0)).xyz;
+	position = cameraPosVec;
+	
+	//position = (modelMat * vec4(cameraPosVec, 1.0)).xyz;
+	
+	vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	eyeDir = normalize(-viewPosition).xyz;
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}
+
+//void main()
+//{
+//	texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+//	texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+//
+//	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+//}

+ 32 - 0
Praxis3D/Data/Shaders/test3.frag

@@ -0,0 +1,32 @@
+//#version 150
+
+//out vec4 outColor;
+
+//void main()
+//{
+//   outColor = vec4(1.0, 1.0, 1.0, 1.0);
+//}
+
+#version 330 core
+
+//in vec2 texCoord;
+in vec3 color;
+in vec2 texCoord;
+
+out vec4 fragColor;
+
+//layout(location = 0) out vec4 positionBuffer;
+//layout(location = 1) out vec4 diffuseBuffer;
+//layout(location = 2) out vec4 normalBuffer;
+//layout(location = 3) out vec4 texCoordBuffer;
+//layout(location = 4) out vec4 emissiveBuffer;
+
+uniform sampler2D diffuseTexture;
+
+void main() 
+{
+	//vec4 textureColor = texture(diffuseTexture, texCoord).rgba;
+	//fragColor = vec4(textureColor.xyz, 1.0);
+	fragColor = texture(diffuseTexture, texCoord).rgba;
+	//fragColor = vec4(1.0, 0.0, 0.0, 1.0);
+}

+ 52 - 0
Praxis3D/Data/Shaders/test3.vert

@@ -0,0 +1,52 @@
+//#version 150
+//
+//in vec2 position;
+//
+//void main()
+//{
+//    gl_Position = vec4(0.5, 0.5, 0.0, 1.0);
+//}
+
+#version 330 core
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 vertexUV;
+
+//in vec2 position;
+//in vec3 color;
+
+uniform mat4 MVP;
+
+out vec3 color;
+out vec2 texCoord;
+
+void main() 
+{
+
+	//texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+	//texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+
+	//gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+
+	//texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+	//texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+
+	//gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+
+	color = vec3(vertexUV, 0.0);
+	texCoord = vertexUV;
+	vec3 pos = vertexPosition;
+	//pos.z += 0.9;
+
+	//pos /= vec3(2.0, 2.0, 2.0);
+
+	//Color = vec3(vertexPosition.xy, 0.0);
+	gl_Position = MVP * vec4(pos, 1.0);
+	
+
+  // Color = color;
+   //gl_Position = vec4(position, 0.0, 1.0);
+  // Color = vec3(position, 0.0);
+  // gl_Position = vec4(1.0, 1.0, 0.0, 1.0);
+}

+ 444 - 0
Praxis3D/Data/Shaders/water.frag

@@ -0,0 +1,444 @@
+#version 330 core
+
+//uniform vec3 cameraPosVec;
+uniform vec3 cameraTargetVec;
+uniform float elapsedTime;
+uniform vec4 testVec;
+
+//in vec2 texCoord;
+
+out vec4 fragColor;
+
+// Geometry buffers
+layout(location = 0) out vec4 positionBuffer;
+layout(location = 1) out vec4 diffuseBuffer;
+layout(location = 2) out vec4 normalBuffer;
+layout(location = 3) out vec4 emissiveBuffer;
+
+// Variables from vertex shader
+in vec3 worldPos;
+in vec2 texCoord;
+in vec3 normal;
+in vec3 eyeDir;
+in mat3 TBN;
+in vec3 viewDir;
+in vec3 position;
+
+//void main() 
+//{
+//   fragColor = vec4(0.0, texCoord, 1.0);
+//}
+
+vec3      iResolution = vec3(1600, 900, 0);           // viewport resolution (in pixels)
+float     iGlobalTime = elapsedTime;           // shader playback time (in seconds)
+uniform float     iChannelTime[4];       // channel playback time (in seconds)
+uniform vec3      iChannelResolution[4]; // channel resolution (in pixels)
+vec4      iMouse = vec4(0.0, 0.0, 0.0, 0.0);                // mouse pixel coords. xy: current (if MLB down), zw: click
+uniform sampler2D iChannel0;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel1;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel2;			 // input channel. XX = 2D/Cube
+uniform sampler2D iChannel3;			 // input channel. XX = 2D/Cube
+uniform vec4      iDate;                 // (year, month, day, time in seconds)
+uniform float     iSampleRate;           // sound sample rate (i.e., 44100)
+
+
+// "Wavescape" by dr2 - 2015
+// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
+
+// Water waves, including what the fish sees.
+
+// Acknowledgments: thanks for the following -
+//  Dave_H's multilayered clouds with nimitz's variable layer spacing
+//    (but depends on elevation rather than distance).
+//  Wave shapes from TDM; they seem a little more "energetic" than TekF's.
+//  Raymarching with binary subdivision, as used by Dave_H for mountains;
+//    TekF and TDM use one or the other, not both.
+//  Buoy based on TekF's, but raymarched for generality; shows aging effects
+//    below waterline.
+//  Foam idea from TekF.
+//  Noise functions from iq.
+
+const float pi = 3.14159;
+const vec4 cHashA4 = vec4 (0., 1., 57., 58.);
+const vec3 cHashA3 = vec3 (1., 57., 113.);
+const float cHashM = 43758.54;
+
+vec2 Hashv2f (float p)
+{
+  return fract (sin (p + cHashA4.xy) * cHashM);
+}
+
+vec4 Hashv4f (float p)
+{
+  return fract (sin (p + cHashA4) * cHashM);
+}
+
+vec4 Hashv4v3 (vec3 p)
+{
+  const vec3 cHashVA3 = vec3 (37.1, 61.7, 12.4);
+  const vec3 e = vec3 (1., 0., 0.);
+  return fract (sin (vec4 (dot (p + e.yyy, cHashVA3), dot (p + e.xyy, cHashVA3),
+     dot (p + e.yxy, cHashVA3), dot (p + e.xxy, cHashVA3))) * cHashM);
+}
+
+float Noiseff (float p)
+{
+  float i, f;
+  i = floor (p);  f = fract (p);
+  f = f * f * (3. - 2. * f);
+  vec2 t = Hashv2f (i);
+  return mix (t.x, t.y, f);
+}
+
+float Noisefv2 (vec2 p)
+{
+  vec2 i, f;
+  i = floor (p);  f = fract (p);
+  f = f * f * (3. - 2. * f);
+  vec4 t = Hashv4f (dot (i, cHashA3.xy));
+  return mix (mix (t.x, t.y, f.x), mix (t.z, t.w, f.x), f.y);
+}
+
+float Noisefv3a (vec3 p)
+{
+  vec3 i, f;
+  i = floor (p);  f = fract (p);
+  f *= f * (3. - 2. * f);
+  vec4 t1 = Hashv4v3 (i);
+  vec4 t2 = Hashv4v3 (i + vec3 (0., 0., 1.));
+  return mix (mix (mix (t1.x, t1.y, f.x), mix (t1.z, t1.w, f.x), f.y),
+              mix (mix (t2.x, t2.y, f.x), mix (t2.z, t2.w, f.x), f.y), f.z);
+}
+
+float Fbm1 (float p)
+{
+  float f, a;
+  f = 0.;  a = 1.;
+  for (int i = 0; i < 5; i ++) {
+    f += a * Noiseff (p);
+    a *= 0.5;  p *= 2.;
+  }
+  return f;
+}
+
+float Fbm3 (vec3 p)
+{
+  const mat3 mr = mat3 (0., 0.8, 0.6, -0.8, 0.36, -0.48, -0.6, -0.48, 0.64);
+  float f, a, am, ap;
+  f = 0.;  a = 0.5;
+  am = 0.5;  ap = 4.;
+  p *= 0.5;
+  for (int i = 0; i < 6; i ++) {
+    f += a * Noisefv3a (p);
+    p *= mr * ap;  a *= am;
+  }
+  return f;
+}
+
+float Fbmn (vec3 p, vec3 n)
+{
+  vec3 f = vec3 (0.);
+  float a = 1.;
+  for (int i = 0; i < 5; i ++) {
+    f += a * vec3 (Noisefv2 (p.yz), Noisefv2 (p.zx), Noisefv2 (p.xy));
+    a *= 0.5;  p *= 2.;
+  }
+  return dot (f, abs (n));
+}
+
+vec3 VaryNf (vec3 p, vec3 n, float f)
+{
+  vec3 e = vec3 (0.2, 0., 0.);
+  float s = Fbmn (p, n);
+  vec3 g = vec3 (Fbmn (p + e.xyy, n) - s,
+     Fbmn (p + e.yxy, n) - s, Fbmn (p + e.yyx, n) - s);
+  return normalize (n + f * (g - n * dot (n, g)));
+}
+
+float SmoothBump (float lo, float hi, float w, float x)
+{
+  return (1. - smoothstep (hi - w, hi + w, x)) * smoothstep (lo - w, lo + w, x);
+}
+
+float PrSphDf (vec3 p, float s)
+{
+  return length (p) - s;
+}
+
+float PrCylDf (vec3 p, float r, float h)
+{
+  return max (length (p.xy) - r, abs (p.z) - h);
+}
+
+int idObj;
+mat3 ballMat;
+vec3 qHit, ballPos, sunCol, sunDir, cloudDisp, waterDisp;
+float tCur, fCloud;
+const float dstFar = 300.;
+
+vec3 SkyGrndCol (vec3 ro, vec3 rd)
+{
+  vec3 p, q, cSun, skyBg, clCol, col;
+  float colSum, attSum, s, att, a, dDotS, ds;
+  const vec3 cCol1 = 0.5 * vec3 (0.15, 0.2, 0.4),
+     cCol2 = 0.5 * vec3 (0.25, 0.5, 0.7), gCol = 1.3 * vec3 (0.05, 0.08, 0.05);
+  const float cloudLo = 100., cloudRngI = 1./50., atFac = 0.06;
+  const int nLay = 30;
+  if (rd.y < 0.015 * Fbm1 (16. * rd.x)) col = gCol * (0.5 + 0.5 *
+     Noisefv2 (1000. * vec2 (5. * atan (rd.x, rd.z), rd.y)));
+  else {
+    fCloud = clamp (fCloud, 0., 1.);
+    dDotS = max (dot (rd, sunDir), 0.);
+    ro += cloudDisp;
+    p = ro;
+    p.xz += (cloudLo - p.y) * rd.xz / rd.y;
+    p.y = cloudLo;
+    ds = 1. / (cloudRngI * rd.y * (2. - rd.y) * float (nLay));
+    colSum = 0.;  attSum = 0.;
+    s = 0.;  att = 0.;
+    for (int j = 0; j < nLay; j ++) {
+      q = p + rd * s;
+      q.z *= 0.7;
+      att += atFac * max (fCloud - Fbm3 (0.02 * q), 0.);
+      a = (1. - attSum) * att;
+      colSum += a * (q.y - cloudLo) * cloudRngI;
+      attSum += a;  s += ds;
+      if (attSum >= 1.) break;
+    }
+    colSum += 0.5 * min ((1. - attSum) * pow (dDotS, 3.), 1.);
+    clCol = vec3 (1.) * colSum + 0.05 * sunCol;
+    cSun = sunCol * clamp ((min (pow (dDotS, 1500.) * 2., 1.) +
+       min (pow (dDotS, 10.) * 0.75, 1.)), 0., 1.);
+    skyBg = mix (cCol1, cCol2, 1. - rd.y);
+    col = clamp (mix (skyBg + cSun, 1.6 * clCol, attSum), 0., 1.);
+  }
+  return col;
+}
+
+vec3 SeaFloorCol (vec3 rd)
+{
+  vec2 p;
+  float w, f;
+  p = 5. * rd.xz / rd.y;
+  w = 1.;
+  f = 0.;
+  for (int j = 0; j < 4; j ++) {
+    f += w * Noisefv2 (p);
+    w *= 0.5;  p *= 2.;
+  }
+  return mix (vec3 (0.01, 0.04, 0.02), vec3 (0, 0.05, 0.05), 
+     smoothstep (0.4, 0.7, f));
+}
+
+float WaveHt (vec3 p)
+{
+  const mat2 qRot = mat2 (1.6, -1.2, 1.2, 1.6);
+  vec4 t4, ta4, v4;
+  vec2 q2, t2, v2;
+  float wFreq, wAmp, pRough, ht;
+  wFreq = 0.16;  wAmp = 0.6;  pRough = 5.;
+  q2 = p.xz + waterDisp.xz;
+  ht = 0.;
+  for (int j = 0; j < 5; j ++) {
+    t2 = 1.1 * tCur * vec2 (1., -1.);
+    t4 = vec4 (q2 + t2.xx, q2 + t2.yy) * wFreq;
+    t2 = vec2 (Noisefv2 (t4.xy), Noisefv2 (t4.zw));
+    t4 += 2. * vec4 (t2.xx, t2.yy) - 1.;
+    ta4 = abs (sin (t4));
+    v4 = (1. - ta4) * (ta4 + abs (cos (t4)));
+    v2 = pow (1. - pow (v4.xz * v4.yw, vec2 (0.65)), vec2 (pRough));
+    ht += (v2.x + v2.y) * wAmp;
+    q2 *= qRot;  wFreq *= 1.9;  wAmp *= 0.22;
+    pRough = 0.8 * pRough + 0.2;
+  }
+  return ht;
+}
+
+float WaveRay (vec3 ro, vec3 rd)
+{
+  vec3 p;
+  float dHit, h, s, sLo, sHi;
+  s = 0.;
+  sLo = 0.;
+  dHit = dstFar;
+  for (int j = 0; j < 150; j ++) {
+    p = ro + s * rd;
+    h = p.y - WaveHt (p);
+    if (h < 0.) break;
+    sLo = s;
+    s += max (0.2, h) + 0.005 * s;
+    if (s > dstFar) break;
+  }
+  if (h < 0.) {
+    sHi = s;
+    for (int j = 0; j < 7; j ++) {
+      s = 0.5 * (sLo + sHi);
+      p = ro + s * rd;
+      h = step (0., p.y - WaveHt (p));
+      sLo += h * (s - sLo);
+      sHi += (1. - h) * (s - sHi);
+    }
+    dHit = sHi;
+  }
+  return dHit;
+}
+
+float WaveOutRay (vec3 ro, vec3 rd)
+{
+  vec3 p;
+  float dHit, h, s, sLo, sHi;
+  s = 0.;
+  sLo = 0.;
+  dHit = dstFar;
+  ro.y *= -1.;
+  rd.y *= -1.;
+  for (int j = 0; j < 150; j ++) {
+    p = ro + s * rd;
+    h = p.y + WaveHt (p);
+    if (h < 0.) break;
+    sLo = s;
+    s += max (0.2, h) + 0.005 * s;
+    if (s > dstFar) break;
+  }
+  if (h < 0.) {
+    sHi = s;
+    for (int j = 0; j < 7; j ++) {
+      s = 0.5 * (sLo + sHi);
+      p = ro + s * rd;
+      h = step (0., p.y + WaveHt (p));
+      sLo += h * (s - sLo);
+      sHi += (1. - h) * (s - sHi);
+    }
+    dHit = sHi;
+  }
+  return dHit;
+}
+
+vec3 WaveNf (vec3 p, float d)
+{
+  vec2 e = vec2 (max (0.1, 5e-5 * d * d), 0.);
+  float h = WaveHt (p);
+  return normalize (vec3 (h - WaveHt (p + e.xyy), e.x, h - WaveHt (p + e.yyx)));
+}
+
+vec3 ShowScene (vec3 ro, vec3 rd)
+{
+  vec3 col, vn, rdd, refCol, uwatCol;
+  float dstHit, dstWat, dif, bk, sh, foamFac;
+  const float eta = 0.75, att = 0.5;
+  int idObjT;
+  bool doReflect;
+  if (ro.y > WaveHt (ro)) {
+    dstWat = WaveRay (ro, rd);
+    idObj = -1;
+    if (idObj < 0) dstHit = dstFar;
+    doReflect = (dstWat < dstFar && dstWat < dstHit);
+    if (doReflect) {
+      ro += rd * dstWat;
+      vn = WaveNf (ro, dstWat);
+      rdd = rd;
+      rd = reflect (rd, vn);
+      idObj = -1;
+      if (idObj < 0) dstHit = dstFar;
+    }
+    col = SkyGrndCol (ro, rd);
+    if (doReflect) {
+      refCol = col;
+      rd = refract (rdd, vn, eta);
+      idObj = -1;
+      if (idObj < 0) dstHit = dstFar;
+      col = SeaFloorCol (rd);
+      col = mix (col, 0.8 * refCol, pow (1. - abs (dot (rdd, vn)), 5.));
+      foamFac = pow (clamp (WaveHt (ro) +
+         0.004 * Fbm3 (256. * ro) - 0.65, 0., 1.), 8.);
+      col = mix (col, vec3 (1.), foamFac);
+    }
+  } else {
+    uwatCol = vec3 (0., 0.05, 0.05) +
+       step (0.4, Fbm1 (20. * tCur)) * vec3 (0.02, 0.02, 0.03);
+    col = uwatCol;
+    dstWat = WaveOutRay (ro, rd);
+    idObj = -1;
+    if (idObj < 0) dstHit = dstFar;
+    if (dstWat < dstFar && dstWat < dstHit) {
+      ro += rd * dstWat;
+      vn = - WaveNf (ro, dstWat);
+      rdd = refract (rd, vn, 1. / eta);
+      if (length (rdd) > 0.) rd = rdd;
+      else rd = reflect (rd, vn);
+      idObj = -1;
+      if (idObj < 0) dstHit = dstFar;
+      if (rd.y > 0.) col = mix (uwatCol, 0.9 * SkyGrndCol (ro, rd),
+         exp (- 0.07 * att * dstWat));
+    } 
+  }
+  return col;
+}
+
+void BallPM ()
+{
+  const vec3 e = vec3 (1., 0., 0.);
+  float h[5], b;
+  ballPos = vec3 (0., 0., 0.);
+  h[0] = WaveHt (ballPos);
+  h[1] = WaveHt (ballPos + e.yyx);  h[2] = WaveHt (ballPos - e.yyx);
+  h[3] = WaveHt (ballPos + e);  h[4] = WaveHt (ballPos - e);
+  ballPos.y = 0.5 + (2. * h[0] + h[1] + h[2] + h[3] + h[4]) / 9.;
+  b = (h[1] - h[2]) / (4. * e.x);
+  ballMat[2] = normalize (vec3 (0., b, 1.));
+  b = (h[3] - h[4]) / (4. * e.x);
+  ballMat[1] = normalize (cross (ballMat[2], vec3 (1., b, 0.)));
+  ballMat[0] = cross (ballMat[1], ballMat[2]);
+}
+
+void main()
+{
+  vec2 uv = 2. * gl_FragCoord.xy / iResolution.xy - 1.;
+  uv.x *= iResolution.x / iResolution.y;
+  tCur = iGlobalTime;
+  vec4 mPtr = iMouse;
+  mPtr.xy = mPtr.xy / iResolution.xy - 0.5;
+  mat3 vuMat;
+  vec3 col, ro, rd;
+  vec2 vEl, vAz;
+  float el, az, zmFac, a, tPer, tSeq;
+  cloudDisp = 10. * tCur * vec3 (1., 0., 1.);
+  waterDisp = 0.5 * tCur * vec3 (-1., 0., 1.);
+  sunDir = normalize (vec3 (0.2, 0.5, 0.5));
+  sunCol = vec3 (1., 0.4, 0.3) + vec3 (0., 0.5, 0.2) * sunDir.y;
+  fCloud = 0.5 + 0.2 * sin (0.022 * 2. * pi * tCur);
+  zmFac = 3.5;
+  tPer = 35.;
+  if (mPtr.z <= 0.) {
+    az = 0.01 * tCur;
+    el = 0.2 * pi;
+    tSeq = mod (tCur, tPer);
+    if (mod (floor (tCur / tPer), 2.) == 0.) {
+      a = SmoothBump (10., 30., 5., tSeq);
+      zmFac -= 0.1 * a;
+      el -= 0.19 * pi * a;
+    } else {
+      a = SmoothBump (8., 26., 8., tSeq);
+      zmFac -= 0.05 * a;
+      el -= 0.55 * pi * a;
+    }
+  } else {
+    az = 1.1 * pi * mPtr.x;
+    el = 0.02 * pi - 0.7 * pi * mPtr.y;
+	
+	//az = cameraTargetVec.x;
+	//el = cameraTargetVec.y;
+	
+  }
+  vEl = vec2 (cos (el), sin (el));
+  vAz = vec2 (cos (az), sin (az));
+  rd = normalize (vec3 (uv, zmFac));
+  vuMat = mat3 (1., 0., 0., 0., vEl.x, - vEl.y, 0., vEl.y, vEl.x) *
+     mat3 (vAz.x, 0., vAz.y, 0., 1., 0., - vAz.y, 0., vAz.x);
+  rd = rd * vuMat;
+  ro = vec3 (0., 0., -20.) * vuMat;
+  ro.y += 2.;
+  BallPM ();
+  col = ShowScene (ro, rd);
+  diffuseBuffer = vec4 (sqrt (clamp (col, 0., 1.)), 1.);
+}

+ 55 - 0
Praxis3D/Data/Shaders/water.vert

@@ -0,0 +1,55 @@
+#version 330 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 textureCoord;
+layout(location = 3) in vec3 vertexTangent;
+layout(location = 4) in vec3 vertexBitangent;
+
+// Variables passed to fragment shader
+out vec3 worldPos;
+out vec2 texCoord;
+out vec3 normal;
+out vec3 eyeDir;
+out mat3 TBN;
+out vec3 position;
+
+out vec3 viewDir;
+
+uniform mat4 MVP;
+uniform mat4 modelMat;
+uniform mat4 modelViewMat;
+uniform mat4 projMat;
+uniform vec3 cameraPosVec;
+
+void main(void)
+{
+	viewDir = (modelViewMat * vec4(1.0, 0.0, 0.0, 0.0)).xyz;
+	
+	//vec4 cs_position = (modelViewMat * vec4(vertexPosition, 1.0)).xyz;
+    //position = (modelViewMat * vec4(cameraPosVec, 1.0)).xyz;
+	position = cameraPosVec;
+	
+	//position = (modelMat * vec4(cameraPosVec, 1.0)).xyz;
+	
+	vec4 viewPosition = projMat * modelViewMat * vec4(vertexPosition,1.0);
+	mat3 normalMatrix = transpose(inverse(mat3(modelMat)));
+	
+    worldPos = (modelMat * vec4(vertexPosition, 1.0)).xyz;
+	texCoord = textureCoord;
+	normal = (modelMat * vec4(vertexNormal, 0.0)).xyz;
+	eyeDir = normalize(-viewPosition).xyz;
+	TBN = mat3(normalMatrix * vertexTangent, normalMatrix * vertexBitangent, normalMatrix * vertexNormal);
+
+	//gl_Position = viewPosition;
+	gl_Position = MVP * vec4(vertexPosition, 1.0);
+}
+
+//void main()
+//{
+//	texCoord.x = (gl_VertexID == 2) ?  2.0 :  0.0;
+//	texCoord.y = (gl_VertexID == 1) ?  2.0 :  0.0;
+//
+//	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
+//}

+ 19 - 0
Praxis3D/Data/config.ini

@@ -0,0 +1,19 @@
+window_position_x 290
+window_position_y 130
+window_size_windowed_x 1600
+window_size_windowed_y 900
+window_size_fullscreen_x 1920
+window_size_fullscreen_y 1080
+window_fullscreen 0
+gl_context_major_version 4
+gl_context_minor_version 4
+vertical_sync 1
+default_texture default.png
+generate_mipmaps 1
+gl_texture_anisotropy 16
+camera_freelook_speed 25
+face_culling 0
+fov 80
+z_near 0.1
+z_far 40000
+default_map default_lite.pmap

+ 59 - 0
Praxis3D/Data/error-strings-eng.data

@@ -0,0 +1,59 @@
+{
+	"Error Codes":
+	{
+		"Undefined"					: "Undefined",
+		"Success"					: "Succeed",
+		"Failure"					: "Failed",
+		"Destroy_obj_not_found"		: "Destruction: object not found",
+		"Glew_failed"				: "OpenGL Extension Wrangler Library has failed to initialize",
+		"Ifstream_failed"			: "Unable to read input file stream",
+		"Clock_QueryFrequency"		: "Unable to query the clock frequency",
+		"Framebuffer_failed"		: "Framebuffer has failed to load",
+		"Geometrybuffer_failed"		: "Geometry buffer has failed to load",
+		"AssimpScene_failed"		: "Assimp scene has failed to load",
+		"ObjectPool_full"			: "Object pool overflow",
+		"Property_no_filename"		: "No filename specified",
+		"Shader_attach_failed"		: "Attaching shaders to GPU program has failed",
+		"Shader_compile_failed"		: "Shader compilation from source code has failed to load",
+		"Shader_creation_failed"	: "Shader handle creation has failed",
+		"Shader_link_failed"		: "Shader linking has failed",
+		"Shader_loading_failed"		: "Shader has failed to load",
+		"Texture_not_found"			: "Texture was not found",
+		"Texture_empty"				: "Texture data was not found",
+		"Invalid_num_vid_displays"	: "Invalid number of video displays",
+		"SDL_video_init_failed"		: "SDL Video has failed to initialize",
+		"SDL_vsync_failed"			: "Failed to change vertical synchronization mode",
+		"Window_creation_failed"	: "Unable to create a window"
+	},
+	"Error Sources":
+	{
+		"Source_Unknown"		: "Unknown",
+		"Source_General"		: "General",
+		"Source_Engine"			: "Engine",
+		"Source_Renderer"		: "Renderer",
+		"Source_Scripting"		: "Scripting System",
+ 		"Source_Config"			: "Configuration",
+		"Source_ConfigLoader"	: "Config Loader",
+		"Source_TextureLoader"	: "Texture Loader",
+		"Source_ModelLoader"	: "Model Loader",
+		"Source_ShaderLoader"	: "Shader Loader",
+		"Source_FileLoader"		: "File Loader",
+		"Source_SceneLoader"	: "Scene Loader",
+		"Source_GeometryBuffer"	: "Geometry Buffer",
+		"Source_GraphicsObject"	: "Graphics Object",
+		"Source_ScriptObject"	: "Script Object",
+		"Source_PlayerObject"	: "Player Object",
+		"Source_GameObject"		: "Game Object",
+		"Source_SkyObject"		: "Sky Object",
+		"Source_LightObject"	: "Light Object",
+		"Source_PropertyLoader"	: "Property Loader",
+		"Source_Window"			: "Window System"
+	},
+	"Error Types":
+	{
+		"Info"					: "Info",
+		"Warning"				: "Warning",
+		"Error"					: "Error",
+		"FatalError"			: "Fatal Error"
+	}
+}

BIN
Praxis3D/Debug/Praxis3D.tlog/CL.command.1.tlog


BIN
Praxis3D/Debug/Praxis3D.tlog/CL.read.1.tlog


BIN
Praxis3D/Debug/Praxis3D.tlog/CL.write.1.tlog


+ 2 - 0
Praxis3D/Debug/Praxis3D.tlog/Praxis3D.lastbuildstate

@@ -0,0 +1,2 @@
+#TargetFrameworkVersion=v4.0:PlatformToolSet=v140:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit
+Debug|Win32|C:\Users\Paul\Documents\Visual Studio 2015\Projects\Praxis3D\|

BIN
Praxis3D/Debug/Praxis3D.tlog/link.command.1.tlog


BIN
Praxis3D/Debug/Praxis3D.tlog/link.read.1.tlog


BIN
Praxis3D/Debug/Praxis3D.tlog/link.write.1.tlog


BIN
Praxis3D/Debug/engine.obj.enc


BIN
Praxis3D/Debug/shaderloader.obj.enc


BIN
Praxis3D/Debug/vc140.idb


BIN
Praxis3D/Praxis3D.rc


+ 415 - 0
Praxis3D/Praxis3D.vcxproj

@@ -0,0 +1,415 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug 64bit|Win32">
+      <Configuration>Debug 64bit</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug 64bit|x64">
+      <Configuration>Debug 64bit</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release 64bit|Win32">
+      <Configuration>Release 64bit</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release 64bit|x64">
+      <Configuration>Release 64bit</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{A97875DA-A9E8-4B91-928C-8F0CE88581B9}</ProjectGuid>
+    <RootNamespace>Praxis3D</RootNamespace>
+    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|Win32'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|Win32'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|x64'">
+    <IncludePath>$(SolutionDir)\VC x64\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC x64\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <IncludePath>$(SolutionDir)\VC\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|x64'">
+    <IncludePath>$(SolutionDir)\VC x64\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)\VC x64\lib;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug 64bit|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb_debug.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Windows</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release 64bit|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>opengl32.lib;assimp.lib;SDL2.lib;SDL2main.lib;glew32.lib;FreeImage.lib;tbb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="main.cpp" />
+    <ClCompile Include="Source\ChangeController.cpp" />
+    <ClCompile Include="Source\ClockLocator.cpp" />
+    <ClCompile Include="Source\Config.cpp" />
+    <ClCompile Include="Source\ConfigLoader.cpp" />
+    <ClCompile Include="Source\DeferredRenderer.cpp" />
+    <ClCompile Include="Source\Engine.cpp" />
+    <ClCompile Include="Source\ErrorCodes.cpp" />
+    <ClCompile Include="Source\ErrorHandler.cpp" />
+    <ClCompile Include="Source\ErrorHandlerLocator.cpp" />
+    <ClCompile Include="Source\GeometryBuffer.cpp" />
+    <ClCompile Include="Source\Input.cpp" />
+    <ClCompile Include="Source\KeyCommand.cpp" />
+    <ClCompile Include="Source\Loaders.cpp" />
+    <ClCompile Include="Source\Math.cpp" />
+    <ClCompile Include="Source\ModelLoader.cpp" />
+    <ClCompile Include="Source\NullSystemObjects.cpp" />
+    <ClCompile Include="Source\ObserverBase.cpp" />
+    <ClCompile Include="Source\PlayState.cpp" />
+    <ClCompile Include="Source\PropertyLoader.cpp" />
+    <ClCompile Include="Source\PropertySet.cpp" />
+    <ClCompile Include="Source\Renderer.cpp" />
+    <ClCompile Include="Source\RendererScene.cpp" />
+    <ClCompile Include="Source\RendererSystem.cpp" />
+    <ClCompile Include="Source\RenderTask.cpp" />
+    <ClCompile Include="Source\Scancodes.cpp" />
+    <ClCompile Include="Source\SceneLoader.cpp" />
+    <ClCompile Include="Source\ScriptingScene.cpp" />
+    <ClCompile Include="Source\ScriptingSystem.cpp" />
+    <ClCompile Include="Source\ScriptingTask.cpp" />
+    <ClCompile Include="Source\ServiceBase.cpp" />
+    <ClCompile Include="Source\ShaderLoader.cpp" />
+    <ClCompile Include="Source\ShaderUniformUpdater.cpp" />
+    <ClCompile Include="Source\SpinWait.cpp" />
+    <ClCompile Include="Source\TaskManager.cpp" />
+    <ClCompile Include="Source\TaskManagerLocator.cpp" />
+    <ClCompile Include="Source\TaskScheduler.cpp" />
+    <ClCompile Include="Source\TextureLoader.cpp" />
+    <ClCompile Include="Source\Universal.cpp" />
+    <ClCompile Include="Source\Window.cpp" />
+    <ClCompile Include="Source\WindowLocator.cpp" />
+    <ClCompile Include="Source\WorldEditState.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="resource.h" />
+    <ClInclude Include="Source\BaseGraphicsObjects.h" />
+    <ClInclude Include="Source\BaseScriptObject.h" />
+    <ClInclude Include="Source\CameraGraphicsObject.h" />
+    <ClInclude Include="Source\CameraScript.h" />
+    <ClInclude Include="Source\ChangeController.h" />
+    <ClInclude Include="Source\Clock.h" />
+    <ClInclude Include="Source\ClockLocator.h" />
+    <ClInclude Include="Source\Config.h" />
+    <ClInclude Include="Source\ConfigLoader.h" />
+    <ClInclude Include="Source\DebugMoveScript.h" />
+    <ClInclude Include="Source\DebugUIScript.h" />
+    <ClInclude Include="Source\DeferredRenderer.h" />
+    <ClInclude Include="Source\Engine.h" />
+    <ClInclude Include="Source\EngineDefinitions.h" />
+    <ClInclude Include="Source\EngineState.h" />
+    <ClInclude Include="Source\EnumFactory.h" />
+    <ClInclude Include="Source\ErrorCodes.h" />
+    <ClInclude Include="Source\ErrorHandler.h" />
+    <ClInclude Include="Source\ErrorHandlerLocator.h" />
+    <ClInclude Include="Source\Framebuffer.h" />
+    <ClInclude Include="Source\GeometryBuffer.h" />
+    <ClInclude Include="Source\GraphicsDataSets.h" />
+    <ClInclude Include="Source\Input.h" />
+    <ClInclude Include="Source\KeyCommand.h" />
+    <ClInclude Include="Source\LightingGraphicsObjects.h" />
+    <ClInclude Include="Source\LoaderBase.h" />
+    <ClInclude Include="Source\Loaders.h" />
+    <ClInclude Include="Source\Math.h" />
+    <ClInclude Include="Source\ModelLoader.h" />
+    <ClInclude Include="Source\ModelGraphicsObjects.h" />
+    <ClInclude Include="Source\NullSystemObjects.h" />
+    <ClInclude Include="Source\ObjectPool.h" />
+    <ClInclude Include="Source\ObserverBase.h" />
+    <ClInclude Include="Source\PlayState.h" />
+    <ClInclude Include="Source\PropertyLoader.h" />
+    <ClInclude Include="Source\PropertySet.h" />
+    <ClInclude Include="Source\Renderer.h" />
+    <ClInclude Include="Source\RendererScene.h" />
+    <ClInclude Include="Source\RendererSystem.h" />
+    <ClInclude Include="Source\RenderTask.h" />
+    <ClInclude Include="Source\RenderTaskBase.h" />
+    <ClInclude Include="Source\Scancodes.h" />
+    <ClInclude Include="Source\SceneLoader.h" />
+    <ClInclude Include="Source\ScriptingScene.h" />
+    <ClInclude Include="Source\ScriptingSystem.h" />
+    <ClInclude Include="Source\ScriptingTask.h" />
+    <ClInclude Include="Source\ServiceBase.h" />
+    <ClInclude Include="Source\ShaderGraphicsObjects.h" />
+    <ClInclude Include="Source\ShaderLoader.h" />
+    <ClInclude Include="Source\ShaderUniforms.h" />
+    <ClInclude Include="Source\ShaderUniformUpdater.h" />
+    <ClInclude Include="Source\SolarTimeScript.h" />
+    <ClInclude Include="Source\SpinWait.h" />
+    <ClInclude Include="Source\System.h" />
+    <ClInclude Include="Source\TaskManager.h" />
+    <ClInclude Include="Source\TaskManagerLocator.h" />
+    <ClInclude Include="Source\TaskScheduler.h" />
+    <ClInclude Include="Source\TextureLoader.h" />
+    <ClInclude Include="Source\Universal.h" />
+    <ClInclude Include="Source\Utilities.h" />
+    <ClInclude Include="Source\Window.h" />
+    <ClInclude Include="Source\WindowLocator.h" />
+    <ClInclude Include="Source\WorldEditObject.h" />
+    <ClInclude Include="Source\WorldEditState.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Data\config.ini" />
+    <None Include="Data\error-strings-eng.data" />
+    <None Include="Data\Shaders\geomBillboard.pfrag" />
+    <None Include="Data\Shaders\geomBillboard.pgeom" />
+    <None Include="Data\Shaders\geomBillboard.pvert" />
+    <None Include="Data\Shaders\geometryPass.pfrag" />
+    <None Include="Data\Shaders\geometryPass.pvert" />
+    <None Include="Data\Shaders\lightPass.pfrag" />
+    <None Include="Data\Shaders\lightPass.pvert" />
+    <None Include="Data\Shaders\test.pfrag" />
+    <None Include="Data\Shaders\test.pvert" />
+    <None Include="Data\Shaders\test2.pfrag" />
+    <None Include="Data\Shaders\test2.pvert" />
+    <None Include="Data\Shaders\test3.pfrag" />
+    <None Include="Data\Shaders\test3.pvert" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Praxis3D.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="..\praxis icon.ico" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 488 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -0,0 +1,488 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="Error Handler">
+      <UniqueIdentifier>{527078f0-5ddf-457f-905e-60c0a061b178}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Loaders">
+      <UniqueIdentifier>{1793a391-f01d-489e-ab8b-623f7c11dddb}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer">
+      <UniqueIdentifier>{762a3635-e177-4908-92fc-b59df1af9011}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting">
+      <UniqueIdentifier>{97d3a3e7-453c-44bf-96b4-1a5e3262ebd8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Service Locators">
+      <UniqueIdentifier>{159a020c-bb51-4396-9c31-ea49e1945fb7}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Shaders">
+      <UniqueIdentifier>{9a5d20b8-3826-46ae-80c7-b4f0d92833e2}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Task Systems">
+      <UniqueIdentifier>{7884a775-a801-4368-a5d1-bc971d20cf84}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Error Handler\Header Files">
+      <UniqueIdentifier>{c0cc073d-92a1-41e3-a19d-778b74c18b23}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Loaders\Header Files">
+      <UniqueIdentifier>{6c84e2b4-5138-47eb-a22b-d57c2bfd80bf}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Task Systems\Header Files">
+      <UniqueIdentifier>{23e5cba8-50f2-4da2-b633-d0c2daa80d1b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Shaders\Header Files">
+      <UniqueIdentifier>{b65b6534-32ee-424a-8221-626611204e2a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Service Locators\Header Files">
+      <UniqueIdentifier>{72ba355d-6075-41b9-9410-3082f9dc3a07}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting\Header Files">
+      <UniqueIdentifier>{a8026cf1-b125-4d8c-a821-461e3847ae72}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer\Header Files">
+      <UniqueIdentifier>{fdd2e934-f479-42e8-b9aa-6ea62965fb30}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Error Handler\Source Files">
+      <UniqueIdentifier>{4ed0debc-38a6-4bb1-90c8-dd0ac7d45a45}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Loaders\Source Files">
+      <UniqueIdentifier>{02fb5669-6977-4d02-a630-0a9832c16488}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Task Systems\Source Files">
+      <UniqueIdentifier>{0d4807fe-34bd-4d3f-874a-e1f5aff44855}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Shaders\Source Files">
+      <UniqueIdentifier>{4e0fc49e-14f8-4628-a6af-b89316867b8c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Service Locators\Source Files">
+      <UniqueIdentifier>{23f31a49-9c98-421b-a92f-02c761c14bbd}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting\Source Files">
+      <UniqueIdentifier>{99cc98b8-4791-476a-b5d9-b9145cfe5dfe}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer\Source Files">
+      <UniqueIdentifier>{6cdba2cf-f62f-4908-b999-e66e29fcfd7b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting\Objects">
+      <UniqueIdentifier>{27407ea5-ae43-4495-8fb6-7632dbe46b52}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer\Objects">
+      <UniqueIdentifier>{95899e83-b73c-4db4-9e86-a89e400af84b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting\Objects\Header Files">
+      <UniqueIdentifier>{099a1c14-0bf1-4151-8504-d93efa2ef3d9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Scripting\Objects\Source Files">
+      <UniqueIdentifier>{fd6863a9-2ba9-4867-95a0-07ec73e64e1d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer\Objects\Header Files">
+      <UniqueIdentifier>{f910eb77-1789-4857-8cab-1c8aa943f607}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Renderer\Objects\Source Files">
+      <UniqueIdentifier>{d068d5ec-015b-49b9-9ab9-d916853c91ba}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Shaders\Shader Files">
+      <UniqueIdentifier>{8a7430b9-2eb2-4ed9-95e7-4dc4562e0cf8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Engine States">
+      <UniqueIdentifier>{2ede773b-3125-41f3-9d9b-995f909d6c4c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Engine States\Header Files">
+      <UniqueIdentifier>{b0f033ce-97f6-4177-9397-f71c16a814bf}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Engine States\Source Files">
+      <UniqueIdentifier>{819ed32a-501c-459f-a502-656acde391a4}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="main.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ErrorCodes.cpp">
+      <Filter>Error Handler\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ErrorHandler.cpp">
+      <Filter>Error Handler\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ConfigLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ModelLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\PropertyLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\SceneLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ShaderLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\TextureLoader.cpp">
+      <Filter>Loaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\SpinWait.cpp">
+      <Filter>Task Systems\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\TaskManager.cpp">
+      <Filter>Task Systems\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\TaskScheduler.cpp">
+      <Filter>Task Systems\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ClockLocator.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ErrorHandlerLocator.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Loaders.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\TaskManagerLocator.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\WindowLocator.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ScriptingScene.cpp">
+      <Filter>Scripting\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ScriptingSystem.cpp">
+      <Filter>Scripting\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ScriptingTask.cpp">
+      <Filter>Scripting\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\DeferredRenderer.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\GeometryBuffer.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Renderer.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\RendererScene.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\RendererSystem.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\RenderTask.cpp">
+      <Filter>Renderer\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ShaderUniformUpdater.cpp">
+      <Filter>Shaders\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ChangeController.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Config.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Engine.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Input.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\KeyCommand.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Math.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\NullSystemObjects.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ObserverBase.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\PropertySet.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Scancodes.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ServiceBase.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Universal.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Window.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\PlayState.cpp">
+      <Filter>Engine States\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\WorldEditState.cpp">
+      <Filter>Engine States\Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Source\ErrorCodes.h">
+      <Filter>Error Handler\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ErrorHandler.h">
+      <Filter>Error Handler\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ConfigLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\LoaderBase.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ModelLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\PropertyLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\SceneLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ShaderLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\TextureLoader.h">
+      <Filter>Loaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\SpinWait.h">
+      <Filter>Task Systems\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\TaskManager.h">
+      <Filter>Task Systems\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\TaskScheduler.h">
+      <Filter>Task Systems\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ClockLocator.h">
+      <Filter>Service Locators\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ErrorHandlerLocator.h">
+      <Filter>Service Locators\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Loaders.h">
+      <Filter>Service Locators\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\TaskManagerLocator.h">
+      <Filter>Service Locators\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\WindowLocator.h">
+      <Filter>Service Locators\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ScriptingScene.h">
+      <Filter>Scripting\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ScriptingSystem.h">
+      <Filter>Scripting\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ScriptingTask.h">
+      <Filter>Scripting\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\BaseScriptObject.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\CameraScript.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\DebugUIScript.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\DeferredRenderer.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Framebuffer.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\GeometryBuffer.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Renderer.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\RendererScene.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\RendererSystem.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\RenderTask.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\RenderTaskBase.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\BaseGraphicsObjects.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ShaderUniforms.h">
+      <Filter>Shaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ShaderUniformUpdater.h">
+      <Filter>Shaders\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ChangeController.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Clock.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Engine.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\EngineDefinitions.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\EnumFactory.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Input.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\KeyCommand.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Math.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\NullSystemObjects.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ObjectPool.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ObserverBase.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\PropertySet.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Scancodes.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ServiceBase.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\System.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Universal.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Utilities.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Window.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ModelGraphicsObjects.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\CameraGraphicsObject.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ShaderGraphicsObjects.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\LightingGraphicsObjects.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\GraphicsDataSets.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\DebugMoveScript.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\EngineState.h">
+      <Filter>Engine States\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\PlayState.h">
+      <Filter>Engine States\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\WorldEditState.h">
+      <Filter>Engine States\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\WorldEditObject.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\SolarTimeScript.h">
+      <Filter>Scripting\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Data\Shaders\geomBillboard.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\geomBillboard.pgeom">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\geomBillboard.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\geometryPass.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\geometryPass.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\lightPass.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\lightPass.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test2.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test2.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test3.pfrag">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\Shaders\test3.pvert">
+      <Filter>Shaders\Shader Files</Filter>
+    </None>
+    <None Include="Data\config.ini" />
+    <None Include="Data\error-strings-eng.data" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Praxis3D.rc">
+      <Filter>Resource Files</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="..\praxis icon.ico">
+      <Filter>Resource Files</Filter>
+    </Image>
+  </ItemGroup>
+</Project>

BIN
Praxis3D/Release/Praxis3D.tlog/CL.command.1.tlog


BIN
Praxis3D/Release/Praxis3D.tlog/CL.read.1.tlog


BIN
Praxis3D/Release/Praxis3D.tlog/CL.write.1.tlog


+ 2 - 0
Praxis3D/Release/Praxis3D.tlog/Praxis3D.lastbuildstate

@@ -0,0 +1,2 @@
+#TargetFrameworkVersion=v4.0:PlatformToolSet=v140:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit
+Release|Win32|C:\Users\Paul\Documents\Visual Studio 2015\Projects\Praxis3D\|

BIN
Praxis3D/Release/Praxis3D.tlog/link.command.1.tlog


BIN
Praxis3D/Release/Praxis3D.tlog/link.read.1.tlog


BIN
Praxis3D/Release/Praxis3D.tlog/link.write.1.tlog


+ 127 - 0
Praxis3D/Source/BaseGraphicsObjects.h

@@ -0,0 +1,127 @@
+#pragma once
+
+#include <atomic>
+
+#include "GraphicsDataSets.h"
+#include "Loaders.h"
+#include "Math.h"
+#include "NullSystemObjects.h"
+#include "System.h"
+
+class BaseGraphicsObject : public SystemObject
+{
+public:
+	BaseGraphicsObject(SystemScene *p_systemScene, const std::string &p_name, Properties::PropertyID p_objectType)
+		: SystemObject(p_systemScene, p_name, p_objectType), m_needsUpdate(true), m_affectedByLighting(true) { }
+	virtual ~BaseGraphicsObject() { }
+		
+	BitMask getSystemType() { return Systems::Graphics; }
+
+	virtual BitMask getDesiredSystemChanges()	{ return Systems::Changes::Spacial::All;	}
+	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::None;			}
+
+	// Processes any spacial changes
+	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+	{
+		if(p_changeType & Systems::Changes::Spacial::Position)
+		{
+			m_baseObjectData.m_position = 
+				p_subject->getVec3(this, Systems::Changes::Spacial::Position) + m_baseObjectData.m_offsetPosition;
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spacial::Rotation)
+		{
+			m_baseObjectData.m_rotation = 
+				p_subject->getVec3(this, Systems::Changes::Spacial::Rotation) + m_baseObjectData.m_offsetRotation;
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spacial::Scale)
+		{
+			m_baseObjectData.m_scale = p_subject->getVec3(this, Systems::Changes::Spacial::Scale);
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spacial::ModelMatrix)
+		{
+			m_baseObjectData.m_modelMat = p_subject->getMat4(this, Systems::Changes::Spacial::ModelMatrix);
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::Lighting)
+		{
+			m_affectedByLighting = p_subject->getBool(this, Systems::Changes::Graphics::Lighting);
+		}
+	}
+	
+	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spacial::Position:
+			return m_baseObjectData.m_position;
+			break;
+		case Systems::Changes::Spacial::Rotation:
+			return m_baseObjectData.m_rotation;
+			break;
+		case Systems::Changes::Spacial::Scale:
+			return m_baseObjectData.m_scale;
+			break;
+		}
+
+		return ObservedSubject::getVec3(p_observer, p_changedBits);
+	}
+
+	const virtual bool getBool(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Graphics::Lighting:
+			return m_affectedByLighting;
+			break;
+		}
+
+		return ObservedSubject::getBool(p_observer, p_changedBits);
+	}
+	
+	const inline GraphicsData &getBaseObjectData() const { return m_baseObjectData; }
+
+protected:
+	// A flag telling if this object should be rendered during geometry pass or as a post-process (i.e. after lighting)
+	bool m_affectedByLighting;
+	bool m_needsUpdate;
+
+	GraphicsData m_baseObjectData;
+};
+
+class LoadableGraphicsObject : public BaseGraphicsObject
+{
+public:
+	LoadableGraphicsObject(SystemScene *p_systemScene, const std::string &p_name, Properties::PropertyID p_objectType,
+						   ModelLoader::ModelHandle p_model, ShaderLoader::ShaderProgram *p_shader)
+	: BaseGraphicsObject(p_systemScene, p_name, p_objectType), m_rendererData(p_model, p_shader, m_baseObjectData)
+	{
+		m_active = false;
+		m_loadedToMemory = false;
+	}
+
+	virtual void loadToMemory() { m_loadedToMemory = true; }
+
+	virtual ErrorCode loadToVideoMemory() { return ErrorCode::Success; }
+
+	const inline bool loadedToMemory() const { return m_loadedToMemory; }
+	const inline bool active() const { return m_active; }
+
+	inline void setActive(bool p_flag) { m_active = p_flag; }
+	
+	inline RenderableObjectData &getRenderableObjectData() { return m_rendererData; }
+
+protected:
+	inline void setLoadedToMemory(bool p_flag) { m_loadedToMemory = p_flag; }
+
+	RenderableObjectData m_rendererData;
+
+	std::atomic<bool> m_loadedToMemory;
+	bool m_active;
+};

+ 23 - 0
Praxis3D/Source/BaseScriptObject.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include "System.h"
+
+class BaseScriptObject : public SystemObject
+{
+public:
+	BaseScriptObject(SystemScene *p_systemScene, std::string p_name, Properties::PropertyID p_objectType)
+		: SystemObject(p_systemScene, p_name, p_objectType)
+	{
+
+	}
+	
+	BitMask getSystemType() final { return Systems::Scripting; }
+
+	virtual BitMask getDesiredSystemChanges() { return Systems::Changes::None; }
+
+	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::Spacial::All; }
+
+	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
+
+protected:
+};

+ 62 - 0
Praxis3D/Source/CameraGraphicsObject.h

@@ -0,0 +1,62 @@
+#pragma once
+
+#include <functional>
+
+#include "BaseGraphicsObjects.h"
+#include "Loaders.h"
+
+class RendererScene;
+
+class CameraObject : public BaseGraphicsObject
+{
+	friend class RendererScene;
+	friend class RendererState;
+	//friend class BaseGraphicsObject;
+public:
+	CameraObject(SystemScene *p_systemScene, std::string p_name)
+		: BaseGraphicsObject(p_systemScene, p_name, Properties::Camera)
+	{
+		// Assign camera position
+		m_baseObjectData.m_position = Math::Vec3f(0.0f, 0.0f, 0.0f);
+
+		// Assign the target vector
+		m_baseObjectData.m_rotation = Math::Vec3f(1.0f, 1.0f, 1.0f);
+
+		// Assign the up vector
+		m_baseObjectData.m_scale = Math::Vec3f(0.0f, 1.0f, 0.0f);
+	}
+	virtual ~CameraObject() { }
+
+	virtual ErrorCode init()
+	{
+		return ErrorCode::Success;
+	}
+
+	virtual void loadToMemory() { }
+
+	// Exports all the data of the object as a PropertySet
+	virtual PropertySet exportObject()
+	{
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::Camera);
+		propertySet.addProperty(Properties::Name, m_name);
+
+		return propertySet;
+	}
+
+	inline void update(const float p_deltaTime)
+	{
+		// NOTE: rotation vector is used as a target vector; scale vector is used as up-vector (to save space)
+		//m_baseObjectData.m_modelMat.initCamera(m_baseObjectData.m_position, m_baseObjectData.m_rotation, m_baseObjectData.m_scale);
+		m_needsUpdate = false;
+	}
+
+	// Interested in Model Matrix, that would act as view matrix (so camera could be attached to any object)
+	//virtual BitMask getDesiredSystemChanges() { return Systems::Changes::Spacial::ModelMatrix; }
+	
+protected:
+	Math::Vec2f m_cameraAngle;
+};

+ 226 - 0
Praxis3D/Source/CameraScript.h

@@ -0,0 +1,226 @@
+#pragma once
+
+#include "BaseScriptObject.h"
+#include "Math.h"
+#include "WindowLocator.h"
+
+class Camera : public BaseScriptObject
+{
+	friend class ScriptingScene;
+public:
+	Camera(SystemScene *p_systemScene, std::string p_name, Properties::PropertyID p_objectType)
+		: BaseScriptObject(p_systemScene, p_name, p_objectType)
+	{
+		m_speed = 0.0f;
+		m_fasterSpeed = 0.0f;
+		m_verticalAngle = 0.0f;
+		m_horizontalAngle = 0.0f;
+	}
+
+	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spacial::Position:
+			return m_positionVec;
+			break;
+		case Systems::Changes::Spacial::Rotation:
+			return m_targetVec;
+			break;
+		}
+
+		return ObservedSubject::getVec3(p_observer, p_changedBits);
+	}
+
+	const virtual Math::Mat4f &getMat4(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spacial::ModelMatrix:
+			return m_modelMatrix;
+			break;
+		}
+
+		return ObservedSubject::getMat4(p_observer, p_changedBits);
+	}
+
+	// Setters
+	const inline void setPosition(const Math::Vec3f &p_position)	{ m_positionVec = p_position; }
+	const inline void setFasterSpeed(const float p_speed)			{ m_fasterSpeed = p_speed; }
+	const inline void setSpeed(const float p_speed)					{ m_speed = p_speed; }
+	const inline void setAngles(const Math::Vec2f &p_angles) 
+	{ 
+		m_horizontalAngle = p_angles.x;
+		m_verticalAngle = p_angles.y;
+	}
+
+protected:
+	float	m_speed,
+			m_fasterSpeed,
+			m_verticalAngle,
+			m_horizontalAngle;
+
+	Math::Mat4f m_modelMatrix;
+	Math::Vec3f m_positionVec,
+				m_targetVec,
+				m_upVector,
+				m_horizontalVec;
+};
+
+class FreeCamera : public Camera
+{
+public:
+	FreeCamera(SystemScene *p_systemScene, std::string p_name) : Camera(p_systemScene, p_name, Properties::FreeCamera)
+	{
+
+	}
+
+	virtual ErrorCode init()
+	{
+		return ErrorCode::Success;
+	}
+
+	void loadToMemory()
+	{
+		// Nothing to load
+	}
+
+	// Exports all the data of the object as a PropertySet
+	virtual PropertySet exportObject()
+	{
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::FreeCamera);
+		propertySet.addProperty(Properties::Name, m_name);
+		propertySet.addProperty(Properties::Position, m_positionVec);
+		propertySet.addProperty(Properties::Angle, Math::Vec2f(m_horizontalAngle, m_verticalAngle));
+		propertySet.addProperty(Properties::Speed, m_speed);
+		propertySet.addProperty(Properties::SprintSpeed, m_fasterSpeed);
+
+		// Add root key-binding property set
+		auto &keyBinds = propertySet.addPropertySet(Properties::Keybindings);
+
+		// Add individual key-bindings
+		keyBinds.addProperty(Properties::ForwardKey, (int)m_forwardKey.getFirstBinding());
+		keyBinds.addProperty(Properties::BackwardKey, (int)m_backwardKey.getFirstBinding());
+		keyBinds.addProperty(Properties::LeftStrafeKey, (int)m_strafeLeftKey.getFirstBinding());
+		keyBinds.addProperty(Properties::RightStrafeKey, (int)m_strafeRightKey.getFirstBinding());
+		keyBinds.addProperty(Properties::SprintKey, (int)m_sprintKey.getFirstBinding());
+
+		return propertySet;
+	}
+
+	virtual void update(const float p_deltaTime)
+	{
+		// Only move the camera if the mouse is captured by the window
+		if(Config::windowVar().mouse_captured)
+		{
+			const auto &mouseInfo = WindowLocator::get().getMouseInfo();
+
+			m_horizontalAngle -= Config::inputVar().mouse_jaw * mouseInfo.m_movementX * (Config::inputVar().mouse_sensitivity * 0.01f);
+			m_verticalAngle -= Config::inputVar().mouse_pitch * mouseInfo.m_movementY * (Config::inputVar().mouse_sensitivity * 0.01f);
+
+			m_verticalAngle = Math::clip(m_verticalAngle, -Config::inputVar().mouse_pitch_clip, Config::inputVar().mouse_pitch_clip);
+		}
+
+		// Calculate camera's rotation
+		m_targetVec.target(m_verticalAngle, m_horizontalAngle);
+		m_horizontalVec.horizontal(m_horizontalAngle);
+
+		// Set speed for movement
+		float speed = m_speed;
+
+		// If sprint key is pressed, increase the movement speed
+		if(m_sprintKey.isActivated())
+			speed = m_fasterSpeed;
+
+		// Check the status of all the movement keys
+		if(m_forwardKey.isActivated())
+			m_positionVec += m_targetVec * speed * p_deltaTime;
+		if(m_backwardKey.isActivated())
+			m_positionVec -= m_targetVec * speed * p_deltaTime;
+		if(m_strafeLeftKey.isActivated())
+			m_positionVec -= m_horizontalVec * speed * p_deltaTime;
+		if(m_strafeRightKey.isActivated())
+			m_positionVec += m_horizontalVec * speed * p_deltaTime;
+
+		// Calculate camera's position based on the pressed movement keys
+		m_upVector = Math::cross(m_horizontalVec, m_targetVec);
+		m_modelMatrix.initCamera(m_positionVec, m_targetVec + m_positionVec, m_upVector);
+
+		// Set the target vector variable, so it can be retrieved later by listeners
+		m_targetVec = Math::Vec3f(0.0f);
+		m_targetVec.y = m_verticalAngle;
+		m_targetVec.z = m_horizontalAngle;
+		
+		// Notify listeners
+		postChanges(Systems::Changes::Spacial::Position | 
+					Systems::Changes::Spacial::Rotation | 
+					Systems::Changes::Spacial::ModelMatrix);
+	}
+	
+	// Setters for the movement keys:
+	inline void setForwardKey(Scancode p_key)
+	{
+		m_forwardKey.unbindAll();
+		m_forwardKey.bind(p_key);
+	}
+	inline void setForwardKey(std::string &p_string)
+	{
+		m_forwardKey.unbindAll();
+		m_forwardKey.bind(p_string);
+	}
+
+	inline void setBackwardKey(Scancode p_key)
+	{
+		m_backwardKey.unbindAll();
+		m_backwardKey.bind(p_key);
+	}
+	inline void setBackwardKey(std::string &p_string)
+	{
+		m_backwardKey.unbindAll();
+		m_backwardKey.bind(p_string);
+	}
+
+	inline void setStrafeLeftKey(Scancode p_key)
+	{
+		m_strafeLeftKey.unbindAll();
+		m_strafeLeftKey.bind(p_key);
+	}
+	inline void setStrafeLeftKey(std::string &p_string)
+	{
+		m_strafeLeftKey.unbindAll();
+		m_strafeLeftKey.bind(p_string);
+	}
+
+	inline void setStrafeRightKey(Scancode p_key)
+	{
+		m_strafeRightKey.unbindAll();
+		m_strafeRightKey.bind(p_key);
+	}
+	inline void setStrafeRightKey(std::string &p_string)
+	{
+		m_strafeRightKey.unbindAll();
+		m_strafeRightKey.bind(p_string);
+	}
+
+	inline void setSprintKey(Scancode p_key)
+	{
+		m_sprintKey.unbindAll();
+		m_sprintKey.bind(p_key);
+	}
+	inline void setSprintKey(std::string &p_string)
+	{
+		m_sprintKey.unbindAll();
+		m_sprintKey.bind(p_string);
+	}
+
+private:
+	KeyCommand	m_forwardKey,
+				m_backwardKey,
+				m_strafeLeftKey,
+				m_strafeRightKey,
+				m_sprintKey;
+};

+ 508 - 0
Praxis3D/Source/ChangeController.cpp

@@ -0,0 +1,508 @@
+
+#include "ChangeController.h"
+#include "ErrorHandlerLocator.h"
+#include "TaskManager.h"
+
+ChangeController::ChangeController() : m_lastID(0), m_tlsNotifyList(TLS_OUT_OF_INDEXES), m_taskManager(nullptr)
+{
+	// Reserve space to avoid multiple reallocations
+	m_cumulativeNotifyList.reserve((size_t)Config::engineVar().change_ctrl_cml_notify_list_reserv);
+	m_subjectsList.reserve((size_t)Config::engineVar().change_ctrl_subject_list_reserv);
+	m_subjectsList.resize(1);
+
+	// Setup per thread local storage of notify and one-off-notify lists
+	m_tlsNotifyList = ::TlsAlloc();
+	m_tlsOneTimeNotifyList = ::TlsAlloc();
+
+	//_ASSERT(m_tlsNotifyList != TLS_OUT_OF_INDEXES && "ChangeController: Not enough space in TLS");
+	//_ASSERT(m_tlsOneOffNotifyList != TLS_OUT_OF_INDEXES && "ChangeController: Not enough space in TLS");
+
+	// Reserve space and prepare notify lists for the main (this) thread
+	std::vector<Notification> *list = new std::vector<Notification>;
+	list->reserve((size_t)Config::engineVar().change_ctrl_notify_list_reserv);
+	::TlsSetValue(m_tlsNotifyList, list);
+	m_notifyLists.push_back(list);
+
+	// Reserve space and prepare one-off notify lists for the main (this) thread
+	std::vector<OneTimeNotification> *oneOffList = new std::vector<OneTimeNotification>;
+	oneOffList->reserve((size_t)Config::engineVar().change_ctrl_oneoff_notify_list_reserv);
+	::TlsSetValue(m_tlsOneTimeNotifyList, oneOffList);
+	m_oneTimeNotifyLists.push_back(oneOffList);
+}
+ChangeController::~ChangeController()
+{
+	// Clean up all the subjects and their observers
+	// Iterate over all subjects
+	for(decltype(m_subjectsList.size()) subjectIndex = 0; subjectIndex < m_subjectsList.size(); subjectIndex++)
+	{
+		// If subject is valid
+		if(m_subjectsList[subjectIndex].m_subject)
+		{
+			// Iterate over all subject's observers
+			for(decltype(m_subjectsList[subjectIndex].m_observersList.size()) observerIndex = 0; 
+			observerIndex < m_subjectsList[subjectIndex].m_observersList.size(); observerIndex++)
+			{
+				// Unregister the subject
+				unregisterSubject(m_subjectsList[subjectIndex].m_subject, m_subjectsList[subjectIndex].m_observersList[observerIndex].m_observer);
+			}
+		}
+	}
+	
+	// Free thread local storage
+	if (m_tlsNotifyList != TLS_OUT_OF_INDEXES)
+		::TlsFree(m_tlsNotifyList);
+
+	if(m_tlsOneTimeNotifyList != TLS_OUT_OF_INDEXES)
+		::TlsFree(m_tlsOneTimeNotifyList);
+
+	// Free main thread (this) storage
+	std::vector<Notification> *list = (std::vector<Notification>*)m_notifyLists.back();
+	if(list != nullptr)
+		delete list;
+
+	std::vector<OneTimeNotification> *oneOffList = (std::vector<OneTimeNotification>*)m_oneTimeNotifyLists.back();
+	if(oneOffList != nullptr)
+		delete oneOffList;
+}
+
+ErrorCode ChangeController::registerSubject(ObservedSubject *p_subject, BitMask p_interestedBits, Observer *p_observer, BitMask p_observerBits)
+{
+	// Current return error is "failed" untill the registering has been completed
+	ErrorCode returnError = ErrorCode::Failure;
+
+	if(p_subject && p_observer)
+	{
+		// Stop the updates and lock, during the subject registration
+		SpinWait::Lock lock(m_spinWaitUpdate);
+
+		unsigned int ID = p_subject->getID(this);
+
+		// If subject has already been registered
+		if(ID != ObservedSubject::g_invalidID)
+		{
+			// Add a new observer
+			SubjectInfo &subjectInfo = m_subjectsList[ID];
+			subjectInfo.m_observersList.push_back(ObserverRequest(p_observer, p_interestedBits, p_observerBits));
+
+			p_interestedBits &= ~subjectInfo.m_interestBits;
+
+			if(p_interestedBits)
+			{
+				subjectInfo.m_interestBits |= p_interestedBits;
+				p_subject->updateInterestBits(this, p_interestedBits);
+			}
+		}
+		// If the subject is new
+		else
+		{
+			if(m_freeIDsList.empty())
+			{
+				ID = ++m_lastID;
+				// TODO ASSERT ERROR
+				_ASSERT(ID == m_subjectsList.size());
+				m_subjectsList.resize(ID + 1);
+			}
+			else
+			{
+				ID = m_freeIDsList.back();
+				m_freeIDsList.pop_back();
+			}
+
+			SubjectInfo &subjectInfo = m_subjectsList[ID];
+			subjectInfo.m_subject = p_subject;
+			subjectInfo.m_observersList.push_back(ObserverRequest(p_observer, p_interestedBits, p_observerBits));
+			subjectInfo.m_interestBits = p_interestedBits;
+
+			p_subject->attach(this, p_interestedBits, ID);
+		}
+
+		returnError = ErrorCode::Success;
+	}
+
+	return returnError;
+}
+ErrorCode ChangeController::unregisterSubject(ObservedSubject *p_subject, Observer *p_observer)
+{
+	// Current return error is "failed" untill the unregistering has been completed
+	ErrorCode returnError = ErrorCode::Failure;
+
+	if(p_subject && p_observer)
+	{
+		// Stop the updates and lock, during the subject registration
+		SpinWait::Lock lock(m_spinWaitUpdate);
+
+		unsigned int ID = p_subject->getID(this);
+
+		if(m_subjectsList.size() <= ID || m_subjectsList[ID].m_subject != p_subject)
+		{
+			return ErrorCode::Failure;
+		}
+
+		// TODO ASSERT ERROR
+		_ASSERT(m_subjectsList[ID].m_subject == p_subject);
+
+		std::vector<ObserverRequest> &observerList = m_subjectsList[ID].m_observersList;
+		std::vector<ObserverRequest>::iterator observerListIterator = std::find(observerList.begin(), observerList.end(), p_observer);
+
+		if(observerListIterator != observerList.end())
+		{
+			observerList.erase(observerListIterator);
+
+			if(observerList.empty())
+			{
+				m_subjectsList[ID].m_subject = nullptr;
+				m_freeIDsList.push_back(ID);
+				p_subject->detach(this);
+			}
+
+			returnError = ErrorCode::Success;
+		}
+	}
+
+	return returnError;
+}
+
+ErrorCode ChangeController::distributeChanges(BitMask p_systemsToNotify, BitMask p_changesToDistribute)
+{
+	// Store the parameters so they can be used by multiple threads
+	m_systemsToNotify = p_systemsToNotify;
+	m_changesToDistribute = p_changesToDistribute;
+
+	// Loop through the notifications.
+	// Some of them might generate more notifications, so it might need to loop through multiple times
+	while(true)
+	{
+		// iterate over every thread-specific one-time notification list
+		for(decltype(m_oneTimeNotifyLists)::iterator listIterator = m_oneTimeNotifyLists.begin(); listIterator != m_oneTimeNotifyLists.end(); listIterator++)
+		{
+			// Loop over ever notification in this list
+			auto &currentList = **listIterator;
+			for(decltype(currentList.size()) i = 0, size = currentList.size(); i < size; i++)
+			{
+				// Notify the observer about the change. We cannot check if the change is desired, since the
+				// observer is not registered with the change controller in one-time changes.
+				currentList[i].m_observer->changeOccurred(currentList[i].m_subject, currentList[i].m_changedBits);
+			}
+
+			// Clear out the list before moving to the next one
+			currentList.clear();
+		}
+
+		// Make sure index list is big enough to contain all subjects
+		m_indexList.resize(m_subjectsList.size());
+
+		// Iterate over the every list (from thread local storage) and generate cumulative notify list
+		for(decltype(m_notifyLists)::iterator listIterator = m_notifyLists.begin(); listIterator != m_notifyLists.end(); listIterator++)
+		{
+			auto *currentList = *listIterator;
+			for(decltype(currentList->size()) i = 0, listSize = currentList->size(); i < listSize; i++)
+			{
+				// Get notification
+				Notification &notification = currentList->at(i);
+
+				// Get subject's ID
+				unsigned int ID = notification.m_subject->getID(this);
+
+				// TODO ASSERT ERROR
+				_ASSERT(ID != ObservedSubject::g_invalidID);
+
+				// If the ID is valid
+				if(ID != ObservedSubject::g_invalidID)
+				{
+					// Get subject's index (flag)
+					unsigned int index = m_indexList[ID];
+
+					// If index flag is set, then subject is already in cumulative notify list
+					if(index)
+					{
+						// A subject only needs to be notified once for all the changes
+						// If it already added to the notify list, combine the changes
+						m_cumulativeNotifyList[index].m_changedBits |= notification.m_changedBits;
+					}
+					else
+					{
+						// Set the subject's index flag
+						m_indexList[ID] = (unsigned int)(m_cumulativeNotifyList.size());
+
+						// Add the notification to the cumulative notify list
+						m_cumulativeNotifyList.push_back(MappedNotification(ID, notification.m_changedBits));
+					}
+				}
+			}
+
+			// Clear out the list before moving to the next one
+			currentList->clear();
+		}
+
+		// Get the number of notifications we need to process
+		std::size_t numberOfChanges = m_cumulativeNotifyList.size();
+
+		// If there are no messages to process, exit the loop
+		if(numberOfChanges == 0)
+			break;
+		
+		// If there are more changes to distribute than grain size, do it in parallel
+		if((unsigned int) (numberOfChanges > Config::engineVar().change_ctrl_grain_size && m_taskManager != nullptr))
+		{
+			// Process the notifications in a parallel loop
+			m_taskManager->parallelFor(nullptr, distributionCallback, this, 0, (unsigned int) numberOfChanges, Config::engineVar().change_ctrl_grain_size);
+		}
+		else
+		{
+			// Not enough notifications to distribute them in parallel, so process them in serial
+			distributeRange(0, (unsigned int) numberOfChanges);
+		}
+
+		if(m_changesToDistribute == Systems::Changes::All)
+		{
+			// Clear out all the lists after the distribution of the notifications
+			m_cumulativeNotifyList.clear();
+			m_indexList.clear();
+		}
+		else
+		{
+			// Some of the notifications may need to be distributed later,
+			// in that case, do not clear the lists and just exit the loop
+			break;
+		}
+	}
+
+	return ErrorCode::Success;
+}
+void ChangeController::changeOccurred(ObservedSubject *p_subject, BitMask p_changedBits)
+{
+	// Current return error is "failed" until the task has been completed
+	//ErrorCode returnError = ErrorCode::Failure;
+
+	// TODO ASSERT ERROR
+	_ASSERT(p_subject);
+
+	// Check is subject is valid
+	if(p_subject)
+	{
+		// If the subject is shutting down
+		if(!p_changedBits)
+		{
+			ErrorCode error = removeSubject(p_subject);
+			if(error != ErrorCode::Success)
+				ErrHandlerLoc::get().log(error, ErrorSource::Source_Engine);
+		}
+		else
+		{
+			// Get thread local notification list
+			auto &notifyList = getNotifyList(m_tlsNotifyList);
+
+			// Don't check for duplicates, for performance reasons
+			notifyList.push_back(Notification(p_subject, p_changedBits));
+		}
+	}
+}
+
+void ChangeController::oneTimeChange(ObservedSubject *p_subject, Observer *p_observer, BitMask p_changedBits)
+{
+	// TODO ASSERT ERROR
+	_ASSERT(p_subject);
+	_ASSERT(p_observer);
+
+	// Check if both subject and observer are valid
+	if(p_subject != nullptr && p_observer != nullptr)
+	{
+		// Get thread local notification list
+		auto &oneTimeNotifyList = getOneTimeNotifyList(m_tlsOneTimeNotifyList);
+
+		// Don't check for duplicates, for performance reasons
+		oneTimeNotifyList.push_back(OneTimeNotification(p_subject, p_observer, p_changedBits));
+	}
+}
+
+ErrorCode ChangeController::setTaskManager(TaskManager *p_taskManager)
+{
+	if(!p_taskManager)
+		return ErrorCode::Undefined;
+
+	if(m_taskManager != nullptr)
+		resetTaskManager();
+
+	// Set up pre-thread tsl notify list
+	if(m_tlsNotifyList != TLS_OUT_OF_INDEXES)
+	{
+		m_taskManager = p_taskManager;
+
+		// Make a callback to InitThreadLocalData from each thread
+		m_taskManager->nonStandardPerThreadCallback(initThreadLocalData, this);
+
+		return ErrorCode::Success;
+	}
+
+	return ErrorCode::Failure;
+}
+
+void ChangeController::resetTaskManager()
+{
+	// Free up data task manager data
+	if(m_taskManager)
+	{
+		// Make a callback to FreeThreadLocalData from each thread
+		m_taskManager->nonStandardPerThreadCallback(freeThreadLocalData, this);
+		m_notifyLists.clear();
+		m_taskManager = nullptr;
+
+		// Restore main thread data
+		std::vector<Notification> *notificationList = new std::vector<Notification>();
+		::TlsSetValue(m_tlsNotifyList, notificationList);
+		m_notifyLists.push_back(notificationList);
+	}
+}
+
+void ChangeController::initThreadLocalData(void* p_controller)
+{
+	// TODO ERROR
+	// ASSERT( arg && "ChangeManager: No manager pointer passed to InitThreadLocalNotifyList" );
+
+	// Cast the passed controller to its type
+	ChangeController *controller = (ChangeController*)p_controller;
+
+	// Check if TLS handle is valid
+	if(::TlsGetValue(controller->m_tlsNotifyList) == NULL)
+	{
+		// Create a new notify array
+		std::vector<Notification> *notifyList = new std::vector<Notification>;
+
+		// Reserve space in new array and set it as a tread local storage for current (this) thread
+		notifyList->reserve((unsigned int)Config::engineVar().change_ctrl_notify_list_reserv);
+		::TlsSetValue(controller->m_tlsNotifyList, notifyList);
+
+		// Lock muted while adding the new array to the list
+		SpinWait::Lock lock(controller->m_spinWaitUpdate);
+		controller->m_notifyLists.push_back(notifyList);
+	}
+
+	// Check if TLS handle is valid
+	if(::TlsGetValue(controller->m_tlsOneTimeNotifyList) == NULL)
+	{
+		// Create a new notify array
+		std::vector<OneTimeNotification> *oneTimeNotifyList = new std::vector<OneTimeNotification>;
+
+		// Reserve space in new array and set it as a tread local storage for current (this) thread
+		oneTimeNotifyList->reserve((unsigned int)Config::engineVar().change_ctrl_oneoff_notify_list_reserv);
+		::TlsSetValue(controller->m_tlsOneTimeNotifyList, oneTimeNotifyList);
+
+		// Lock muted while adding the new array to the list
+		SpinWait::Lock lock(controller->m_spinWaitUpdate);
+		controller->m_oneTimeNotifyLists.push_back(oneTimeNotifyList);
+	}
+}
+void ChangeController::freeThreadLocalData(void* p_controller)
+{
+	// TODO ERROR
+	// ASSERT(arg && "ChangeManager: No manager pointer passed to FreeThreadLocalNotifyList");
+
+	// Cast the passed controller to its type
+	ChangeController *controller = (ChangeController*)p_controller;
+
+	// If the array in current thread local storage exists
+	if(controller->m_tlsNotifyList != TLS_OUT_OF_INDEXES)
+	{
+		// Cast array to its type and delete it
+		delete static_cast<std::vector<Notification>*>(::TlsGetValue(controller->m_tlsNotifyList));
+		// Set the TLS handle to null
+		::TlsSetValue(controller->m_tlsNotifyList, NULL);
+	}
+
+	// If the array in current thread local storage exists
+	if(controller->m_tlsOneTimeNotifyList != TLS_OUT_OF_INDEXES)
+	{
+		// Cast array to its type and delete it
+		delete static_cast<std::vector<OneTimeNotification>*>(::TlsGetValue(controller->m_tlsOneTimeNotifyList));
+		// Set the TLS handle to null
+		::TlsSetValue(controller->m_tlsOneTimeNotifyList, NULL);
+	}
+
+}
+
+void ChangeController::distributionCallback(void *p_controller, unsigned int p_begin, unsigned int p_end)
+{
+	// Process the given range (this will be called from multiple threads)
+	ChangeController *controller = (ChangeController*)p_controller;
+	controller->distributeRange(p_begin, p_end);
+}
+
+void ChangeController::distributeRange(unsigned int p_begin, unsigned int p_end)
+{
+	// Loop through all the notification in the given range
+	for (size_t i = p_begin; i < p_end; i++)
+	{
+		// Get the notification and the subject
+		MappedNotification &notification = m_cumulativeNotifyList[i];
+		SubjectInfo &subject = m_subjectsList[notification.m_subjectID];
+
+		// Distribute any desired changes
+		BitMask activeChanges = notification.m_changedBits & m_changesToDistribute;
+
+		if (activeChanges)
+		{
+			// Clear the bit for the changes we are distributing
+			notification.m_changedBits &= ~activeChanges;
+
+			// Loop through all the observers and let them process the notification
+			std::vector<ObserverRequest> &observerList = subject.m_observersList;
+			for (size_t j = 0; j != observerList.size(); j++)
+			{
+				// Determine if this observer is interested in this notification
+				BitMask changesToSend = observerList[j].m_interestBits & activeChanges;
+				if (changesToSend)
+				{
+					// If this observer is part of the systems to be notified then we can pass it this notification
+					if (observerList[j].m_observerIdBits & m_systemsToNotify)
+					{
+						// Have the observer process this change (notification)
+						observerList[j].m_observer->changeOccurred(subject.m_subject, changesToSend);
+					}
+				}
+			}
+		}
+	}
+}
+
+ErrorCode ChangeController::removeSubject(ObservedSubject *p_subject)
+{
+	// TODO ERRORS
+	ErrorCode returnError = ErrorCode::Failure;
+
+	std::vector<ObserverRequest> observerList;
+	{
+		SpinWait::Lock lock(m_spinWaitUpdate);
+
+		unsigned int ID = p_subject->getID(this);
+		_ASSERT(ID != unsigned int(-1));
+		_ASSERT(m_subjectsList[ID].m_subject == p_subject);
+
+		if (m_subjectsList.size() <= ID || m_subjectsList[ID].m_subject != p_subject)
+		{
+			return ErrorCode::Failure;
+			// TODO ERROR FAILURE
+		}
+		observerList = m_subjectsList[ID].m_observersList;
+		m_subjectsList[ID].m_subject = NULL;
+		m_freeIDsList.push_back(ID);
+		returnError = ErrorCode::Success;
+		// TODO ERROR SUCCESS
+	}
+
+	std::vector<ObserverRequest>::iterator listIterator = observerList.begin();
+	for (; listIterator != observerList.end(); listIterator++)
+	{
+		p_subject->detach(listIterator->m_observer);
+	}
+
+	return returnError;
+}
+
+inline std::vector<ChangeController::Notification> &ChangeController::getNotifyList(unsigned int p_tlsIndex)
+{
+	return *static_cast<std::vector<ChangeController::Notification>*>(::TlsGetValue(p_tlsIndex));
+}
+inline std::vector<ChangeController::OneTimeNotification> &ChangeController::getOneTimeNotifyList(unsigned int p_tlsIndex)
+{
+	return *static_cast<std::vector<ChangeController::OneTimeNotification>*>(::TlsGetValue(p_tlsIndex));
+}

+ 110 - 0
Praxis3D/Source/ChangeController.h

@@ -0,0 +1,110 @@
+
+#include <list>
+//#include <set>
+#include <vector>
+
+#include "ObserverBase.h"
+#include "SpinWait.h"
+
+class TaskManager;
+
+class ChangeController : public Observer
+{
+public:
+	ChangeController();
+	virtual ~ChangeController();
+
+	ErrorCode registerSubject(ObservedSubject *p_subject, BitMask p_interestedBits, Observer *p_observer, BitMask p_observerBits = Systems::Types::All);
+	ErrorCode unregisterSubject(ObservedSubject *p_subject, Observer *p_observer);
+
+	ErrorCode distributeChanges(BitMask p_systemsToNotify = Systems::Types::All, BitMask p_changesToDistribute = Systems::Changes::All);
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType);
+
+	// Sends a one-off notification about a change, without requiring the registration of subject-observer
+	void oneTimeChange(ObservedSubject *p_subject, Observer *p_observer, BitMask p_changedBits);
+
+	ErrorCode setTaskManager(TaskManager *p_taskManager);
+
+	void shutdown() { }
+
+	void resetTaskManager();
+
+private:
+	class ObserverRequest
+	{
+	public:
+		ObserverRequest(Observer *p_observer = nullptr, BitMask p_interestBits = 0, BitMask p_idBits = Systems::Changes::All) :
+			m_observer(p_observer), m_interestBits(p_interestBits), m_observerIdBits(p_idBits) { }
+
+		Observer	*m_observer;
+		BitMask		m_interestBits;
+		BitMask		m_observerIdBits;
+
+		bool operator < (const ObserverRequest &p_observerRequest)	const { return m_observer < p_observerRequest.m_observer; }
+		bool operator > (const ObserverRequest &p_observerRequest)	const { return m_observer > p_observerRequest.m_observer; }
+		bool operator == (Observer *p_observer)						const { return m_observer == p_observer; }
+	};
+
+	struct SubjectInfo
+	{
+		SubjectInfo() : m_subject(nullptr), m_interestBits(0) { }
+
+		ObservedSubject				*m_subject;
+		BitMask						 m_interestBits;
+		std::vector<ObserverRequest> m_observersList;
+	};
+	struct Notification
+	{
+		Notification(ObservedSubject *p_subject, BitMask p_changedBits) : 
+			m_subject(p_subject), m_changedBits(p_changedBits) { }
+
+		ObservedSubject *m_subject;
+		BitMask			m_changedBits;
+	};
+	struct OneTimeNotification
+	{
+		OneTimeNotification(ObservedSubject *p_subject, Observer *p_observer, BitMask p_changedBits) :
+			m_subject(p_subject), m_observer(p_observer), m_changedBits(p_changedBits) { }
+
+		ObservedSubject *m_subject;
+		Observer		*m_observer;
+		BitMask			m_changedBits;
+	};
+	struct MappedNotification
+	{
+		MappedNotification(unsigned int p_ID, BitMask p_changedBits) : m_subjectID(p_ID), m_changedBits(p_changedBits) { }
+
+		unsigned int m_subjectID;
+		BitMask		 m_changedBits;
+	};
+
+	std::vector<unsigned int>						m_indexList;
+	std::vector<unsigned int>						m_freeIDsList;
+	std::vector<SubjectInfo>						m_subjectsList;
+	std::vector<MappedNotification>					m_cumulativeNotifyList;
+	std::list<std::vector<Notification>*>			m_notifyLists;
+	std::list<std::vector<OneTimeNotification>*>	m_oneTimeNotifyLists;
+
+	// Last ID and handles for thread local storage data
+	unsigned int m_lastID;
+	unsigned int m_tlsNotifyList;
+	unsigned int m_tlsOneTimeNotifyList;
+
+	BitMask m_systemsToNotify;
+	BitMask m_changesToDistribute;
+
+	SpinWait	m_spinWaitUpdate;
+	TaskManager *m_taskManager;
+
+	static void initThreadLocalData(void* p_controller);
+	static void freeThreadLocalData(void* p_controller);
+
+	static void distributionCallback(void *p_controller, unsigned int p_begin, unsigned int p_end);
+
+	void distributeRange(unsigned int p_begin, unsigned int p_end);
+
+	ErrorCode removeSubject(ObservedSubject *p_subject);
+
+	std::vector<Notification> &getNotifyList(unsigned int p_tlsIndex);
+	std::vector<OneTimeNotification> &getOneTimeNotifyList(unsigned int p_tlsIndex);
+};

+ 208 - 0
Praxis3D/Source/Clock.h

@@ -0,0 +1,208 @@
+#pragma once
+
+#include <iostream>
+#include <Windows.h>
+
+#include "Config.h"
+#include "ErrorCodes.h"
+
+// Base class from which real and null implementations must inherit
+// Does not contain some core methods, to restrict access to however can only call the base class, and whoever has
+// the real implementation is the "owner" and can access all methods. Designed with inention of using for service locator.
+class ClockBase
+{
+public:
+	// Returns a double of the last frame in milliseconds
+	const virtual double getDeltaMS() const = 0;
+	// Returns a float of the last frame in milliseconds
+	const virtual float getDeltaMSF() const = 0;
+
+	// Returns a double of the last frame in seconds
+	const virtual double getDeltaSeconds() const = 0;
+	// Returns a float of the last frame in seconds
+	const virtual float getDeltaSecondsF() const = 0;
+
+	// Returns a double of the elapsed seconds since first update
+	const virtual double getElapsedSeconds() const { return 0.0; }
+	// Returns a float of the elapsed seconds since first update
+	const virtual float getElapsedSecondsF() const { return 0.0f; }
+
+	// Returns the number of elapsed frames since the first update call
+	const virtual unsigned int getElapsedNumFrames() = 0;
+
+	// Returns current number Frames per Second
+	const virtual float getFPS() = 0;
+
+protected:
+	virtual const double getCurrentTime() = 0;
+	virtual const float getCurrentTimeF() = 0;
+};
+
+// Real implementation. init() and update() methods are not in base class, to restrict access when used in service locator.
+class Clock : public ClockBase
+{
+private:
+	LARGE_INTEGER m_timeCurrent;
+	LARGE_INTEGER m_timeLast;
+
+	unsigned int m_currentNumFrames;
+	unsigned int m_lastNumFrames;
+
+	float	m_currentFPS,
+			m_tickSum,
+			*m_tickList;
+
+	double	m_currentMS,
+			m_frequency,
+			m_elapsedSeconds;
+
+	int	m_tickSamples,
+		m_currentTickIndex;
+
+public:
+	Clock()
+	{
+		m_timeCurrent.u.HighPart = 0;
+		m_timeCurrent.u.LowPart = 0;
+		m_timeLast.u.HighPart = 0;
+		m_timeLast.u.LowPart = 0;
+
+		m_currentNumFrames = 0;
+		m_lastNumFrames = 0;
+		m_currentFPS = 0.0f;
+
+		m_currentMS = 0.0;
+		m_frequency = 0.0;
+
+		m_elapsedSeconds = 0.0;
+
+		m_tickSum = 0.0f;
+		m_tickSamples = 0;
+		m_currentTickIndex = 0;
+
+		m_tickList = nullptr;
+	}
+	~Clock() { }
+
+	ErrorCode init()
+	{
+		LARGE_INTEGER ticksPerSec;
+
+		// Get CPU's ticks per second
+		if(QueryPerformanceFrequency(&ticksPerSec))
+		{
+			// Get current time
+			QueryPerformanceCounter(&m_timeCurrent);
+			m_timeLast = m_timeCurrent;
+
+			// Divide ticks per second by 1000 (to get MS instead of seconds) and cast to double
+			m_frequency = double(ticksPerSec.QuadPart) / 1000.0;
+
+			m_tickSamples = 100;// Config::engineVar().smoothing_tick_samples;
+
+			m_tickList = new float[m_tickSamples]();
+
+			for(int i = 0; i < m_tickSamples; i++)
+				m_tickList[i] = 0.0f;
+
+			return ErrorCode::Success;
+		}
+		else
+		{
+			return ErrorCode::Clock_QueryFrequency;
+		}
+	}
+
+	// Must only be called once per frame
+	inline void update()
+	{
+		// Increment the frame counter
+		m_currentNumFrames++;
+
+		// Get current time
+		QueryPerformanceCounter(&m_timeCurrent);
+
+		// Calculate current delta time milliseconds
+		m_currentMS = double(m_timeCurrent.QuadPart - m_timeLast.QuadPart) / m_frequency;
+
+		// Add current frame delta time to total elapsed time
+		m_elapsedSeconds += (m_currentMS / 1000.0);
+
+		m_timeLast = m_timeCurrent;
+		
+		// Increment tick index and reset it when it goes out of bounds
+		m_currentTickIndex++;
+		if(m_currentTickIndex >= m_tickSamples)
+			m_currentTickIndex = 0;
+
+		// Add the current tick's ms to the list
+		m_tickList[m_currentTickIndex] = (float)m_currentMS / 1000.0f;
+
+		// Add all the ticks
+		m_tickSum = 0.0f;
+		for(int i = 0; i < m_tickSamples; i++)
+			m_tickSum += m_tickList[i];
+
+		// Average out the ticks and calculate FPS
+		m_currentFPS = 1.0f / (m_tickSum / m_tickSamples);
+	}
+
+	// Returns a double of the last frame in milliseconds
+	const double getDeltaMS() const { return m_currentMS; }
+
+	// Returns a float of the last frame in milliseconds
+	const float getDeltaMSF() const { return (float)m_currentMS; }
+
+	// Returns a double of the last frame in seconds
+	const double getDeltaSeconds() const { return m_currentMS / 1000.0; }
+
+	// Returns a float of the last frame in seconds
+	const float getDeltaSecondsF() const { return (float)(m_currentMS / 1000.0); }
+
+	// Returns a double of the elapsed seconds since first update
+	const double getElapsedSeconds() const { return m_elapsedSeconds; }
+
+	// Returns a float of the elapsed seconds since first update
+	const float getElapsedSecondsF() const { return (float)m_elapsedSeconds; }
+
+	// Number of elapsed frames since the first update call
+	// update() needs to be called once per frame to work accurately
+	const unsigned int getElapsedNumFrames() { return m_currentNumFrames; }
+
+	// Returns current number Frames per Second
+	const float getFPS() { return m_currentFPS; }
+
+protected:
+	const double getCurrentTime()
+	{
+		// Get current time
+		QueryPerformanceCounter(&m_timeCurrent);
+
+		return double(m_timeCurrent.QuadPart) / m_frequency;
+	}
+	const float getCurrentTimeF() { return (float)getCurrentTime(); }
+};
+
+// Null version of the clock class, returning "0" from all functions
+class ClockNull : public ClockBase
+{
+public:
+	const double getDeltaMS() const { return 0.0; }
+	const float getDeltaMSF() const { return 0.0f; }
+
+	const double getDeltaSeconds() const { return 0.0; }
+	const float getDeltaSecondsF() const { return 0.0f; }
+
+	// Returns a double of the elapsed seconds since first update
+	const double getElapsedSeconds() const { return 0.0; }
+
+	// Returns a float of the elapsed seconds since first update
+	const float getElapsedSecondsF() const { return 0.0f; }
+
+	const double getCurrentTime() { return 0.0; }
+	const float getCurrentTimeF() { return 0.0f; }
+
+	const unsigned int getElapsedNumFrames() { return 0; }
+
+	const float getFPS() { return 0; }
+};

+ 4 - 0
Praxis3D/Source/ClockLocator.cpp

@@ -0,0 +1,4 @@
+#include "ClockLocator.h"
+
+ClockNull ClockLocator::m_nullClock;
+ClockBase *ClockLocator::m_clock = &m_nullClock;

+ 23 - 0
Praxis3D/Source/ClockLocator.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Clock.h"
+
+// Service locator provides global access to specific instantiated classes (i.e. services),
+// but retains the ability to switch instances, and has fail-proof mechanism that returns 
+// an empty (null) service if it hasn't been initialized. Uses dependency injection pattern.
+class ClockLocator
+{
+public:
+	// Get the services
+	inline static ClockBase &get() { return *m_clock; }
+
+	// Initialize the service locator to use null services
+	inline static ErrorCode init() { m_clock = &m_nullClock; return ErrorCode::Success; }
+
+	// If the passed service is null, assign current service as a null service, otherwise assign the passed service as current
+	inline static void provide(ClockBase *p_clock) { m_clock = (p_clock == nullptr) ? &m_nullClock : p_clock; }
+
+private:
+	static ClockBase *m_clock;
+	static ClockNull m_nullClock;
+};

+ 398 - 0
Praxis3D/Source/Config.cpp

@@ -0,0 +1,398 @@
+#include <fstream>
+
+#include "Config.h"
+#include "ErrorHandlerLocator.h"
+#include "Loaders.h"
+
+namespace Systems
+{
+	DEFINE_ENUM(TypeID, TYPEID)
+}
+namespace Properties
+{
+	DEFINE_ENUM(PropertyID, PROPERTYID)
+}
+
+// Predefined variables for "AddVariablePredef" macro
+#define VEC_PREDEF m_variables
+#define HASH_PREDEF m_hashTable
+#define OFFSET_PREDEF m_varVectorOffset
+
+// Macros used to simplify the method of adding configuration variables
+// Also used to add variable name as a string, without the need of hand-writing it,
+// and making sure it spelled the same as the variable name (makes code editing easier)
+#define AddVariable(VECTOR, HASH, KEY, STRUCT, VAR) HASH[#VAR] = KEY; VECTOR.push_back(Variable(#VAR, KEY, &STRUCT.VAR))
+
+// A version of the "AddVariable" macro, with 3 of the 5 parameters predefined
+#define AddVariablePredef(STRUCT, VAR) AddVariable(VEC_PREDEF, HASH_PREDEF, VEC_PREDEF.size() + OFFSET_PREDEF, STRUCT, VAR)
+
+Config::ConfigFileVariables	Config::m_configFileVar;
+Config::EngineVariables		Config::m_engineVar;
+Config::FramebfrVariables	Config::m_framebfrVar;
+Config::GameplayVariables	Config::m_gameplayVar;
+Config::GraphicsVariables	Config::m_graphicsVar;
+Config::InputVariables		Config::m_inputVar;
+Config::ModelVariables		Config::m_modelVar;
+Config::ObjectPoolVariables	Config::m_objPoolVar;
+Config::PathsVariables		Config::m_filepathVar;
+Config::RendererVariables	Config::m_rendererVar;
+Config::ShaderVariables		Config::m_shaderVar;
+Config::TextureVariables	Config::m_textureVar;
+Config::WindowVariables		Config::m_windowVar;
+
+std::vector<Config::Variable> Config::m_variables;
+std::unordered_map<std::string, size_t> Config::m_hashTable;
+
+void Config::init()
+{
+	// Add variables and their names to internal containers, so they could be checked / names matched later.
+	// Note: not all the variables are assigned to containers, as some are not meant to be loaded from config file.
+
+	// Engine variables
+	AddVariablePredef(m_engineVar, change_ctrl_cml_notify_list_reserv);
+	AddVariablePredef(m_engineVar, change_ctrl_grain_size);
+	AddVariablePredef(m_engineVar, change_ctrl_notify_list_reserv);
+	AddVariablePredef(m_engineVar, change_ctrl_oneoff_notify_list_reserv);
+	AddVariablePredef(m_engineVar, change_ctrl_subject_list_reserv);
+	AddVariablePredef(m_engineVar, delta_time_divider);
+	AddVariablePredef(m_engineVar, gl_context_major_version);
+	AddVariablePredef(m_engineVar, gl_context_minor_version);
+	AddVariablePredef(m_engineVar, smoothing_tick_samples);
+
+	// Frame-buffer variables
+	AddVariablePredef(m_framebfrVar, gl_position_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_position_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_position_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_diffuse_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_diffuse_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_diffuse_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_emissive_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_emissive_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_emissive_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_normal_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_normal_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_normal_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_depth_buffer_internal_format);
+	AddVariablePredef(m_framebfrVar, gl_depth_buffer_texture_type);
+	AddVariablePredef(m_framebfrVar, gl_depth_buffer_texture_format);
+	AddVariablePredef(m_framebfrVar, gl_buffers_min_filter);
+	AddVariablePredef(m_framebfrVar, gl_buffers_mag_filter);
+	AddVariablePredef(m_framebfrVar, gl_buffers_wrap_s_method);
+	AddVariablePredef(m_framebfrVar, gl_buffers_wrap_t_method);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_min_filter);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_mag_filter);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_wrap_s_method);
+	AddVariablePredef(m_framebfrVar, gl_blur_buffer_wrap_t_method);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_min_filter);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_mag_filter);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_s_method);
+	AddVariablePredef(m_framebfrVar, gl_final_buffer_t_method);
+
+	// Game-play variables
+	AddVariablePredef(m_gameplayVar, default_map);
+	AddVariablePredef(m_gameplayVar, camera_freelook_speed);
+
+	// Graphics variables
+	AddVariablePredef(m_graphicsVar, double_buffering);
+	AddVariablePredef(m_graphicsVar, multisampling);
+	AddVariablePredef(m_graphicsVar, alpha_size);
+	AddVariablePredef(m_graphicsVar, dir_shadow_res_x);
+	AddVariablePredef(m_graphicsVar, dir_shadow_res_y);
+	AddVariablePredef(m_graphicsVar, max_num_point_lights);
+	AddVariablePredef(m_graphicsVar, max_num_spot_lights);
+	AddVariablePredef(m_graphicsVar, multisample_buffers);
+	AddVariablePredef(m_graphicsVar, multisample_samples);
+	AddVariablePredef(m_graphicsVar, alpha_threshold);
+	AddVariablePredef(m_graphicsVar, emissive_threshold);
+	AddVariablePredef(m_graphicsVar, parallax_height_scale);
+	AddVariablePredef(m_graphicsVar, fog_color_x);
+	AddVariablePredef(m_graphicsVar, fog_color_y);
+	AddVariablePredef(m_graphicsVar, fog_color_z);
+	AddVariablePredef(m_graphicsVar, fog_density);
+	AddVariablePredef(m_graphicsVar, fov);
+	AddVariablePredef(m_graphicsVar, gamma);
+	AddVariablePredef(m_graphicsVar, light_atten_constant);
+	AddVariablePredef(m_graphicsVar, light_atten_linear);
+	AddVariablePredef(m_graphicsVar, light_atten_quadratic);
+	AddVariablePredef(m_graphicsVar, light_color_r);
+	AddVariablePredef(m_graphicsVar, light_color_g);
+	AddVariablePredef(m_graphicsVar, light_color_b);
+	AddVariablePredef(m_graphicsVar, texture_tiling_factor);
+	AddVariablePredef(m_graphicsVar, z_far);
+	AddVariablePredef(m_graphicsVar, z_near);
+	
+	// Input variables
+	AddVariablePredef(m_inputVar, back_key);
+	AddVariablePredef(m_inputVar, backward_editor_key);
+	AddVariablePredef(m_inputVar, backward_key);
+	AddVariablePredef(m_inputVar, center_key);
+	AddVariablePredef(m_inputVar, clip_mouse_key);
+	AddVariablePredef(m_inputVar, close_window_key);
+	AddVariablePredef(m_inputVar, debug_1_key);
+	AddVariablePredef(m_inputVar, debug_2_key);
+	AddVariablePredef(m_inputVar, down_editor_key);
+	AddVariablePredef(m_inputVar, down_key);
+	AddVariablePredef(m_inputVar, escape_key);
+	AddVariablePredef(m_inputVar, forward_editor_key);
+	AddVariablePredef(m_inputVar, forward_key);
+	AddVariablePredef(m_inputVar, fullscreen_key);
+	AddVariablePredef(m_inputVar, jump_key);
+	AddVariablePredef(m_inputVar, left_editor_key);
+	AddVariablePredef(m_inputVar, left_strafe_key);
+	AddVariablePredef(m_inputVar, next_editor_key);
+	AddVariablePredef(m_inputVar, modifier_editor_key);
+	AddVariablePredef(m_inputVar, next_editor_key);
+	AddVariablePredef(m_inputVar, num_preallocated_keybinds);
+	AddVariablePredef(m_inputVar, previous_editor_key);
+	AddVariablePredef(m_inputVar, right_editor_key);
+	AddVariablePredef(m_inputVar, right_strafe_key);
+	AddVariablePredef(m_inputVar, save_editor_key);
+	AddVariablePredef(m_inputVar, sprint_key);
+	AddVariablePredef(m_inputVar, up_editor_key);
+	AddVariablePredef(m_inputVar, up_key);
+	AddVariablePredef(m_inputVar, vsync_key);
+	AddVariablePredef(m_inputVar, mouse_filter);
+	AddVariablePredef(m_inputVar, mouse_warp_mode);
+	AddVariablePredef(m_inputVar, mouse_jaw);
+	AddVariablePredef(m_inputVar, mouse_pitch);
+	AddVariablePredef(m_inputVar, mouse_pitch_clip);
+	AddVariablePredef(m_inputVar, mouse_sensitivity);
+
+	// Model variables
+	AddVariablePredef(m_modelVar, calcTangentSpace);
+	AddVariablePredef(m_modelVar, joinIdenticalVertices);
+	AddVariablePredef(m_modelVar, makeLeftHanded);
+	AddVariablePredef(m_modelVar, triangulate);
+	AddVariablePredef(m_modelVar, removeComponent);
+	AddVariablePredef(m_modelVar, genNormals);
+	AddVariablePredef(m_modelVar, genSmoothNormals);
+	AddVariablePredef(m_modelVar, genUVCoords);
+	AddVariablePredef(m_modelVar, optimizeMeshes);
+	AddVariablePredef(m_modelVar, optimizeGraph);
+
+	// Object pool variables
+	AddVariablePredef(m_objPoolVar, model_object_pool_size);
+	AddVariablePredef(m_objPoolVar, point_light_pool_size);
+	AddVariablePredef(m_objPoolVar, shader_object_pool_size);
+	AddVariablePredef(m_objPoolVar, spot_light_pool_size);
+
+	// File-path variables
+	AddVariablePredef(m_filepathVar, config_path);
+	AddVariablePredef(m_filepathVar, map_path);
+	AddVariablePredef(m_filepathVar, model_path);
+	AddVariablePredef(m_filepathVar, object_path);
+	AddVariablePredef(m_filepathVar, shader_path);
+	AddVariablePredef(m_filepathVar, sound_path);
+	AddVariablePredef(m_filepathVar, texture_path);
+	
+	// Renderer variables
+	AddVariablePredef(m_rendererVar, dir_light_vert_shader);
+	AddVariablePredef(m_rendererVar, dir_light_frag_shader);
+	AddVariablePredef(m_rendererVar, point_light_vert_shader);
+	AddVariablePredef(m_rendererVar, point_light_frag_shader);
+	AddVariablePredef(m_rendererVar, spot_light_vert_shader);
+	AddVariablePredef(m_rendererVar, spot_light_frag_shader);
+	AddVariablePredef(m_rendererVar, dir_light_quad);
+	AddVariablePredef(m_rendererVar, point_light_sphere);
+	AddVariablePredef(m_rendererVar, spot_light_cone);
+	AddVariablePredef(m_rendererVar, stencil_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, stencil_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, geometry_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, geometry_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, geom_billboard_vert_shader);
+	AddVariablePredef(m_rendererVar, geom_billboard_frag_shader);
+	AddVariablePredef(m_rendererVar, geom_billboard_goem_shader);
+	AddVariablePredef(m_rendererVar, gaussian_blur_vertical_frag_shader);
+	AddVariablePredef(m_rendererVar, gaussian_blur_vertical_vert_shader);
+	AddVariablePredef(m_rendererVar, gaussian_blur_horizontal_frag_shader);
+	AddVariablePredef(m_rendererVar, gaussian_blur_horizontal_vert_shader);
+	AddVariablePredef(m_rendererVar, light_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, light_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, dir_light_quad_offset_x);
+	AddVariablePredef(m_rendererVar, dir_light_quad_offset_y);
+	AddVariablePredef(m_rendererVar, dir_light_quad_offset_z);
+	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_x);
+	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_y);
+	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_z);
+	AddVariablePredef(m_rendererVar, depth_test_func);
+	AddVariablePredef(m_rendererVar, face_culling_mode);
+	AddVariablePredef(m_rendererVar, heightmap_combine_channel);
+	AddVariablePredef(m_rendererVar, heightmap_combine_texture);
+	AddVariablePredef(m_rendererVar, max_num_point_lights);
+	AddVariablePredef(m_rendererVar, max_num_spot_lights);
+	AddVariablePredef(m_rendererVar, objects_loaded_per_frame);
+	AddVariablePredef(m_rendererVar, shader_pool_size);
+	AddVariablePredef(m_rendererVar, depth_test);
+	AddVariablePredef(m_rendererVar, face_culling);
+
+	// Shader variables
+	AddVariablePredef(m_shaderVar, modelMatUniform);
+	AddVariablePredef(m_shaderVar, viewMatUniform);
+	AddVariablePredef(m_shaderVar, projectionMatUniform);
+	AddVariablePredef(m_shaderVar, viewProjectionMatUniform);
+	AddVariablePredef(m_shaderVar, modelViewMatUniform);
+	AddVariablePredef(m_shaderVar, modelViewProjectionMatUniform);
+	AddVariablePredef(m_shaderVar, screenSizeUniform);
+	AddVariablePredef(m_shaderVar, elapsedTimeUniform);
+	AddVariablePredef(m_shaderVar, gammaUniform);
+	AddVariablePredef(m_shaderVar, alphaCullingUniform);
+	AddVariablePredef(m_shaderVar, alphaThresholdUniform);
+	AddVariablePredef(m_shaderVar, emissiveThresholdUniform);
+	AddVariablePredef(m_shaderVar, parallaxHeightScale);
+	AddVariablePredef(m_shaderVar, textureTilingFactorUniform);
+	AddVariablePredef(m_shaderVar, dirLightColor);
+	AddVariablePredef(m_shaderVar, dirLightDirection);
+	AddVariablePredef(m_shaderVar, dirLightIntensity);
+	AddVariablePredef(m_shaderVar, numPointLightsUniform);
+	AddVariablePredef(m_shaderVar, numSpotLightsUniform);
+	AddVariablePredef(m_shaderVar, pointLightViewProjectionMatUniform);
+	AddVariablePredef(m_shaderVar, pointLightBuffer);
+	AddVariablePredef(m_shaderVar, spotLightBuffer);
+	AddVariablePredef(m_shaderVar, spotLightViewProjectionMatUniform);
+	AddVariablePredef(m_shaderVar, stencilPassViewProjectionMatUniform);
+	AddVariablePredef(m_shaderVar, dirShadowMapMVPUniform);
+	AddVariablePredef(m_shaderVar, dirShadowMapBiasMVPUniform);
+	AddVariablePredef(m_shaderVar, cameraPosVecUniform);
+	AddVariablePredef(m_shaderVar, cameraUpVecUniform);
+	AddVariablePredef(m_shaderVar, cameraRightVecUniform);
+	AddVariablePredef(m_shaderVar, positionMapUniform);
+	AddVariablePredef(m_shaderVar, diffuseMapUniform);
+	AddVariablePredef(m_shaderVar, normalMapUniform);
+	AddVariablePredef(m_shaderVar, emissiveMapUniform);
+	AddVariablePredef(m_shaderVar, blurMapUniform);
+	AddVariablePredef(m_shaderVar, sunGlowTextureUniform);
+	AddVariablePredef(m_shaderVar, skyMapTextureUniform);
+	AddVariablePredef(m_shaderVar, dirShadowMapTextureUniform);
+	AddVariablePredef(m_shaderVar, diffuseTextureUniform);
+	AddVariablePredef(m_shaderVar, normalTextureUniform);
+	AddVariablePredef(m_shaderVar, specularTextureUniform);
+	AddVariablePredef(m_shaderVar, emissiveTextureUniform);
+	AddVariablePredef(m_shaderVar, glossTextureUniform);
+	AddVariablePredef(m_shaderVar, heightTextureUniform);
+	AddVariablePredef(m_shaderVar, fogDensityUniform);
+	AddVariablePredef(m_shaderVar, fogColorUniform);
+	AddVariablePredef(m_shaderVar, billboardScaleUniform);
+	AddVariablePredef(m_shaderVar, depthTypeUniform);
+	AddVariablePredef(m_shaderVar, testMatUniform);
+	AddVariablePredef(m_shaderVar, testVecUniform);
+	AddVariablePredef(m_shaderVar, testFloatUniform);
+
+	// Texture variables
+	AddVariablePredef(m_textureVar, default_texture);
+	AddVariablePredef(m_textureVar, default_emissive_texture);
+	AddVariablePredef(m_textureVar, default_height_texture);
+	AddVariablePredef(m_textureVar, default_normal_texture);
+	AddVariablePredef(m_textureVar, default_specular_intensity);
+	AddVariablePredef(m_textureVar, default_specular_power);
+	AddVariablePredef(m_textureVar, gl_texture_anisotropy);
+	AddVariablePredef(m_textureVar, gl_texture_magnification);
+	AddVariablePredef(m_textureVar, gl_texture_minification);
+	AddVariablePredef(m_textureVar, number_of_mipmaps);
+	AddVariablePredef(m_textureVar, generate_mipmaps);
+
+	// Window variables
+	AddVariablePredef(m_windowVar, name);
+	AddVariablePredef(m_windowVar, default_display);
+	AddVariablePredef(m_windowVar, window_position_x);
+	AddVariablePredef(m_windowVar, window_position_y);
+	AddVariablePredef(m_windowVar, window_size_fullscreen_x);
+	AddVariablePredef(m_windowVar, window_size_fullscreen_y);
+	AddVariablePredef(m_windowVar, window_size_windowed_x);
+	AddVariablePredef(m_windowVar, window_size_windowed_y);
+	AddVariablePredef(m_windowVar, fullscreen);
+	AddVariablePredef(m_windowVar, fullscreen_borderless);
+	AddVariablePredef(m_windowVar, mouse_captured);
+	AddVariablePredef(m_windowVar, mouse_release_on_lost_focus);
+	AddVariablePredef(m_windowVar, resizable);
+	AddVariablePredef(m_windowVar, vertical_sync);
+}
+ErrorCode Config::loadFromFile(const std::string &p_filename)
+{
+	std::ifstream configFile(p_filename, std::ifstream::in);
+	std::string singleWord;
+	ErrorCode returnCode = ErrorCode::Success;
+
+	if(configFile.fail())
+	{
+		returnCode = ErrorCode::Ifstream_failed;
+	}
+	else
+	{
+		while(!configFile.eof())
+		{
+			// Get next word
+			configFile >> singleWord;
+
+			// Match the word in the hash table
+			auto &tableEntry = m_hashTable.find(singleWord);
+
+			// If match was found
+			if(tableEntry != m_hashTable.end())
+			{
+				// Get map key and next word (that will be converted to value)
+				auto mapKey = tableEntry->second;
+				configFile >> singleWord;
+
+				// Pass the word as a value
+				m_variables[mapKey - m_varVectorOffset].setVariable(singleWord);
+
+				// DEBUGGING: log an info error about modified variable
+				ErrHandlerLoc::get().log(Info, Source_Config, m_variables[mapKey - m_varVectorOffset].toString());
+			}
+		}
+	}
+
+	configFile.close();
+
+	// Check boundaries of some values that are integer representations of enums
+	if(m_rendererVar.heightmap_combine_channel < TextureColorChannelOffset::ColorOffset_Red ||
+	   m_rendererVar.heightmap_combine_channel >= TextureColorChannelOffset::ColorOffset_NumChannels)
+		m_rendererVar.heightmap_combine_channel = TextureColorChannelOffset::ColorOffset_Alpha;
+
+	if(m_rendererVar.heightmap_combine_texture < Model::ModelMaterialType::ModelMat_diffuse ||
+	   m_rendererVar.heightmap_combine_texture >= Model::ModelMaterialType::NumOfModelMaterials)
+		m_rendererVar.heightmap_combine_texture = Model::ModelMaterialType::ModelMat_normal;
+
+	return returnCode;
+}
+ErrorCode Config::saveToFile(const std::string &p_filename)
+{
+	std::string fileContents;
+
+	for(std::vector<Variable>::size_type i = 0, size = m_variables.size(); i < size; i++)
+	{
+		// Only add variables that have been changed (if they haven't no need to add them,
+		// since they are initialized to that value by default
+		if(m_variables[i].valueChanged())
+		{
+			fileContents += m_variables[i].toString() + "\n";
+		}
+	}
+
+	return ErrorCode::Success;
+}
+
+void Config::setVariable(std::string p_name, std::string p_variable)
+{
+	// Match the name in the hash table
+	auto &tableEntry = m_hashTable.find(p_name);
+
+	// If match was found
+	if(tableEntry != m_hashTable.end())
+	{
+		// Get map key
+		auto mapKey = tableEntry->second;
+
+		// Pass the word as a value
+		m_variables[mapKey - m_varVectorOffset].setVariable(p_variable);
+
+		// DEBUGGING: log an info error about modified variable
+		ErrHandlerLoc::get().log(Info, Source_Config, m_variables[mapKey - m_varVectorOffset].toString());
+	}
+}

+ 1120 - 0
Praxis3D/Source/Config.h

@@ -0,0 +1,1120 @@
+#pragma once
+
+#include <GL\glew.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "ErrorCodes.h"
+#include "EnumFactory.h"
+#include "Utilities.h"
+
+typedef unsigned int BitMask;
+
+//#define ever ;;
+
+//#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+//#define GL_LINEAR 0x2601
+//#define GL_LESS 0x0201
+//#define GL_BACK 0x0405
+
+namespace Systems
+{
+	#define TYPEID(Code) \
+	Code(Null, = -1) \
+	Code(Graphics,) \
+	Code(Scripting,) \
+	Code(NumberOfSystems,) 
+	DECLARE_ENUM(TypeID, TYPEID)
+
+	const static std::string SystemNames[NumberOfSystems] =
+	{
+		GetString(Graphics),
+		GetString(Scripting)
+	};
+	
+	namespace Types
+	{
+		static const BitMask All = static_cast<BitMask>(-1);
+		static const BitMask Max = 32;
+	}
+
+	namespace Changes
+	{
+		namespace Generic
+		{
+			static const BitMask CreateObject	= (1 << 0);
+			static const BitMask DeleteObject	= (1 << 1);
+			static const BitMask ExtendObject	= (1 << 2);
+			static const BitMask UnextendObject = (1 << 3);
+			static const BitMask Name			= (1 << 4);
+			static const BitMask All			= CreateObject | DeleteObject | ExtendObject;
+		}
+		namespace Spacial
+		{
+			static const BitMask Position		= (1 << 5);
+			static const BitMask Rotation		= (1 << 6);
+			static const BitMask Scale			= (1 << 7);
+			static const BitMask ModelMatrix	= (1 << 8);
+			static const BitMask All			= Position | Rotation | Scale | ModelMatrix;
+		}
+		namespace Graphics
+		{
+			static const BitMask Target		= (1 << 9);
+			static const BitMask UpVector	= (1 << 10);
+			static const BitMask Lighting	= (1 << 11);
+			static const BitMask All		= Target | UpVector | Lighting;
+		}
+		namespace Physics
+		{
+
+		}
+		namespace Audio
+		{
+
+		}
+		namespace Scripting
+		{
+
+		}
+
+		static const BitMask Link = (1 << 29);
+
+		static const BitMask None = 0;
+		static const BitMask All = static_cast<BitMask>(-1);
+	}
+}
+
+namespace Properties
+{
+	#define PROPERTYID(Code) \
+	Code(Null, = 0) \
+	/* General */ \
+	Code(ArrayEntry,) \
+	Code(Default,) \
+	Code(Filename,) \
+	Code(Index,) \
+	Code(Keybindings,) \
+	Code(LoadInBackground,) \
+	Code(Name,) \
+	Code(Objects,) \
+	Code(Scene,) \
+	Code(Systems,) \
+	Code(Type,) \
+	/* Geometry */ \
+	Code(OffsetPosition,) \
+	Code(OffsetRotation,) \
+	Code(Position,) \
+	Code(Rotation,) \
+	Code(Scale,) \
+	/* Graphics */ \
+	Code(AlphaThreshold, ) \
+	Code(Attenuation,) \
+	Code(Camera,) \
+	Code(Color,) \
+	Code(CutoffAngle,) \
+	Code(Diffuse,) \
+	Code(Direction,) \
+	Code(DirectionalLight,) \
+	Code(Emissive,) \
+	Code(FragmentShader,) \
+	Code(GeometryShader,) \
+	Code(Gloss,) \
+	Code(Graphics,) \
+	Code(Height,) \
+	Code(Intensity,) \
+	Code(Lighting,) \
+	Code(Materials,) \
+	Code(Models,) \
+	Code(ModelObject,) \
+	Code(ModelPoolSize,) \
+	Code(Normal,) \
+	Code(PointLight,) \
+	Code(PointLightPoolSize,) \
+	Code(PostProcess,) \
+	Code(Shaders,) \
+	Code(ShaderPoolSize,) \
+	Code(ShaderGraphicsObject,) \
+	Code(ShaderModelObject,) \
+	Code(Specular,) \
+	Code(SpotLight,) \
+	Code(SpotLightPoolSize,) \
+	Code(TessControlShader,) \
+	Code(TessEvaluationShader,) \
+	Code(TextureTilingFactor,) \
+	Code(VertexShader,) \
+	/* Key binds */ \
+	Code(BackwardKey,) \
+	Code(CenterKey,) \
+	Code(CloseKey,) \
+	Code(DebugCaptureMouseKey,) \
+	Code(DebugFullscreenKey,) \
+	Code(DebugVertSyncKey,) \
+	Code(DownKey,) \
+	Code(ForwardKey,) \
+	Code(LeftKey,) \
+	Code(LeftStrafeKey,) \
+	Code(ModifierKey,) \
+	Code(NextKey,) \
+	Code(PreviousKey,) \
+	Code(RightKey,) \
+	Code(RightStrafeKey,) \
+	Code(SaveKey,) \
+	Code(SprintKey,) \
+	Code(UpKey,) \
+	/* Linking */ \
+	Code(ObjectLinks,) \
+	Code(Observer,) \
+	Code(Subject,) \
+	/* Scripting */ \
+	Code(Angle,) \
+	Code(BaseUIScript,) \
+	Code(DayOfYear,) \
+	Code(DebugMoveScript,) \
+	Code(DebugUIScript,) \
+	Code(FreeCamera,) \
+	Code(Latitude,) \
+	Code(Longitude,) \
+	Code(InputScript,) \
+	Code(Hours,) \
+	Code(KeyCode,) \
+	Code(KeyName,) \
+	Code(Minutes,) \
+	Code(Radius,) \
+	Code(Scripting,) \
+	Code(Seconds,) \
+	Code(SolarTimeScript,) \
+	Code(Speed,) \
+	Code(SprintSpeed,) \
+	Code(TimeMultiplier,) \
+	Code(WorldEditScript,) \
+	/* Window */ \
+	Code(Fullscreen,) \
+	Code(MouseCapture,) \
+	Code(VerticalSync,) \
+	Code(WindowTitle,) \
+	/* End of property IDs */ \
+	Code(NumberOfPropertyIDs,) 
+	DECLARE_ENUM(PropertyID, PROPERTYID)
+
+	const static std::string PropertyNames[PropertyID::NumberOfPropertyIDs] =
+	{
+		GetString(Null),
+		GetString(ArrayEntry),
+		GetString(Default),
+		GetString(Filename),
+		GetString(Index),
+		GetString(Keybindings),
+		GetString(LoadInBackground),
+		GetString(Name),
+		GetString(Objects),
+		GetString(Scene),
+		GetString(Systems),
+		GetString(Type),
+		GetString(OffsetPosition),
+		GetString(OffsetRotation),
+		GetString(Position),
+		GetString(Rotation),
+		GetString(Scale),
+		GetString(AlphaThreshold),
+		GetString(Attenuation),
+		GetString(Camera),
+		GetString(Color),
+		GetString(CutoffAngle),
+		GetString(Diffuse),
+		GetString(Direction),
+		GetString(DirectionalLight),
+		GetString(Emissive),
+		GetString(FragmentShader),
+		GetString(GeometryShader),
+		GetString(Gloss),
+		GetString(Graphics),
+		GetString(Height),
+		GetString(Intensity),
+		GetString(Lighting),
+		GetString(Materials),
+		GetString(Models),
+		GetString(ModelObject),
+		GetString(ModelPoolSize),
+		GetString(Normal),
+		GetString(PointLight),
+		GetString(PointLightPoolSize),
+		GetString(PostProcess),
+		GetString(Shaders),
+		GetString(ShaderPoolSize),
+		GetString(ShaderGraphicsObject),
+		GetString(ShaderModelObject),
+		GetString(Specular),
+		GetString(SpotLight),
+		GetString(SpotLightPoolSize),
+		GetString(TessControlShader),
+		GetString(TessEvaluationShader),
+		GetString(TextureTilingFactor),
+		GetString(VertexShader),
+		GetString(BackwardKey),
+		GetString(CenterKey),
+		GetString(CloseKey),
+		GetString(DebugCaptureMouseKey),
+		GetString(DebugFullscreenKey),
+		GetString(DebugVertSyncKey),
+		GetString(DownKey),
+		GetString(ForwardKey),
+		GetString(LeftKey),
+		GetString(LeftStrafeKey),
+		GetString(ModifierKey),
+		GetString(NextKey),
+		GetString(PreviousKey),
+		GetString(RightKey),
+		GetString(RightStrafeKey),
+		GetString(SaveKey),
+		GetString(SprintKey),
+		GetString(UpKey),
+		GetString(ObjectLinks),
+		GetString(Observer),
+		GetString(Subject),
+		GetString(Angle),
+		GetString(BaseUIScript),
+		GetString(DayOfYear),
+		GetString(DebugMoveScript),
+		GetString(DebugUIScript),
+		GetString(FreeCamera),
+		GetString(Latitude),
+		GetString(Longitude),
+		GetString(InputScript),
+		GetString(Hours),
+		GetString(KeyCode),
+		GetString(KeyName),
+		GetString(Minutes),
+		GetString(Radius),
+		GetString(Scripting),
+		GetString(Seconds),
+		GetString(SolarTimeScript),
+		GetString(Speed),
+		GetString(SprintSpeed),
+		GetString(TimeMultiplier),
+		GetString(WorldEditScript),
+		GetString(Fullscreen),
+		GetString(MouseCapture),
+		GetString(VerticalSync),
+		GetString(WindowTitle)
+	};
+
+
+	// A few overloaded static functions to convert other values to PropertyID enum
+	// Note: converting from string here is very slow, and would be better implemented
+	// by using hash-maps or similar optimized string search algorithm
+
+	static Properties::PropertyID toPropertyID(const int p_value)
+	{
+		// If the passed value is within enum range, static cast it to ID, if not, return null ID
+		if(p_value > Properties::PropertyID::Null && p_value < Properties::PropertyID::NumberOfPropertyIDs)
+			return static_cast<Properties::PropertyID>(p_value);
+		else
+			return Properties::PropertyID::Null;
+	}
+	static Properties::PropertyID toPropertyID(const float p_value)
+	{
+		// If the passed value is within enum range, static cast it to ID, if not, return null ID
+		if(p_value > Properties::PropertyID::Null && p_value < Properties::PropertyID::NumberOfPropertyIDs)
+			return static_cast<Properties::PropertyID>((int)p_value);
+		else
+			return Properties::PropertyID::Null;
+	}
+	static Properties::PropertyID toPropertyID(const std::string &p_value)
+	{
+		// If string is empty return null ID
+		if(p_value.empty() == true)
+			return Properties::PropertyID::Null;
+
+		// If the property ID is encoded in the string (in a format ID(propID), like so: "ID(12)" ),
+		// extract the property ID from it, convert it to an int and return property ID from overloaded function
+		if(p_value[0] == 'I' && p_value[1] == 'D' && p_value[2] == '(' && p_value[p_value.size() - 1] == ')')
+			return toPropertyID(std::stoi(p_value.substr(3, p_value.size() - 1)));
+
+		// Iterate over all PropertyIDs and compare the string to property name
+		for(int i = 0; i < Properties::PropertyID::NumberOfPropertyIDs; i++)
+			if(Properties::PropertyNames[i] == p_value)
+				return static_cast<Properties::PropertyID>(i);
+
+		// If this point is reached, no match was found, return null ID
+		return Properties::PropertyID::Null;
+	}
+}
+
+// Provides global read-only access to various configuration variables. A data-driven way of hard-coded values.
+// To read values from a file, needs to be initialized and loaded before any system accesses the variables.
+class Config
+{
+	// These friend classes are the only objects allowed to modify config variables:
+	friend class DebugUIScript;
+	friend class DeferredRenderer;
+	friend class ErrorHandler;
+	friend class Window;
+public:
+	struct ConfigFileVariables
+	{
+		ConfigFileVariables()
+		{
+			config_file = "Data\\config.ini";
+			error_code_strings_eng = "error-strings-eng.data";
+		}
+
+		std::string config_file;
+		std::string error_code_strings_eng;
+	};
+	struct EngineVariables
+	{
+		EngineVariables()
+		{
+			change_ctrl_cml_notify_list_reserv = 4096;
+			change_ctrl_grain_size = 50;
+			change_ctrl_notify_list_reserv = 8192;
+			change_ctrl_oneoff_notify_list_reserv = 64;
+			change_ctrl_subject_list_reserv = 8192;
+			delta_time_divider = 1000;
+			gl_context_major_version = 3;
+			gl_context_minor_version = 3;
+			smoothing_tick_samples = 100;
+			running = true;
+		}
+
+		int change_ctrl_cml_notify_list_reserv;
+		int change_ctrl_grain_size;
+		int change_ctrl_notify_list_reserv;
+		int change_ctrl_oneoff_notify_list_reserv;
+		int change_ctrl_subject_list_reserv;
+		int delta_time_divider;
+		int gl_context_major_version;
+		int gl_context_minor_version;
+		int smoothing_tick_samples;
+		bool running;
+	};
+	struct FramebfrVariables
+	{
+		FramebfrVariables()
+		{
+			gl_position_buffer_internal_format = GL_RGBA32F;
+			gl_position_buffer_texture_format = GL_RGBA;
+			gl_position_buffer_texture_type = GL_FLOAT;
+
+			gl_diffuse_buffer_internal_format = GL_RGBA16F;
+			gl_diffuse_buffer_texture_format = GL_RGBA;
+			gl_diffuse_buffer_texture_type = GL_FLOAT;
+
+			gl_emissive_buffer_internal_format = GL_RGBA16F;
+			gl_emissive_buffer_texture_format = GL_RGBA;
+			gl_emissive_buffer_texture_type = GL_FLOAT;
+
+			gl_normal_buffer_internal_format = GL_RGBA16F;
+			gl_normal_buffer_texture_format = GL_RGBA;
+			gl_normal_buffer_texture_type = GL_FLOAT;
+
+			gl_blur_buffer_internal_format = GL_RGBA16F;
+			gl_blur_buffer_texture_format = GL_RGBA;
+			gl_blur_buffer_texture_type = GL_FLOAT;
+
+			gl_final_buffer_internal_format = GL_RGBA16F;
+			gl_final_buffer_texture_format = GL_RGBA;
+			gl_final_buffer_texture_type = GL_FLOAT;
+
+			gl_depth_buffer_internal_format = GL_DEPTH_COMPONENT32F;
+			gl_depth_buffer_texture_format = GL_DEPTH_COMPONENT;
+			gl_depth_buffer_texture_type = GL_FLOAT;
+
+			gl_buffers_min_filter = GL_NEAREST;
+			gl_buffers_mag_filter = GL_NEAREST;
+			gl_buffers_wrap_s_method = GL_CLAMP_TO_EDGE;
+			gl_buffers_wrap_t_method = GL_CLAMP_TO_EDGE;
+
+			gl_blur_buffer_min_filter = GL_LINEAR;
+			gl_blur_buffer_mag_filter = GL_LINEAR;
+			gl_blur_buffer_wrap_s_method = GL_CLAMP_TO_EDGE;
+			gl_blur_buffer_wrap_t_method = GL_CLAMP_TO_EDGE;
+
+			gl_final_buffer_min_filter = GL_NEAREST;
+			gl_final_buffer_mag_filter = GL_NEAREST;
+			gl_final_buffer_s_method = GL_CLAMP_TO_EDGE;
+			gl_final_buffer_t_method = GL_CLAMP_TO_EDGE;
+		}
+
+		int gl_position_buffer_internal_format;
+		int gl_position_buffer_texture_type;
+		int gl_position_buffer_texture_format;
+
+		int gl_diffuse_buffer_internal_format;
+		int gl_diffuse_buffer_texture_format;
+		int gl_diffuse_buffer_texture_type;
+
+		int gl_emissive_buffer_internal_format;
+		int gl_emissive_buffer_texture_format;
+		int gl_emissive_buffer_texture_type;
+
+		int gl_normal_buffer_internal_format;
+		int gl_normal_buffer_texture_format;
+		int gl_normal_buffer_texture_type;
+
+		int gl_blur_buffer_internal_format;
+		int gl_blur_buffer_texture_type;
+		int gl_blur_buffer_texture_format;
+
+		int gl_final_buffer_internal_format;
+		int gl_final_buffer_texture_type;
+		int gl_final_buffer_texture_format;
+
+		int gl_depth_buffer_internal_format;
+		int gl_depth_buffer_texture_type;
+		int gl_depth_buffer_texture_format;
+
+		float gl_buffers_min_filter;
+		float gl_buffers_mag_filter;
+		int gl_buffers_wrap_s_method;
+		int gl_buffers_wrap_t_method;
+
+		float gl_blur_buffer_min_filter;
+		float gl_blur_buffer_mag_filter;
+		int gl_blur_buffer_wrap_s_method;
+		int gl_blur_buffer_wrap_t_method;
+
+		float gl_final_buffer_min_filter;
+		float gl_final_buffer_mag_filter;
+		int gl_final_buffer_s_method;
+		int gl_final_buffer_t_method;
+	};
+	struct GameplayVariables
+	{
+		GameplayVariables()
+		{
+			default_map = "default.pmap";
+			camera_freelook_speed = 10.0f;
+		}
+
+		std::string default_map;
+		float camera_freelook_speed;
+	};
+	struct GraphicsVariables
+	{
+		GraphicsVariables()
+		{
+			double_buffering = true;
+			multisampling = true;
+			alpha_size = 8;
+			current_resolution_x = 0;
+			current_resolution_y = 0;
+			dir_shadow_res_x = 2048;
+			dir_shadow_res_y = 2048;
+			max_num_point_lights = 50;
+			max_num_spot_lights = 50;
+			multisample_buffers = 1;
+			multisample_samples = 1;
+			rendering_res_x = 1600;
+			rendering_res_y = 900;
+			alpha_threshold = 0.0f;
+			emissive_threshold = 0.3f;
+			parallax_height_scale = 0.1f;
+			fog_color_x = 0.55f;
+			fog_color_y = 0.55f;
+			fog_color_z = 0.55f;
+			fog_density = 0.003f;
+			fov = 60.0f;
+			gamma = 2.2f;
+			light_atten_constant = 0.0f;
+			light_atten_linear = 0.0f;
+			light_atten_quadratic = 1.0f;
+			light_color_r = 1.0f;
+			light_color_g = 1.0f;
+			light_color_b = 1.0f;
+			texture_tiling_factor = 1.0f;
+			z_far = 8000.0f;
+			z_near = 0.1f;
+		}
+
+		bool double_buffering;
+		bool multisampling;
+		int alpha_size;
+		int current_resolution_x;
+		int current_resolution_y;
+		int dir_shadow_res_x;
+		int dir_shadow_res_y;
+		int max_num_point_lights;
+		int max_num_spot_lights;
+		int multisample_buffers;
+		int multisample_samples;
+		int rendering_res_x;
+		int rendering_res_y;
+		float alpha_threshold;
+		float emissive_threshold;
+		float parallax_height_scale;
+		float fog_color_x;
+		float fog_color_y;
+		float fog_color_z;
+		float fog_density;
+		float fov;
+		float gamma;
+		float light_atten_constant;
+		float light_atten_linear;
+		float light_atten_quadratic;
+		float light_color_r;
+		float light_color_g;
+		float light_color_b;
+		float texture_tiling_factor;
+		float z_far;
+		float z_near;
+	};
+	struct InputVariables
+	{
+		InputVariables()
+		{
+			back_key = 42;
+			backward_editor_key = 90;
+			backward_key = 22;
+			center_key = 93;
+			clip_mouse_key = 67;
+			close_window_key = 41;
+			debug_1_key = 58;
+			debug_2_key = 59;
+			down_editor_key = 89;
+			down_key = 89;
+			escape_key = 41;
+			forward_editor_key = 96;
+			forward_key = 26;
+			fullscreen_key = 66;
+			jump_key = 44;
+			left_editor_key = 92;
+			left_strafe_key = 4;
+			modifier_editor_key = 224;
+			next_editor_key = 85;
+			num_preallocated_keybinds = 110;
+			previous_editor_key = 84;
+			right_editor_key = 94;
+			right_strafe_key = 7;
+			save_editor_key = 22;
+			sprint_key = 225;
+			up_editor_key = 75;
+			up_key = 95;
+			vsync_key = 68;
+			mouse_filter = false;
+			mouse_warp_mode = true;
+			mouse_jaw = 0.022f;
+			mouse_pitch = 0.022f;
+			mouse_pitch_clip = 1.2f;
+			mouse_sensitivity = 3.0f;
+		}
+
+		int back_key;
+		int backward_editor_key;
+		int backward_key;
+		int center_key;
+		int clip_mouse_key;
+		int close_window_key;
+		int debug_1_key;
+		int debug_2_key;
+		int down_editor_key;
+		int down_key;
+		int escape_key;
+		int forward_editor_key;
+		int forward_key;
+		int fullscreen_key;
+		int jump_key;
+		int left_editor_key;
+		int left_strafe_key;
+		int modifier_editor_key;
+		int next_editor_key;
+		int num_preallocated_keybinds;
+		int previous_editor_key;
+		int right_editor_key;
+		int right_strafe_key;
+		int save_editor_key;
+		int sprint_key;
+		int up_editor_key;
+		int up_key;
+		int vsync_key;
+		bool mouse_filter;
+		bool mouse_warp_mode;
+		float mouse_jaw;
+		float mouse_pitch;
+		float mouse_pitch_clip;
+		float mouse_sensitivity;
+	};
+	struct ModelVariables
+	{
+		ModelVariables()
+		{
+			calcTangentSpace = true;
+			joinIdenticalVertices = false;
+			makeLeftHanded = false;
+			triangulate = true;
+			removeComponent = false;
+			genNormals = true;
+			genSmoothNormals = true;
+			genUVCoords = true;
+			optimizeMeshes = true;
+			optimizeGraph = false;
+		}
+
+		bool calcTangentSpace;
+		bool joinIdenticalVertices;
+		bool makeLeftHanded;
+		bool triangulate;
+		bool removeComponent;
+		bool genNormals;
+		bool genSmoothNormals;
+		bool genUVCoords;
+		bool optimizeMeshes;
+		bool optimizeGraph;
+	};
+	struct ObjectPoolVariables
+	{
+		ObjectPoolVariables()
+		{
+			model_object_pool_size = 20;
+			point_light_pool_size = 50;
+			shader_object_pool_size = 10;
+			spot_light_pool_size = 25;
+		}
+
+		int model_object_pool_size;
+		int point_light_pool_size;
+		int shader_object_pool_size;
+		int spot_light_pool_size;
+	};
+	struct PathsVariables
+	{
+		PathsVariables()
+		{
+			config_path = "Data\\";
+			map_path = "Data\\Maps\\";
+			model_path = "Data\\Models\\";
+			object_path = "Data\\Objects\\";
+			shader_path = "Data\\Shaders\\";
+			sound_path = "Data\\Sounds\\";
+			texture_path = "Data\\Materials\\";
+		}
+
+		std::string config_path;
+		std::string map_path;
+		std::string model_path;
+		std::string object_path;
+		std::string shader_path;
+		std::string sound_path;
+		std::string texture_path;
+	};
+	struct RendererVariables
+	{
+		RendererVariables()
+		{
+			dir_light_vert_shader = "dirLightPass.vert";
+			dir_light_frag_shader = "dirLightPass.frag";
+			point_light_vert_shader = "pointLightPass.vert";
+			point_light_frag_shader = "pointLightPass.frag";
+			spot_light_vert_shader = "spotLightPass.vert";
+			spot_light_frag_shader = "spotLightPass.frag";
+			dir_light_quad = "quad.obj";
+			point_light_sphere = "sphere.obj";
+			spot_light_cone = "cone.3ds";
+			stencil_pass_vert_shader = "stencilPass.vert";
+			stencil_pass_frag_shader = "stencilPass.frag";
+			geometry_pass_vert_shader = "geometryPass.vert";
+			geometry_pass_frag_shader = "geometryPass.frag";
+			geom_billboard_vert_shader = "geomBillboard.vert";
+			geom_billboard_frag_shader = "geomBillboard.frag";
+			geom_billboard_goem_shader = "geomBillboard.geom";
+			gaussian_blur_vertical_frag_shader = "gaussianBlurVertical.frag";
+			gaussian_blur_vertical_vert_shader = "gaussianBlurVertical.vert";
+			gaussian_blur_horizontal_frag_shader = "gaussianBlurHorizontal.frag";
+			gaussian_blur_horizontal_vert_shader = "gaussianBlurHorizontal.vert";
+			light_pass_vert_shader = "lightPass.vert";
+			light_pass_frag_shader = "lightPass.frag";
+			dir_light_quad_offset_x = 0.0f;
+			dir_light_quad_offset_y = 0.0f;
+			dir_light_quad_offset_z = 0.0f;
+			dir_light_quad_rotation_x = 180.0f;
+			dir_light_quad_rotation_y = 0.0f;
+			dir_light_quad_rotation_z = 0.0f;
+			depth_test_func = GL_LESS;
+			face_culling_mode = GL_BACK;
+			heightmap_combine_channel = 3;
+			heightmap_combine_texture = 1;
+			max_num_point_lights = 50;
+			max_num_spot_lights = 10;
+			objects_loaded_per_frame = 1;
+			shader_pool_size = 10;
+			depth_test = true;
+			face_culling = true;
+		}
+
+		std::string dir_light_vert_shader;
+		std::string dir_light_frag_shader;
+		std::string point_light_vert_shader;
+		std::string point_light_frag_shader;
+		std::string spot_light_vert_shader;
+		std::string spot_light_frag_shader;
+		std::string dir_light_quad;
+		std::string point_light_sphere;
+		std::string spot_light_cone;
+		std::string stencil_pass_vert_shader;
+		std::string stencil_pass_frag_shader;
+		std::string geometry_pass_vert_shader;
+		std::string geometry_pass_frag_shader;
+		std::string geom_billboard_vert_shader;
+		std::string geom_billboard_frag_shader;
+		std::string geom_billboard_goem_shader;
+		std::string gaussian_blur_vertical_frag_shader;
+		std::string gaussian_blur_vertical_vert_shader;
+		std::string gaussian_blur_horizontal_frag_shader;
+		std::string gaussian_blur_horizontal_vert_shader;
+		std::string light_pass_vert_shader;
+		std::string light_pass_frag_shader;
+		float dir_light_quad_offset_x;
+		float dir_light_quad_offset_y;
+		float dir_light_quad_offset_z;
+		float dir_light_quad_rotation_x;
+		float dir_light_quad_rotation_y;
+		float dir_light_quad_rotation_z;
+		int depth_test_func;
+		int face_culling_mode;
+		int heightmap_combine_channel;
+		int heightmap_combine_texture;
+		int max_num_point_lights;
+		int max_num_spot_lights;
+		int objects_loaded_per_frame;
+		int shader_pool_size;
+		bool depth_test;
+		bool face_culling;
+	};
+	struct ShaderVariables
+	{
+		ShaderVariables()
+		{
+			modelMatUniform = "modelMat";
+			viewMatUniform = "viewMat";
+			projectionMatUniform = "projMat";
+			viewProjectionMatUniform = "viewProjMat";
+			modelViewMatUniform = "modelViewMat";
+			modelViewProjectionMatUniform = "MVP";
+			screenSizeUniform = "screenSize";
+			elapsedTimeUniform = "elapsedTime";
+			gammaUniform = "gamma";
+			alphaCullingUniform = "alphaCulling";
+			alphaThresholdUniform = "alphaThreshold";
+			emissiveThresholdUniform = "emissiveThreshold";
+			parallaxHeightScale = "parallaxHeightScale";
+			textureTilingFactorUniform = "textureTilingFactor";
+
+			dirLightColor = "directionalLight.m_color";
+			dirLightDirection = "directionalLight.m_direction";
+			dirLightIntensity = "directionalLight.m_intensity";
+			numPointLightsUniform = "numPointLights";
+			numSpotLightsUniform = "numSpotLights";
+			pointLightViewProjectionMatUniform = "pointLightMVP";
+			pointLightBuffer = "PointLights";
+			spotLightBuffer = "SpotLights";
+			spotLightViewProjectionMatUniform = "spotLightMVP";
+			stencilPassViewProjectionMatUniform = "stencilMVP";
+
+			dirShadowMapMVPUniform = "dirShadowMapMVP";
+			dirShadowMapBiasMVPUniform = "dirShadowMapBiasMVP";
+
+			cameraPosVecUniform = "cameraPosVec";
+			cameraTargetVecUniform = "cameraTargetVec";
+			cameraUpVecUniform = "cameraUpVec";
+			cameraRightVecUniform = "cameraRightVec";
+
+			positionMapUniform = "positionMap";
+			diffuseMapUniform = "diffuseMap";
+			normalMapUniform = "normalMap";
+			emissiveMapUniform = "emissiveMap";
+			blurMapUniform = "blurMap";
+
+			sunGlowTextureUniform = "sunGlowMap";
+			skyMapTextureUniform = "skyMap";
+			dirShadowMapTextureUniform = "dirShadowMap";
+			diffuseTextureUniform = "diffuseTexture";
+			normalTextureUniform = "normalTexture";
+			specularTextureUniform = "specularTexture";
+			emissiveTextureUniform = "emissiveTexture";
+			glossTextureUniform = "glossTexture";
+			heightTextureUniform = "heightTexture";
+
+			fogDensityUniform = "fogDensity";
+			fogColorUniform = "fogColor";
+			billboardScaleUniform = "billboardScale";
+			depthTypeUniform = "depthType";
+
+			testMatUniform = "testMat";
+			testVecUniform = "testVec";
+			testFloatUniform = "testFloat";
+		}
+
+		std::string modelMatUniform;
+		std::string viewMatUniform;
+		std::string projectionMatUniform;
+		std::string viewProjectionMatUniform;
+		std::string modelViewMatUniform;
+		std::string modelViewProjectionMatUniform;
+		std::string screenSizeUniform;
+		std::string elapsedTimeUniform;
+		std::string gammaUniform;
+		std::string alphaCullingUniform;
+		std::string alphaThresholdUniform;
+		std::string emissiveThresholdUniform;
+		std::string parallaxHeightScale;
+		std::string textureTilingFactorUniform;
+
+		std::string dirLightColor;
+		std::string dirLightDirection;
+		std::string dirLightIntensity;
+		std::string numPointLightsUniform;
+		std::string numSpotLightsUniform;
+		std::string pointLightViewProjectionMatUniform;
+		std::string pointLightBuffer;
+		std::string spotLightBuffer;
+		std::string spotLightViewProjectionMatUniform;
+		std::string stencilPassViewProjectionMatUniform;
+
+		std::string dirShadowMapMVPUniform;
+		std::string dirShadowMapBiasMVPUniform;
+
+		std::string cameraPosVecUniform;
+		std::string cameraTargetVecUniform;
+		std::string cameraUpVecUniform;
+		std::string cameraRightVecUniform;
+
+		std::string positionMapUniform;
+		std::string diffuseMapUniform;
+		std::string normalMapUniform;
+		std::string emissiveMapUniform;
+		std::string blurMapUniform;
+
+		std::string sunGlowTextureUniform;
+		std::string skyMapTextureUniform;
+		std::string dirShadowMapTextureUniform;
+		std::string diffuseTextureUniform;
+		std::string normalTextureUniform;
+		std::string specularTextureUniform;
+		std::string emissiveTextureUniform;
+		std::string glossTextureUniform;
+		std::string heightTextureUniform;
+
+		std::string fogDensityUniform;
+		std::string fogColorUniform;
+		std::string billboardScaleUniform;
+		std::string depthTypeUniform;
+
+		std::string testMatUniform;
+		std::string testVecUniform;
+		std::string testFloatUniform;
+	};
+	struct TextureVariables
+	{
+		TextureVariables()
+		{
+			default_texture = "default.png";
+			default_emissive_texture = "default_emissive.png";
+			default_height_texture = "default_height.png";
+			default_normal_texture = "default_normal.png";
+			default_specular_intensity = 1.0f;
+			default_specular_power = 32.0f;
+			gl_texture_anisotropy = 16;
+			gl_texture_magnification = GL_LINEAR;
+			gl_texture_minification = GL_LINEAR_MIPMAP_LINEAR;
+			number_of_mipmaps = 50;
+			generate_mipmaps = true;
+		}
+
+		std::string default_texture;
+		std::string default_emissive_texture;
+		std::string default_height_texture;
+		std::string default_normal_texture;
+		float default_specular_intensity;
+		float default_specular_power;
+		int gl_texture_anisotropy;
+		int gl_texture_magnification;
+		int gl_texture_minification;
+		int number_of_mipmaps;
+		bool generate_mipmaps;
+	};
+	struct WindowVariables
+	{
+		WindowVariables()
+		{
+			name = "Praxis3D";
+			default_display = 0;
+			window_position_x = 0;
+			window_position_y = 0;
+			window_size_fullscreen_x = 1920;
+			window_size_fullscreen_y = 1080;
+			window_size_windowed_x = 800;
+			window_size_windowed_y = 600;
+			fullscreen = false;
+			fullscreen_borderless = true;
+			mouse_captured = true;
+			mouse_release_on_lost_focus = true;
+			resizable = true;
+			vertical_sync = true;
+			window_in_focus = true;
+		}
+
+		std::string name;
+		int default_display;
+		int window_position_x;
+		int window_position_y;
+		int window_size_fullscreen_x;
+		int window_size_fullscreen_y;
+		int window_size_windowed_x;
+		int window_size_windowed_y;
+		bool fullscreen;
+		bool fullscreen_borderless;
+		bool mouse_captured;
+		bool mouse_release_on_lost_focus;
+		bool resizable;
+		bool vertical_sync;
+		bool window_in_focus;
+	};
+
+	const inline static ConfigFileVariables	&configFileVar()	{ return m_configFileVar;	}
+	const inline static EngineVariables		&engineVar()		{ return m_engineVar;		}
+	const inline static FramebfrVariables	&getFramebfrVar()	{ return m_framebfrVar;		}
+	const inline static GameplayVariables	&gameplayVar()		{ return m_gameplayVar;		}
+	const inline static GraphicsVariables	&graphicsVar()		{ return m_graphicsVar;		}
+	const inline static InputVariables		&inputVar()			{ return m_inputVar;		}
+	const inline static ModelVariables		&modelVar()			{ return m_modelVar;		}
+	const inline static ObjectPoolVariables &objectPoolVar()	{ return m_objPoolVar;		}
+	const inline static PathsVariables		&filepathVar()		{ return m_filepathVar;		}
+	const inline static RendererVariables	&rendererVar()		{ return m_rendererVar;		}
+	const inline static ShaderVariables		&shaderVar()		{ return m_shaderVar;		}
+	const inline static TextureVariables	&textureVar()		{ return m_textureVar;		}
+	const inline static WindowVariables		&windowVar()		{ return m_windowVar;		}
+
+	// Register all config variables, so we can search through them later
+	static void init();
+	static ErrorCode loadFromFile(const std::string &p_filename);
+	static ErrorCode saveToFile(const std::string &p_filename);
+
+private:
+	class Variable
+	{
+	public:
+		Variable(std::string p_name, size_t p_mapKey, int *p_int) : m_varType(VariableType::intType), m_name(p_name), m_mapKey(p_mapKey), m_valueChanged(false)
+		{
+			m_variable.intPtr = p_int;
+		}
+		Variable(std::string p_name, size_t p_mapKey, bool *p_bool) : m_varType(VariableType::boolType), m_name(p_name), m_mapKey(p_mapKey), m_valueChanged(false)
+		{
+			m_variable.boolPtr = p_bool;
+		}
+		Variable(std::string p_name, size_t p_mapKey, float *p_float) : m_varType(VariableType::floatType), m_name(p_name), m_mapKey(p_mapKey), m_valueChanged(false)
+		{
+			m_variable.floatPtr = p_float;
+		}
+		Variable(std::string p_name, size_t p_mapKey, std::string *p_string) : m_varType(VariableType::stringType), m_name(p_name), m_mapKey(p_mapKey)
+		{
+			m_variable.stringPtr = p_string;
+		}
+		~Variable() { }
+
+		inline bool operator==(const size_t p_mapKey)		 { return (m_mapKey == p_mapKey); }
+		inline bool operator==(const std::string &p_name) { return (m_name == p_name); }
+
+		inline bool valueChanged() { return m_valueChanged; }
+
+		const inline std::string &getName() { return m_name; }
+		inline std::string toString()
+		{
+			std::string returnString = m_name + " ";
+
+			switch(m_varType)
+			{
+			case Config::Variable::intType:
+				returnString += Utilities::toString(*m_variable.intPtr);
+				break;
+			case Config::Variable::boolType:
+				returnString += Utilities::toString(*m_variable.boolPtr);
+				break;
+			case Config::Variable::floatType:
+				returnString += Utilities::toString(*m_variable.floatPtr);
+				break;
+			case Config::Variable::stringType:
+				returnString += *m_variable.stringPtr;
+				break;
+			}
+
+			return returnString;
+		}
+
+		void setVariable(std::string &p_variable)
+		{
+			m_valueChanged = true;
+
+			switch(m_varType)
+			{
+			case Config::Variable::intType:
+				*m_variable.intPtr = std::atoi(p_variable.c_str());
+				break;
+			case Config::Variable::boolType:
+				*m_variable.boolPtr = (p_variable == "1" || p_variable == "true" || p_variable == "True" || p_variable == "TRUE");
+				break;
+			case Config::Variable::floatType:
+				*m_variable.floatPtr = (float)std::atof(p_variable.c_str());
+				break;
+			case Config::Variable::stringType:
+				*m_variable.stringPtr = p_variable;
+				break;
+			}
+		}
+	
+	private:
+		enum VariableType
+		{
+			intType,
+			boolType,
+			floatType,
+			stringType
+		};
+		union
+		{
+			int *intPtr;
+			bool *boolPtr;
+			float *floatPtr;
+			std::string *stringPtr;
+		} m_variable;
+
+		VariableType m_varType;
+		std::string m_name;
+		bool m_valueChanged;
+		size_t m_mapKey;
+	};
+
+	static ConfigFileVariables	m_configFileVar;
+	static EngineVariables		m_engineVar;
+	static FramebfrVariables	m_framebfrVar;
+	static GameplayVariables	m_gameplayVar;
+	static GraphicsVariables	m_graphicsVar;
+	static InputVariables		m_inputVar;
+	static ModelVariables		m_modelVar;
+	static ObjectPoolVariables	m_objPoolVar;
+	static PathsVariables		m_filepathVar;
+	static RendererVariables	m_rendererVar;
+	static ShaderVariables		m_shaderVar;
+	static TextureVariables		m_textureVar;
+	static WindowVariables		m_windowVar;
+
+	static std::vector<Variable> m_variables;
+	static std::unordered_map<std::string, size_t> m_hashTable;
+
+	static const std::vector<Variable>::size_type m_varVectorOffset = 1;
+
+	static void setVariable(std::string p_name, std::string p_variable);
+
+	inline static ConfigFileVariables	&setConfigFileVar()	{ return m_configFileVar;	}
+	inline static EngineVariables		&setEngineVar()		{ return m_engineVar;		}
+	inline static FramebfrVariables		&setFramebfrVar()	{ return m_framebfrVar;		}
+	inline static GameplayVariables		&setGameplayVar()	{ return m_gameplayVar;		}
+	inline static GraphicsVariables		&setGraphicsVar()	{ return m_graphicsVar;		}
+	inline static InputVariables		&setInputVar()		{ return m_inputVar;		}
+	inline static ModelVariables		&setModelVar()		{ return m_modelVar;		}
+	inline static PathsVariables		&setFilepathVar()	{ return m_filepathVar;		}
+	inline static RendererVariables		&setRendererVar()	{ return m_rendererVar;		}
+	inline static ShaderVariables		&setShaderVar()		{ return m_shaderVar;		}
+	inline static TextureVariables		&setTextureVar()	{ return m_textureVar;		}
+	inline static WindowVariables		&setWindowVar()		{ return m_windowVar;		}
+};

+ 277 - 0
Praxis3D/Source/ConfigLoader.cpp

@@ -0,0 +1,277 @@
+#include "ConfigLoader.h"
+#include "ErrorHandlerLocator.h"
+
+ConfigFile::NullValue ConfigFile::NodeBase::m_nullValue;
+std::vector<ConfigFile::NodeBase*> ConfigFile::NodeBase::m_nullArray;
+std::vector<std::vector<ConfigFile::NodeBase*>*> ConfigFile::NodeBase::m_nullArrayOfArray;
+ConfigFile::NodeBase NodeIterator::m_nullNode("", 0);
+
+ConfigFile::ConfigFile()
+{
+
+}
+ConfigFile::~ConfigFile()
+{
+
+}
+
+ErrorCode ConfigFile::import(std::string p_filename, std::unordered_map<std::string, int> &p_hashMap)
+{
+	//m_configString = p_filename;
+	ErrorCode returnCode = ErrorCode::Success;
+
+	// Load the config from a text file
+	returnCode = loadFromFile(p_filename);
+
+	// Check for errors
+	if(returnCode != ErrorCode::Success)
+		return returnCode;
+	else
+	{
+		// Propagate the node structure
+		returnCode = generateNodes(p_hashMap);
+
+		// Check for errors
+		if(returnCode != ErrorCode::Success)
+			return returnCode;
+	}
+
+	return returnCode;
+}
+NodeIterator ConfigFile::getRootNode()
+{
+	if(m_rootNode)
+		return NodeIterator(m_rootNode->getChildren());
+
+	return NodeIterator();
+}
+
+ErrorCode ConfigFile::loadFromFile(std::string p_filename)
+{
+	ErrorCode returnError = ErrorCode::Success;
+
+	std::ifstream configFile;
+	std::string singleLine, tempConfigString;
+
+	configFile.open(p_filename, std::ios::in);
+
+	if(configFile.fail())
+	{
+		configFile.close();
+		//throw Message::messageCode(MSG_FATAL_ERROR, MSG_OBJECT, configFileName + ": Has failed to load.");
+		returnError = ErrorCode::Ifstream_failed;
+	}
+	else
+	{
+		while(!configFile.eof())
+		{
+			// Get every line, to be processed
+			std::getline(configFile, singleLine);
+
+			// If it contains commentary marks, discard the whole line
+			if(singleLine.size() >= 2 && singleLine[0] == '/' && singleLine[1] == '/')
+				continue;
+
+			// Add each line to the whole file string
+			tempConfigString += singleLine;
+		}
+
+		// Remove all spaces and horizontal tabs, except from in between quotation marks
+		for(std::vector<std::string>::size_type i = 0; i < tempConfigString.size(); i++)
+		{
+			if(tempConfigString[i] == '"')					// If it's a quotation mark
+			{
+				m_configString += tempConfigString[i];		// put the first quotation mark in the final string
+				for(i++; tempConfigString[i] != '"'; i++)	// iterate to find the second quotation mark
+					m_configString += tempConfigString[i];	// put every char (that is in between quotation marks) in the final string
+			}
+
+			if(tempConfigString[i] != ' ' && tempConfigString[i] != '\t')	// If the char is not space or tab
+				m_configString += tempConfigString[i];						// Copy it to the final string
+		}
+		configFile.close();
+	}
+
+	return returnError;
+}
+ErrorCode ConfigFile::generateNodes(std::unordered_map<std::string, int> &p_hashMap)
+{
+	ErrorCode returnError = ErrorCode::Success;
+
+	std::vector<std::string>::size_type index = 0;
+	m_rootNode = new ConfigFile::ParentNode("root", p_hashMap["root"]);
+
+	// Find the first open curled bracket
+	for(; m_configString[index] != '{'; index++);
+
+	do
+	{
+		m_rootNode->addChild(getNextNode(index, p_hashMap));
+	} while(m_configString[index] == ',');
+
+	return returnError;
+}
+
+std::string ConfigFile::getNextField(std::vector<std::string>::size_type &p_index)
+{
+	std::string returnName;
+
+	// Search for the first quotation marks
+	for(; p_index < m_configString.size() && m_configString[p_index] != '"'; p_index++);
+
+	// Check if it's end of the string, to not couse out of bounds error
+	if(p_index < m_configString.size())
+		p_index++;	// Increment, so search for second quotation marks won't find first quotation marks
+
+	// Search for the second quotation marks
+	for(; p_index < m_configString.size() && m_configString[p_index] != '"'; p_index++)
+		returnName += m_configString[p_index];
+
+	// Check if it's the end of the string, to not couse out of bounds error
+	if(p_index < m_configString.size())
+		p_index++;	// Increment, so index is not on second quotation marks
+	else
+		p_index--;	// Decrement, so end character ( "}" ) can be found, by main while loop
+
+	return returnName;
+}
+
+ConfigFile::NodeBase *ConfigFile::getNextNode(std::vector<std::string>::size_type &p_index, std::unordered_map<std::string, int> &p_hashMap)
+{
+	NodeBase *returnNode = nullptr;
+
+	// Get the name of the node
+	std::string nodeName = getNextField(p_index);
+
+	// Determine what kind of node to parse, depending on the symbol that's following ":"
+	if(m_configString[p_index] == ':')
+	{
+		p_index++;
+		switch(m_configString[p_index])
+		{
+		// Quotation marks means it only has one entry, with a value in it - hence value node
+		// Just parse the value and convert it to one of the baseValues
+		case '"':
+			returnNode = new ValueNode(nodeName, p_hashMap[nodeName]);
+			returnNode->setValue(getValueNode(getNextField(p_index)));
+			break;
+
+		// Curly brackets means there are more than one entry to follow, which means it's a parent node
+		// Parse every child recursively
+		case '{':
+			returnNode = new ParentNode(nodeName, p_hashMap[nodeName]);
+
+			do
+			{
+				p_index++;
+
+				// Check if the curly bracket scope is not empty
+				if(m_configString[p_index] != '}')
+					returnNode->addChild(getNextNode(p_index, p_hashMap));
+
+			} while(m_configString[p_index] == ',');
+			p_index++;
+			break;
+
+		// Array brackets means there are array-like entries to follow
+		// Go over each entry and add it as a child, recursively
+		case '[':
+			returnNode = new ArrayNode(nodeName, p_hashMap[nodeName]);
+			p_index++;
+
+			// Check if the array bracket scope is not empty
+			if(m_configString[p_index] != ']')
+			{
+				do
+				{
+					std::vector<NodeBase*> *arrayNodes = new std::vector<NodeBase*>;
+
+					do
+					{
+						p_index++;
+						arrayNodes->push_back(getNextNode(p_index, p_hashMap));
+					} while(m_configString[p_index] == ',');
+					p_index++;
+
+					returnNode->addArrayEntry(arrayNodes);
+
+				} while(m_configString[p_index] == ',');
+			}
+			if(m_configString[p_index] != ']')
+			{
+				ErrHandlerLoc::get().log(Warning, Source_FileLoader, "Missing \" ] \" sign");
+			}
+			else
+				p_index++;
+			break;
+
+			returnNode = new NodeBase(nodeName, p_hashMap[nodeName]);
+			ErrHandlerLoc::get().log(Warning, Source_FileLoader, "Unknown sign " + Utilities::toString(m_configString[p_index]) + ". Should be \" \" \", \" { \" or \" [ \"");
+		}
+	}
+	// If the colon symbol was missing, that means there's a syntax mistake in the script
+	// Return a "null" node instead, with an error, so it's save to load a script even with mistakes
+	else
+	{
+		returnNode = new NodeBase(nodeName, p_hashMap[nodeName]);
+		ErrHandlerLoc::get().log(Warning, Source_FileLoader, "Missing \" : \" sign");
+	}
+
+	return returnNode;
+}
+
+ConfigFile::BaseValue *ConfigFile::getValueNode(std::string p_value)
+{
+	if(p_value == "true" || p_value == "false")	// If value is bool
+		return new BoolValue(p_value);
+
+	int decimalPoints = 0, commas = 0;
+	for(std::vector<std::string>::size_type i = 0; i < p_value.size(); i++)
+	{
+		if(isalpha(p_value[i]) && (i < p_value.size() || p_value[i] != 'f'))	// If value is string
+			return new StringValue(p_value);
+
+		if(p_value[i] == '.')
+			decimalPoints++;
+		if(p_value[i] == ',')
+			commas++;
+	}
+
+	if(decimalPoints == 0)	// If value is integer
+	{
+		return new IntValue(p_value);
+	}
+	else
+	{
+		if(decimalPoints == 4 && commas == 3)	// If value is vector of 4 floats
+		{
+			return new Vec4fValue(p_value);
+		}
+		else
+		{
+			if(decimalPoints == 3 && commas == 2)	// If value is vector of 3 floats
+			{
+				return new Vec3fValue(p_value);
+			}
+			else
+			{
+				if(decimalPoints == 2 && commas == 1)	// If value is vector of 2 floats
+				{
+					return new Vec2fValue(p_value);
+				}
+				else
+				{
+					if(decimalPoints == 1 && commas == 0)
+					{
+						if(p_value[p_value.size() - 1] == 'f')	// If value is float
+							return new FloatValue(p_value);
+						else									// If value is double (not implemented yet)
+							return new DoubleValue(p_value);
+					}
+					else
+						return new BoolValue("false");	// If value is incorrect or cannot be read, just create a bool and set it to false
+				}
+			}
+		}
+	}
+}

+ 762 - 0
Praxis3D/Source/ConfigLoader.h

@@ -0,0 +1,762 @@
+#pragma once
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "ErrorCodes.h"
+#include "Math.h"
+#include "Utilities.h"
+
+// Forward declaration
+class NodeIterator;
+
+class ConfigFile
+{
+friend class NodeIterator;
+public:
+	ConfigFile();
+	~ConfigFile();
+
+	ErrorCode import(std::string p_filename, std::unordered_map<std::string, int> &p_hashMap);
+	
+	NodeIterator getRootNode();
+
+private:
+	// Forward declaration
+	class NodeBase;
+	class BaseValue;
+
+	// Internal loading functions
+	ErrorCode loadFromFile(std::string p_filename);
+	ErrorCode generateNodes(std::unordered_map<std::string, int> &p_hashMap);
+
+	// Returns the next string that is between quotation marks (could be a name or a value)
+	std::string getNextField(std::vector<std::string>::size_type &p_index);
+
+	// Methods for filling values and nodes, from raw string data
+	NodeBase *getNextNode(std::vector<std::string>::size_type &p_index, std::unordered_map<std::string, int> &p_hashMap);
+
+	BaseValue *getValueNode(std::string p_value);
+
+	// All currently supported value types
+	class BaseValue
+	{
+	public:
+		virtual std::string getString() = 0;
+		virtual bool getBool() = 0;
+		virtual int getInt() = 0;
+		virtual float getFloat() = 0;
+		virtual double getDouble() = 0;
+		virtual Math::Vec2f getVec2f() = 0;
+		virtual Math::Vec3f getVec3f() = 0;
+		virtual Math::Vec4f getVec4f() = 0;
+
+	protected:
+		virtual void setParameter(std::string p_value) = 0;
+	};
+	class BoolValue : public BaseValue
+	{
+	public:
+		BoolValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value);
+		}
+		inline bool getBool()
+		{
+			return m_value;
+		}
+		inline int getInt()
+		{
+			return m_value ? 0 : 1;
+		}
+		inline float getFloat()
+		{
+			return m_value ? 0.0f : 1.0f;
+		}
+		inline double getDouble()
+		{
+			return m_value ? 0.0 : 1.0;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return m_value ? Math::Vec2f(0.0) : Math::Vec2f(1.0);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return m_value ? Math::Vec3f(0.0) : Math::Vec3f(1.0);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return m_value ? Math::Vec4f(0.0) : Math::Vec4f(1.0);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			m_value = (p_value == "1" || p_value == "true" || p_value == "True" || p_value == "TRUE");
+		}
+
+		bool m_value;
+	};
+	class IntValue : public BaseValue
+	{
+	public:
+		IntValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value);
+		}
+		inline bool getBool()
+		{
+			return m_value == 0 ? false : true;
+		}
+		inline int getInt()
+		{
+			return m_value;
+		}
+		inline float getFloat()
+		{
+			return (float) m_value;
+		}
+		inline double getDouble()
+		{
+			return (double) m_value;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f((float) m_value);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f((float) m_value);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f((float) m_value);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			m_value = std::atoi(p_value.c_str());
+		}
+
+		int m_value;
+	};
+	class FloatValue : public BaseValue
+	{
+	public:
+		FloatValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value);
+		}
+		inline bool getBool()
+		{
+			return m_value == 0.0 ? false : true;
+		}
+		inline int getInt()
+		{
+			return (int) m_value;
+		}
+		inline float getFloat()
+		{
+			return m_value;
+		}
+		inline double getDouble()
+		{
+			return (double) m_value;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f(m_value);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f(m_value);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f(m_value);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			m_value = (float) std::atof(p_value.c_str());
+		}
+
+		float m_value;
+	};
+	class DoubleValue : public BaseValue
+	{
+	public:
+		DoubleValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value);
+		}
+		inline bool getBool()
+		{
+			return m_value == 0.0 ? false : true;
+		}
+		inline int getInt()
+		{
+			return (int) m_value;
+		}
+		inline float getFloat()
+		{
+			return (float) m_value;
+		}
+		inline double getDouble()
+		{
+			return m_value;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f((float) m_value);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f((float) m_value);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f((float) m_value);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			m_value = std::atof(p_value.c_str());
+		}
+
+		double m_value;
+	};
+	class StringValue : public BaseValue
+	{
+	public:
+		StringValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return m_value;
+		}
+		inline bool getBool()
+		{
+			return std::atoi(m_value.c_str()) == 0 ? true : false;
+		}
+		inline int getInt()
+		{
+			return std::atoi(m_value.c_str());
+		}
+		inline float getFloat()
+		{
+			return (float) std::atof(m_value.c_str());
+		}
+		inline double getDouble()
+		{
+			return std::atof(m_value.c_str());
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f((float) std::atof(m_value.c_str()));
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f((float) std::atof(m_value.c_str()));
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f((float) std::atof(m_value.c_str()));
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			m_value = p_value;
+		}
+
+		std::string m_value;
+	};
+	class Vec2fValue : public BaseValue
+	{
+	public:
+		Vec2fValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value.x) + ", " + Utilities::toString(m_value.y);
+		}
+		inline bool getBool()
+		{
+			return (m_value.x == 0.0 && m_value.y == 0.0) ? false : true;
+		}
+		inline int getInt()
+		{
+			return (int) m_value.x;
+		}
+		inline float getFloat()
+		{
+			return m_value.x;
+		}
+		inline double getDouble()
+		{
+			return (float) m_value.x;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return m_value;
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f(m_value, 0.0f);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f(m_value, 0.0f, 0.0f);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			std::string xString, yString;
+			std::vector<std::string>::size_type index;
+			for(index = 0; p_value[index] != ','; index++)
+				xString += p_value[index];	index++;
+			for(; index < p_value.size(); index++)
+				yString += p_value[index];
+
+			m_value = Math::Vec2f((float) std::atof(xString.c_str()), (float) std::atof(yString.c_str()));
+		}
+
+		Math::Vec2f m_value;
+	};
+	class Vec3fValue : public BaseValue
+	{
+	public:
+		Vec3fValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value.x) + ", " + Utilities::toString(m_value.y) + ", " + Utilities::toString(m_value.z);
+		}
+		inline bool getBool()
+		{
+			return (m_value.x == 0.0 && m_value.y == 0.0 && m_value.z == 0.0) ? false : true;
+		}
+		inline int getInt()
+		{
+			return (int) m_value.x;
+		}
+		inline float getFloat()
+		{
+			return m_value.x;
+		}
+		inline double getDouble()
+		{
+			return (float) m_value.x;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f(m_value);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return m_value;
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return Math::Vec4f(m_value, 0.0f);
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			std::string xString, yString, zString;
+			std::vector<std::string>::size_type index;
+			for(index = 0; p_value[index] != ','; index++)
+				xString += p_value[index];	index++;
+			for(; p_value[index] != ','; index++)
+				yString += p_value[index];	index++;
+			for(; index < p_value.size(); index++)
+				zString += p_value[index];
+
+			m_value = Math::Vec3f((float) std::atof(xString.c_str()),
+								  (float) std::atof(yString.c_str()),
+								  (float) std::atof(zString.c_str()));
+		}
+
+		Math::Vec3f m_value;
+	};
+	class Vec4fValue : public BaseValue
+	{
+	public:
+		Vec4fValue(std::string p_value)
+		{
+			setParameter(p_value);
+		}
+
+		inline std::string getString()
+		{
+			return Utilities::toString(m_value.x) + ", " + Utilities::toString(m_value.y) + ", " + Utilities::toString(m_value.z) + ", " + Utilities::toString(m_value.w);
+		}
+		inline bool getBool()
+		{
+			return (m_value.x == 0.0 && m_value.y == 0.0 && m_value.z == 0.0 == 0.0 && m_value.w == 0.0) ? false : true;
+		}
+		inline int getInt()
+		{
+			return (int) m_value.x;
+		}
+		inline float getFloat()
+		{
+			return m_value.x;
+		}
+		inline double getDouble()
+		{
+			return (float) m_value.x;
+		}
+		inline Math::Vec2f getVec2f()
+		{
+			return Math::Vec2f(m_value);
+		}
+		inline Math::Vec3f getVec3f()
+		{
+			return Math::Vec3f(m_value);
+		}
+		inline Math::Vec4f getVec4f()
+		{
+			return m_value;
+		}
+
+	private:
+		inline void setParameter(std::string p_value)
+		{
+			std::string xString, yString, zString, wString;
+			std::vector<std::string>::size_type index;
+			for(index = 0; p_value[index] != ','; index++)
+				xString += p_value[index];	index++;
+			for(; p_value[index] != ','; index++)
+				yString += p_value[index];	index++;
+			for(; p_value[index] != ','; index++)
+				zString += p_value[index];	index++;
+			for(; index < p_value.size(); index++)
+				wString += p_value[index];
+			m_value = Math::Vec4f((float) std::atof(xString.c_str()),
+								  (float) std::atof(yString.c_str()),
+								  (float) std::atof(zString.c_str()),
+								  (float) std::atof(wString.c_str()));
+		}
+
+		Math::Vec4f m_value;
+	};
+	class NullValue : public BaseValue
+	{
+	public:
+		NullValue() { }
+
+		inline std::string getString() { return "null"; }
+		inline bool getBool() { return false; }
+		inline int getInt() { return 0; }
+		inline float getFloat() { return 0.0f; }
+		inline double getDouble() { return 0.0; }
+		inline Math::Vec2f getVec2f() { return Math::Vec2f(); }
+		inline Math::Vec3f getVec3f() { return Math::Vec3f(); }
+		inline Math::Vec4f getVec4f() { return Math::Vec4f(); }
+
+	protected:
+		inline void setParameter(std::string p_value) { }
+	};
+	
+	class NodeBase
+	{
+		friend class ConfigFile;
+		friend class NodeIterator;
+	public:
+		NodeBase(std::string p_name, int p_mapKey) : m_name(p_name), m_mapKey(p_mapKey) { }
+		~NodeBase() { }
+
+		inline std::string getName() { return m_name; }
+		inline int getHashKey() { return m_mapKey; }
+
+		inline bool operator==(const int p_mapKey) { return (m_mapKey == p_mapKey); }
+		inline bool operator==(const std::string p_name) { return (m_name == p_name); }
+
+		inline virtual BaseValue &getValue() { return m_nullValue; }
+
+		inline virtual size_t getNumArrayEntries() { return 0; }
+		inline virtual size_t getNumChildren() { return 0; }
+
+		inline virtual std::string convertToString(const std::string &p_prefix) { return "\"" + m_name + "\": \"" + m_nullValue.getString() + "\""; }
+
+	protected:
+		const inline virtual void setValue(BaseValue *p_value) { delete p_value; }
+
+		inline virtual void addChild(NodeBase *p_child) { delete p_child; }
+		inline virtual void addArrayEntry(std::vector<NodeBase*> *p_arrayEntry) { delete p_arrayEntry; }
+
+		inline virtual std::vector<NodeBase*> *getChildren() { return &m_nullArray; }
+		inline virtual std::vector<std::vector<ConfigFile::NodeBase*>*> *getNodeArray() { return &m_nullArrayOfArray; }
+
+		int m_mapKey;
+		std::string m_name;
+
+		// Null values used for returning instead of null pointers, so the program doesn't crash
+		static NullValue m_nullValue;
+		static std::vector<NodeBase*> m_nullArray;
+		static std::vector<std::vector<ConfigFile::NodeBase*>*> m_nullArrayOfArray;
+	};
+	class ValueNode : public NodeBase
+	{
+		friend ConfigFile::NodeBase *ConfigFile::getNextNode(std::vector<std::string>::size_type &p_index, std::unordered_map<std::string, int> &p_hashMap);
+	public:
+		ValueNode(std::string p_name, int p_mapKey) : NodeBase(p_name, p_mapKey), m_value(nullptr) { }
+		~ValueNode() { delete m_value; }
+
+		inline BaseValue &getValue() { return (m_value) ? *m_value : m_nullValue; }
+
+		inline std::string convertToString(const std::string &p_prefix)
+		{
+			// Add name and value between quotation marks, separate them with colon and a space
+			// Also check if value has been defined, if not, use null value
+			return p_prefix + "\"" + m_name + "\": \"" + ((m_value != nullptr) ? m_value->getString() : m_nullValue.getString()) + "\"";
+		}
+
+	protected:
+		const inline void setValue(BaseValue *p_value)
+		{
+			if(m_value != nullptr)
+				delete m_value;
+			m_value = p_value;
+		}
+
+	private:
+		BaseValue *m_value;
+	};
+	class ParentNode : public NodeBase
+	{
+		friend ConfigFile::NodeBase *ConfigFile::getNextNode(std::vector<std::string>::size_type &p_index, std::unordered_map<std::string, int> &p_hashMap);
+	public:
+		ParentNode(std::string p_name, int p_mapKey) : NodeBase(p_name, p_mapKey) { }
+		~ParentNode()
+		{
+			for(std::vector<NodeBase*>::size_type i = 0, vectorSize = m_childNodes.size(); i < vectorSize; i++)
+				delete m_childNodes[i];
+		}
+
+		inline std::string convertToString(const std::string &p_prefix)
+		{
+			std::string newPrefix = p_prefix + "\t";
+			std::string returnString = p_prefix + "\"" + m_name + "\":\n{\n" + newPrefix;
+
+			// Convert each child to string recursively
+			for(std::vector<NodeBase*>::size_type i = 0, size = m_childNodes.size(); i < size; i++)
+			{
+				returnString += m_childNodes[i]->convertToString(newPrefix);
+				if(i + 1 < size)
+					returnString += ",\n";
+			}
+
+			returnString += "\n}";
+			return returnString;
+		}
+
+		size_t getNumArrayEntries() { return m_childNodes.size(); }
+
+	protected:
+		inline void addChild(NodeBase *p_child) { m_childNodes.push_back(p_child); }
+
+		inline std::vector<NodeBase*> *getChildren() { return &m_childNodes; }
+
+	private:
+		std::vector<NodeBase*> m_childNodes;
+	};
+	class ArrayNode : public NodeBase
+	{
+		friend ConfigFile::NodeBase *ConfigFile::getNextNode(std::vector<std::string>::size_type &p_index, std::unordered_map<std::string, int> &p_hashMap);
+	public:
+		ArrayNode(std::string p_name, int p_mapKey) : NodeBase(p_name, p_mapKey), m_nodeArraySize(0) { }
+		~ArrayNode()
+		{
+			for(std::vector<std::vector<NodeBase*>>::size_type i = 0, iSize = m_nodeArray.size(); i < iSize; i++)
+			{
+				if(m_nodeArray[i] != nullptr)
+				{
+					for(std::vector<NodeBase*>::size_type j = 0, jSize = m_nodeArray[i]->size(); j < jSize; j++)
+						delete (*m_nodeArray[i])[j];
+				}
+				else
+					delete m_nodeArray[i];
+			}
+		}
+
+		inline std::string convertToString(const std::string &p_prefix)
+		{
+			std::string outerPrefix = p_prefix + "\t";
+			std::string innerPrefix = p_prefix + "\t\t";
+			std::string returnString = p_prefix + "\"" + m_name + "\":\n[";
+
+			// Convert each child in each array entry to string recursively
+			for(std::vector<std::vector<NodeBase*>*>::size_type i = 0, iSize = m_nodeArray.size(); i < iSize; i++)
+			{
+				returnString += "\n" + outerPrefix + "{\n";
+				for(std::vector<NodeBase*>::size_type j = 0, jSize = m_nodeArray[i]->size(); j < jSize; j++)
+				{
+					returnString += (*m_nodeArray[i])[j]->convertToString(innerPrefix);
+				}
+				returnString += "\n" + outerPrefix + "}";
+				if(i + 1 < iSize)
+					returnString += ",";
+			}
+
+			returnString += "\n]";
+			return returnString;
+		}
+
+		size_t getNumChildren() { return m_nodeArray.size(); }
+
+		//const inline std::vector<NodeBase*> &getArrayEntry(std::vector<std::vector<NodeBase*>*>::size_type p_index) { return (p_index < m_nodeArraySize) ? *m_nodeArray[p_index] : m_nullArray; }
+
+	protected:
+		inline void addArrayEntry(std::vector<NodeBase*> *p_arrayEntry)
+		{
+			m_nodeArray.push_back(p_arrayEntry);
+			m_nodeArraySize = m_nodeArray.size();
+		}
+
+		inline std::vector<std::vector<ConfigFile::NodeBase*>*> *getNodeArray() { return &m_nodeArray; }
+
+	private:
+		std::vector<std::vector<NodeBase*>*> m_nodeArray;
+		std::vector<std::vector<NodeBase*>*>::size_type m_nodeArraySize;
+	};
+
+	NodeBase *m_rootNode;
+	std::string m_filename;
+	std::string m_configString;
+};
+
+class NodeIterator
+{
+	friend class ConfigFile;
+private:
+	NodeIterator()
+		: m_nodes(nullptr), m_nodeArray(nullptr), m_endOfArray(true), m_index(0), m_nodeArraySize(0), m_validObject(false)
+	{
+
+	}
+	NodeIterator(std::vector<ConfigFile::NodeBase*> *p_nodes)
+		: m_nodes(p_nodes), m_nodeArray(nullptr), m_endOfArray(false), m_index(0), m_nodeArraySize(0), m_validObject(true)
+	{
+		if(!p_nodes)
+		{
+			m_validObject = false;
+			m_nodes = nullptr;
+		}
+		else
+			if(p_nodes->size() == 0)
+			{
+				m_validObject = false;
+				m_nodes = nullptr;
+			}
+	}
+	NodeIterator(std::vector<std::vector<ConfigFile::NodeBase*>*> *p_nodeArray)
+		: m_nodes(nullptr), m_nodeArray(p_nodeArray), m_endOfArray(false), m_index(0), m_validObject(true)
+	{
+		if(!p_nodeArray)
+		{
+			m_nodeArraySize = 0;
+			m_validObject = false;
+			p_nodeArray = nullptr;
+		}
+		else
+			if(p_nodeArray->size() == 0)
+			{
+				m_nodeArraySize = 0;
+				m_validObject = false;
+				p_nodeArray = nullptr;
+			}
+			else
+				m_nodeArraySize = m_nodeArray->size();
+	}
+
+	inline ConfigFile::NodeBase *findNode(std::string p_name)
+	{
+		if(m_nodes)
+		{
+			for(std::vector<ConfigFile::NodeBase*>::size_type i = 0, size = m_nodes->size(); i < size; i++)
+				if(*(*m_nodes)[i] == p_name)
+					return (*m_nodes)[i];
+		}
+
+		return &m_nullNode;
+	}
+	inline ConfigFile::NodeBase *findNode(int p_mapKey)
+	{
+		if(m_nodes)
+		{
+			for(std::vector<ConfigFile::NodeBase*>::size_type i = 0, size = m_nodes->size(); i < size; i++)
+				if(*(*m_nodes)[i] == p_mapKey)
+					return (*m_nodes)[i];
+		}
+
+		return &m_nullNode;
+	}
+
+	std::vector<ConfigFile::NodeBase*> *m_nodes;
+	std::vector<std::vector<ConfigFile::NodeBase*>*> *m_nodeArray;
+	std::vector<std::vector<ConfigFile::NodeBase*>*>::size_type m_nodeArraySize;
+
+	bool m_endOfArray;
+	bool m_validObject;
+	unsigned int m_index;
+
+	static ConfigFile::NodeBase m_nullNode;
+
+public:
+	~NodeIterator() { }
+
+	inline NodeIterator getNode(std::string p_name)		 { return NodeIterator(findNode(p_name)->getChildren()); }
+	inline NodeIterator getNode(int p_mapKey)			 { return NodeIterator(findNode(p_mapKey)->getChildren()); }
+	inline NodeIterator getNodeArray(std::string p_name) { return NodeIterator(findNode(p_name)->getNodeArray()); }
+	inline NodeIterator getNodeArray(int p_mapKey)		 { return NodeIterator(findNode(p_mapKey)->getNodeArray()); }
+	inline NodeIterator getArrayEntry()					 { return (m_nodeArray) ? NodeIterator((*m_nodeArray)[m_index]) : NodeIterator(); }
+	
+	inline ConfigFile::BaseValue &getValue(std::string p_name) { return findNode(p_name)->getValue(); }
+	inline ConfigFile::BaseValue &getValue(int p_mapKey)		{ return findNode(p_mapKey)->getValue(); }
+
+	explicit inline operator bool() const	{ return m_validObject; }
+	inline void operator++(int)				{ (m_index < m_nodeArraySize) ? m_index++ : m_endOfArray = true; }
+	inline bool endOfArray()				{ return m_endOfArray; }
+};

+ 84 - 0
Praxis3D/Source/DebugMoveScript.h

@@ -0,0 +1,84 @@
+#pragma once
+
+#include "BaseScriptObject.h"
+
+// Provides bare object movement functionality for debugging purposes (like making a light move around the scene)
+class DebugMoveScript : public BaseScriptObject
+{
+	friend class ScriptingScene;
+public:
+	DebugMoveScript(SystemScene *p_systemScene, std::string p_name)
+		: BaseScriptObject(p_systemScene, p_name, Properties::DebugMoveScript)
+	{
+		m_speed = 10.0f;
+		m_radius = 0.0f;
+		m_targetVec = Math::normalize(Math::Vec3f(1.0f, 0.0f, 1.0f));
+		m_rotation = Math::Vec3f(0.0f, 1.0f, 0.0f);
+	}
+	~DebugMoveScript() { }
+
+
+	virtual ErrorCode init()
+	{
+		return ErrorCode::Success;
+	}
+
+	void loadToMemory()
+	{
+
+	}
+
+	// Exports all the data of the object as a PropertySet
+	virtual PropertySet exportObject()
+	{
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::DebugMoveScript);
+		propertySet.addProperty(Properties::Name, m_name);
+		propertySet.addProperty(Properties::Position, m_originPosVec);
+		propertySet.addProperty(Properties::Radius, m_radius);
+		propertySet.addProperty(Properties::Rotation, m_rotation);
+		propertySet.addProperty(Properties::Speed, m_speed);
+
+		return propertySet;
+	}
+
+	virtual void update(const float p_deltaTime)
+	{
+		m_targetVec.rotate(m_speed * p_deltaTime, m_rotation);
+
+		m_positionVec = m_targetVec * m_radius + m_originPosVec;
+
+		postChanges(Systems::Changes::Spacial::Position);
+	}
+
+	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spacial::Position:
+			return m_positionVec;
+			break;
+		}
+
+		return ObservedSubject::getVec3(p_observer, p_changedBits);
+	}
+
+	// Setters
+	inline void setMovementSpeed(float p_speed)				{ m_speed = p_speed;			}
+	inline void setRadius(float p_radius)					{ m_radius = p_radius;			}
+	inline void setOriginPosition(Math::Vec3f p_position)	{ m_originPosVec = p_position;	}
+	inline void setMovementAxis(Math::Vec3f p_axis)			{ m_rotation = p_axis;			}
+
+protected:
+	float	m_speed,
+			m_radius;
+
+	Math::Mat4f m_modelMatrix;
+	Math::Vec3f m_positionVec,
+				m_rotation,
+				m_targetVec,
+				m_originPosVec;
+};

+ 152 - 0
Praxis3D/Source/DebugUIScript.h

@@ -0,0 +1,152 @@
+#pragma once
+
+#include "BaseScriptObject.h"
+#include "ClockLocator.h"
+#include "KeyCommand.h"
+
+// Provides bare user interface functionality, like button presses to toggle features (for debugging)
+class DebugUIScript : public BaseScriptObject
+{
+public:
+	DebugUIScript(SystemScene *p_systemScene, std::string p_name)
+		: BaseScriptObject(p_systemScene, p_name, Properties::DebugUIScript)
+	{
+
+		m_elapsedTime = 0.0f;
+		m_showFPSInterval = 1.0f;
+	}
+	~DebugUIScript() { }
+
+
+	virtual ErrorCode init()
+	{
+		return ErrorCode::Success;
+	}
+
+	void loadToMemory()
+	{
+
+	}
+
+	// Exports all the data of the object as a PropertySet
+	virtual PropertySet exportObject()
+	{
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::DebugUIScript);
+		propertySet.addProperty(Properties::Name, m_name);
+
+		// Add root key-binding property set
+		auto &keyBinds = propertySet.addPropertySet(Properties::Keybindings);
+
+		// Add individual key-bindings
+		keyBinds.addProperty(Properties::DebugCaptureMouseKey, (int)m_mouseCaptureCommand.getFirstBinding());
+		keyBinds.addProperty(Properties::DebugFullscreenKey, (int)m_fullscreenCommand.getFirstBinding());
+		keyBinds.addProperty(Properties::DebugVertSyncKey, (int)m_vertSyncCommand.getFirstBinding());
+		keyBinds.addProperty(Properties::CloseKey, (int)m_closeWindowCommand.getFirstBinding());
+
+		return propertySet;
+	}
+
+	virtual void update(const float p_deltaTime)
+	{
+		if(m_closeWindowCommand.isActivated())
+		{
+			// Mark the state of the engine as not running anymore, and it will be shut down after the current frame
+			Config::m_engineVar.running = false;
+		}
+
+		if(m_fullscreenCommand.isActivated())
+		{
+			// Toggle the fullscreen mode
+			WindowLocator::get().setFullscreen(!Config::windowVar().fullscreen);
+
+			// Deactivate the key command so this piece of code is not triggered again if the key wasn't released
+			m_fullscreenCommand.deactivate();
+		}
+
+		if(m_mouseCaptureCommand.isActivated())
+		{
+			// Toggle the mouse capture mode
+			WindowLocator().get().setMouseCapture(!Config::windowVar().mouse_captured);
+
+			// Deactivate the key command so this piece of code is not triggered again if the key wasn't released
+			m_mouseCaptureCommand.deactivate();
+		}
+
+		if(m_vertSyncCommand.isActivated())
+		{
+			// Toggle the vertical synchronization
+			WindowLocator().get().setVerticalSync(!Config::windowVar().vertical_sync);
+
+			// Deactivate the key command so this piece of code is not triggered again if the key wasn't released
+			m_vertSyncCommand.deactivate();
+		}
+
+		m_elapsedTime += p_deltaTime;
+		if(m_elapsedTime > m_showFPSInterval)
+		{
+			m_elapsedTime -= m_showFPSInterval;
+
+			std::string vsyncString = Config::windowVar().vertical_sync ? "VSYNC: ON" : "VSYNC: OFF";
+
+			WindowLocator::get().setWindowTitle(Config::windowVar().name + " | " + vsyncString + " | FPS: " +
+												Utilities::toString(roundf(ClockLocator::get().getFPS())));
+		}
+	}
+
+	inline void setMouseCaptureKey(Scancode p_key)
+	{
+		m_mouseCaptureCommand.unbindAll();
+		m_mouseCaptureCommand.bind(p_key);
+	}
+	inline void setMouseCaptureKey(std::string &p_string)
+	{
+		m_mouseCaptureCommand.unbindAll();
+		m_mouseCaptureCommand.bind(p_string);
+	}
+
+	inline void setFullscreenKey(Scancode p_key)
+	{
+		m_fullscreenCommand.unbindAll();
+		m_fullscreenCommand.bind(p_key);
+	}
+	inline void setFullscreenKey(std::string &p_string)
+	{
+		m_fullscreenCommand.unbindAll();
+		m_fullscreenCommand.bind(p_string);
+	}
+
+	inline void setVerticalSyncKey(Scancode p_key)
+	{
+		m_vertSyncCommand.unbindAll();
+		m_vertSyncCommand.bind(p_key);
+	}
+	inline void setVerticalSyncKey(std::string &p_string)
+	{
+		m_vertSyncCommand.unbindAll();
+		m_vertSyncCommand.bind(p_string);
+	}
+
+	inline void setCloseWindowKey(Scancode p_key)
+	{
+		m_closeWindowCommand.unbindAll();
+		m_closeWindowCommand.bind(p_key);
+	}
+	inline void setCloseWindowKey(std::string &p_string)
+	{
+		m_closeWindowCommand.unbindAll();
+		m_closeWindowCommand.bind(p_string);
+	}
+	
+protected:
+	KeyCommand	m_fullscreenCommand, 
+				m_mouseCaptureCommand,
+				m_vertSyncCommand,
+				m_closeWindowCommand;
+
+	float	m_elapsedTime,
+			m_showFPSInterval;
+};

+ 422 - 0
Praxis3D/Source/DeferredRenderer.cpp

@@ -0,0 +1,422 @@
+
+#include "DeferredRenderer.h"
+#include "RendererScene.h"
+#include "ShaderUniformUpdater.h"
+#include "WindowLocator.h"
+
+DeferredRenderer::SingleTriangle DeferredRenderer::m_fullscreenTriangle;
+
+DeferredRenderer::DeferredRenderer()
+{
+	m_rendererState = new DeferredRendererState(this);
+	m_currentObjectData = nullptr;
+	m_gbuffer = nullptr;
+	m_boundShaderHandle = 0;
+	m_spotLightBufferHandle = 0;
+	m_pointLightBufferHandle = 0;
+
+	for(int i = 0; i < Model::NumOfModelMaterials; i++)
+		m_boundTextureHandles[i] = 0;
+}
+DeferredRenderer::~DeferredRenderer()
+{
+	delete m_gbuffer;
+}
+
+ErrorCode DeferredRenderer::init()
+{
+	ErrorCode returnCode = ErrorCode::Success;
+
+	// Get the current screen size
+	m_screenSize.x = Config::graphicsVar().current_resolution_x;
+	m_screenSize.y = Config::graphicsVar().current_resolution_y;
+
+	// Initialize gbuffer (and also pass the screen size to be used as the buffer size)
+	m_gbuffer = new GeometryBuffer((unsigned int) m_screenSize.x, (unsigned int) m_screenSize.y);
+
+	// Check if the gbuffer initialization was successful
+	if(ErrHandlerLoc::get().ifSuccessful(m_gbuffer->init(), returnCode))
+	{
+		// Create a property-set used to load geometry shader
+		PropertySet geomShaderProperties(Properties::Shaders);
+		geomShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().geometry_pass_vert_shader);
+		geomShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().geometry_pass_frag_shader);
+
+		// Create a property-set used to load lighting shader
+		PropertySet lightShaderProperties(Properties::Shaders);
+		lightShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().light_pass_vert_shader);
+		lightShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().light_pass_frag_shader);
+
+		// Create shaders
+		m_shaderGeometry = Loaders::shader().load(geomShaderProperties);
+		m_shaderLightPass = Loaders::shader().load(lightShaderProperties);
+
+		// Load geometry shaders
+		m_shaderGeometry->loadToMemory();
+		m_shaderGeometry->loadToVideoMemory();
+
+		// Load lighting shaders
+		m_shaderLightPass->loadToMemory();
+		m_shaderLightPass->loadToVideoMemory();
+
+		// Load fullscreen triangle (used to render post-processing effects)
+		m_fullscreenTriangle.load();
+
+		// Enable / disable face culling
+		if(Config::rendererVar().face_culling)
+			glEnable(GL_CULL_FACE);
+		else
+			glDisable(GL_CULL_FACE);
+
+		// Enable / disable depth test
+		if(Config::rendererVar().depth_test)
+			glEnable(GL_DEPTH_TEST);
+		else
+			glDisable(GL_DEPTH_TEST);
+
+		glDepthFunc(GL_LESS);
+
+		// Set face culling mode
+		glCullFace(Config::rendererVar().face_culling_mode);
+
+		// Set depth test function
+		glDepthFunc(Config::rendererVar().depth_test_func);
+
+		// Declare light buffer's block size variable
+		GLint pointLightBlockSize = 0;
+		GLint spotLightBlockSize = 0;
+
+		m_shaderLightPass->bind();
+
+		// Get point light buffer's uniform block index
+		auto pointLightBlockIndex = glGetUniformBlockIndex(m_shaderLightPass->getShaderHandle(), "PointLights");
+		// Get point light buffer's uniform block size
+		glGetActiveUniformBlockiv(m_shaderLightPass->getShaderHandle(), pointLightBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &pointLightBlockSize);
+		// Bind the uniform buffer at point light binding point
+		glUniformBlockBinding(m_shaderLightPass->getShaderHandle(), pointLightBlockIndex, PointLightBindingPoint);
+
+		// Get spot light buffer's uniform block index
+		auto spotLightBlockIndex = glGetUniformBlockIndex(m_shaderLightPass->getShaderHandle(), Config::shaderVar().spotLightBuffer.c_str());
+		// Get spot light buffer's uniform block size
+		glGetActiveUniformBlockiv(m_shaderLightPass->getShaderHandle(), spotLightBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &spotLightBlockSize);
+		// Bind the uniform buffer at spot light binding point
+		glUniformBlockBinding(m_shaderLightPass->getShaderHandle(), spotLightBlockIndex, SpotLightBindingPoint);
+
+		// Calculate the maximum number of lights supported
+		int maxNumPointLights = pointLightBlockSize / sizeof(PointLightDataSet);
+		int maxNumSpotLights = spotLightBlockSize / sizeof(SpotLightDataSet);
+
+		// Make sure the maximum number of lights does not exceed the supported amount
+		if(Config::rendererVar().max_num_point_lights > maxNumPointLights)
+			Config::m_rendererVar.max_num_point_lights = maxNumPointLights;
+		if(Config::rendererVar().max_num_spot_lights > maxNumSpotLights)
+			Config::m_rendererVar.max_num_spot_lights = maxNumSpotLights;
+
+		// Allocate point light buffer on VRAM and bind it to uniform buffer in the shader
+		glGenBuffers(1, &m_pointLightBufferHandle);
+		glBindBuffer(GL_UNIFORM_BUFFER, m_pointLightBufferHandle);
+		glBufferData(GL_UNIFORM_BUFFER, sizeof(PointLightDataSet) * Config::rendererVar().max_num_point_lights, NULL, GL_DYNAMIC_DRAW);
+		glBindBufferBase(GL_UNIFORM_BUFFER, pointLightBlockIndex, m_pointLightBufferHandle);
+
+		// Allocate spot light buffer on VRAM and bind it to uniform buffer in the shader
+		glGenBuffers(1, &m_spotLightBufferHandle);
+		glBindBuffer(GL_UNIFORM_BUFFER, m_spotLightBufferHandle);
+		glBufferData(GL_UNIFORM_BUFFER, sizeof(SpotLightDataSet) * Config::rendererVar().max_num_spot_lights, NULL, GL_DYNAMIC_DRAW);
+		glBindBufferBase(GL_UNIFORM_BUFFER, spotLightBlockIndex, m_spotLightBufferHandle);
+
+		// Calculate projection matrix
+		updateProjectionMatrix();
+	}
+	
+	return returnCode;
+}
+
+void DeferredRenderer::beginRenderCycle(float p_deltaTime)
+{
+
+}
+void DeferredRenderer::endRenderCycle(float p_deltaTime)
+{
+
+}
+
+void DeferredRenderer::renderFrame(const SceneObjects &p_sceneObjects, const float p_deltaTime)
+{
+	// Load all the objects in the load-to-gpu queue. This needs to be done before any rendering, as objects in this
+	// array might have been also added to objects-to-render arrays, so they need to be loaded first
+	for(decltype(p_sceneObjects.m_objectsToLoad.size()) i = 0, size = p_sceneObjects.m_objectsToLoad.size(); i < size; i++)
+	{
+		p_sceneObjects.m_objectsToLoad[i]->loadToVideoMemory();
+
+		// In case shader failed to load or was not specified, assign a geometry shader
+		if(p_sceneObjects.m_objectsToLoad[i]->m_shader->isDefaultProgram())
+			p_sceneObjects.m_objectsToLoad[i]->m_shader = m_shaderGeometry;
+		else
+		{
+			p_sceneObjects.m_objectsToLoad[i]->m_shader->bind();
+			p_sceneObjects.m_objectsToLoad[i]->m_shader->getUniformUpdater().updateTextureUniforms(*m_rendererState);
+		}
+
+	}
+
+	m_gbuffer->initFrame();
+
+	//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	
+	//m_testVec = Math::Vec4f(1.0, 0.0, 0.0, 0.0);
+
+	m_currentCamera = p_sceneObjects.m_camera;
+
+	update();
+
+	geometryPass(p_sceneObjects, p_deltaTime);
+
+	lightingPass(p_sceneObjects, p_deltaTime);
+
+	postLightingPass(p_sceneObjects, p_deltaTime);
+
+	finalPass();
+}
+
+void DeferredRenderer::geometryPass(const SceneObjects &p_sceneObjects, const float p_deltaTime)
+{
+	// Bind buffers
+	m_gbuffer->initGeometryPass();
+
+	// Enable face culling 
+	//if(Config::rendererVar().face_culling)
+	//	glEnable(GL_CULL_FACE);
+
+	//glDepthMask(GL_TRUE);			// Make sure to turn on depth testing
+	//glEnable(GL_DEPTH_TEST);		// as this is much like a regular forward render pass
+	//glDisable(GL_BLEND);
+
+	//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	glClear(GL_DEPTH_BUFFER_BIT);
+
+	m_shaderGeometry->bind();
+	m_shaderGeometry->getUniformUpdater().updateFrame(*m_rendererState);
+	m_shaderGeometry->getUniformUpdater().updateTextureUniforms(*m_rendererState);
+	m_boundShaderHandle = m_shaderGeometry->getShaderHandle();
+
+	// Iterate over all objects to be rendered with geometry shader
+	for(decltype(p_sceneObjects.m_modelObjects.size()) objIndex = 0, numObjects = p_sceneObjects.m_modelObjects.size(); objIndex < numObjects; objIndex++)
+	{
+		drawModelObject(p_sceneObjects.m_modelObjects[objIndex], m_shaderGeometry);
+	}
+
+	// Iterate over all objects to be rendered with a custom shader
+	for(decltype(p_sceneObjects.m_customShaderObjects.size()) objIndex = 0, numObjects = p_sceneObjects.m_customShaderObjects.size(); objIndex < numObjects; objIndex++)
+	{
+		// If shader handle is not already bound, bind it
+		//if(p_sceneObjects.m_customShaderObjects[objIndex]->m_shader->getShaderHandle() != m_boundShaderHandle)
+		{
+			p_sceneObjects.m_customShaderObjects[objIndex]->m_shader->bind();
+			p_sceneObjects.m_customShaderObjects[objIndex]->m_shader->getUniformUpdater().updateFrame(*m_rendererState);
+
+			m_boundShaderHandle = p_sceneObjects.m_customShaderObjects[objIndex]->m_shader->getShaderHandle();
+		}
+
+		if(p_sceneObjects.m_customShaderObjects[objIndex]->m_shader->isTessellated())
+			drawTessellatedObject(p_sceneObjects.m_customShaderObjects[objIndex], p_sceneObjects.m_customShaderObjects[objIndex]->m_shader);
+		else
+			drawModelObject(p_sceneObjects.m_customShaderObjects[objIndex], p_sceneObjects.m_customShaderObjects[objIndex]->m_shader);
+	}
+
+	// Unbind VBO so it is not changed from outside
+	//glBindVertexArray(0);
+}
+
+void DeferredRenderer::lightingPass(const SceneObjects &p_sceneObjects, const float p_deltaTime)
+{
+	m_gbuffer->initLightPass();
+
+	//glEnable(GL_BLEND);
+	//glBlendFunc(GL_ONE, GL_ONE);
+	glDisable(GL_DEPTH_TEST);
+	//glDisable(GL_CULL_FACE);
+
+	// Bind and update the point light buffer
+	glBindBuffer(GL_UNIFORM_BUFFER, m_pointLightBufferHandle);
+	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PointLightDataSet) * p_sceneObjects.m_pointLights.size(), 
+					p_sceneObjects.m_pointLights.data());
+
+	// Bind and update the spot light buffer
+	glBindBuffer(GL_UNIFORM_BUFFER, m_spotLightBufferHandle);
+	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SpotLightDataSet) * p_sceneObjects.m_spotLights.size(),
+					p_sceneObjects.m_spotLights.data());
+
+	// Update the directional light data set (or clear it if there is no directional light in the scene)
+	if(p_sceneObjects.m_directionalLight != nullptr)
+		m_directionalLight = *p_sceneObjects.m_directionalLight;
+	else
+		m_directionalLight.clear();
+
+	// Update the current number of lights in the light buffers
+	m_numPointLights = p_sceneObjects.m_pointLights.size();
+	m_numSpotLights = p_sceneObjects.m_spotLights.size();
+
+	m_shaderLightPass->bind();
+	m_shaderLightPass->getUniformUpdater().updateTextureUniforms(*m_rendererState);
+	m_shaderLightPass->getUniformUpdater().updateFrame(*m_rendererState);
+
+	m_fullscreenTriangle.bind();
+	m_fullscreenTriangle.render();
+	//glBindVertexArray(0);
+}
+
+void DeferredRenderer::postLightingPass(const SceneObjects & p_sceneObjects, const float p_deltaTime)
+{
+	//glDepthMask(GL_FALSE);
+	//glEnable(GL_DEPTH_TEST);
+	//glDepthFunc(GL_LEQUAL);
+
+	//glDisable(GL_BLEND);
+	//glEnable(GL_BLEND);
+	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	//glDisable(GL_CULL_FACE);
+
+	//glDepthMask(GL_TRUE);			// Make sure to turn on depth testing
+	glEnable(GL_DEPTH_TEST);		// as this is much like a regular forward render pass
+	//glDepthFunc(GL_LESS);
+	//glDisable(GL_BLEND);
+
+	// Iterate over all objects to be rendered with a custom shader
+	for(decltype(p_sceneObjects.m_postLightingObjects.size()) objIndex = 0, numObjects = p_sceneObjects.m_postLightingObjects.size(); objIndex < numObjects; objIndex++)
+	{
+		// If shader handle is not already bound, bind it
+		//if(p_sceneObjects.m_postLightingObjects[objIndex]->m_shader->getShaderHandle() != m_boundShaderHandle)
+		{
+			p_sceneObjects.m_postLightingObjects[objIndex]->m_shader->bind();
+			p_sceneObjects.m_postLightingObjects[objIndex]->m_shader->getUniformUpdater().updateFrame(*m_rendererState);
+			p_sceneObjects.m_postLightingObjects[objIndex]->m_shader->getUniformUpdater().updateTextureUniforms(*m_rendererState);
+
+			m_boundShaderHandle = p_sceneObjects.m_postLightingObjects[objIndex]->m_shader->getShaderHandle();
+		}
+
+		drawModelObject(p_sceneObjects.m_postLightingObjects[objIndex], p_sceneObjects.m_postLightingObjects[objIndex]->m_shader);
+	}
+}
+
+void DeferredRenderer::finalPass()
+{
+	m_gbuffer->initFinalPass();
+	glBlitFramebuffer(0, 0, m_screenSize.x, m_screenSize.y,
+					  0, 0, m_screenSize.x, m_screenSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+}
+
+void DeferredRenderer::update()
+{
+	// If the resolution changed
+	if(m_screenSize.x != Config::graphicsVar().current_resolution_x ||
+	   m_screenSize.y != Config::graphicsVar().current_resolution_y)
+	{
+		// Set the new resolution
+		m_screenSize.x = Config::graphicsVar().current_resolution_x;
+		m_screenSize.y = Config::graphicsVar().current_resolution_y;
+
+		// Reload the geometry buffers with the new size
+		m_gbuffer->setBufferSize((unsigned int)m_screenSize.x, (unsigned int)m_screenSize.y);
+
+		// Update projection matrix and set OpenGL view-port with the new size
+		updateProjectionMatrix();
+		glViewport(0, 0, m_screenSize.x, m_screenSize.y);
+	}
+
+
+	m_viewProjMatrix = m_projMatrix * m_currentCamera->getBaseObjectData().m_modelMat;
+}
+
+void DeferredRenderer::drawModelObject(const RenderableObjectData *p_renderableObject, const ShaderLoader::ShaderProgram *p_shader)
+{
+	// Assign a current base object data
+	m_currentObjectData = &(p_renderableObject->m_baseObjectData);
+	
+	// Calculate matrices
+	m_modelViewMatrix = m_currentCamera->getBaseObjectData().m_modelMat * m_currentObjectData->m_modelMat;
+	m_modelViewProjMatrix = m_projMatrix * m_modelViewMatrix;
+
+	// Update per-model uniforms
+	p_shader->getUniformUpdater().updateModel(*m_rendererState);
+		
+	// Bind model's VAO
+	glBindVertexArray(p_renderableObject->m_model.getHandle());
+
+	// Iterate over all the meshes in the model
+	for(decltype(p_renderableObject->m_model.getNumMeshes()) meshIndex = 0, numMeshes = p_renderableObject->m_model.getNumMeshes(); meshIndex < numMeshes; meshIndex++)
+	{
+		// Update per-mesh uniforms
+		p_shader->getUniformUpdater().updateMesh(*m_rendererState);
+
+		// Iterate over all materials and bind them
+		for(int matType = 0; matType < Model::NumOfModelMaterials; matType++)
+		{
+			// Get texture handle
+			unsigned int textureHandle = p_renderableObject->m_materials[matType][p_renderableObject->m_model[meshIndex].m_materialIndex].getHandle();
+
+			// If texture handle is not already bound, bind it
+			//if(textureHandle != m_boundTextureHandles[matType])
+			//{
+				glActiveTexture(GL_TEXTURE0 + matType);
+				glBindTexture(GL_TEXTURE_2D, textureHandle);
+			//	m_boundTextureHandles[matType] = textureHandle;
+			//}
+		}
+		
+		// Draw the actual geometry
+		glDrawElementsBaseVertex(GL_TRIANGLES, 
+								 p_renderableObject->m_model[meshIndex].m_numIndices, 
+								 GL_UNSIGNED_INT,
+								 (void*)(sizeof(unsigned int) * p_renderableObject->m_model[meshIndex].m_baseIndex), 
+								 p_renderableObject->m_model[meshIndex].m_baseVertex);
+	}
+}
+
+void DeferredRenderer::drawTessellatedObject(const RenderableObjectData *p_renderableObject, const ShaderLoader::ShaderProgram *p_shader)
+{
+	glPatchParameteri(GL_PATCH_VERTICES, 3);
+
+	// Assign a current base object data
+	m_currentObjectData = &(p_renderableObject->m_baseObjectData);
+
+	// Calculate matrices
+	m_modelViewMatrix = m_currentCamera->getBaseObjectData().m_modelMat * m_currentObjectData->m_modelMat;
+	m_modelViewProjMatrix = m_projMatrix * m_modelViewMatrix;
+
+	// Update per-model uniforms
+	p_shader->getUniformUpdater().updateModel(*m_rendererState);
+	//p_shader->getUniformUpdater().updateTextureUniforms(*m_rendererState);
+
+	// Bind model's VAO
+	glBindVertexArray(p_renderableObject->m_model.getHandle());
+
+	// Iterate over all the meshes in the model
+	for(decltype(p_renderableObject->m_model.getNumMeshes()) meshIndex = 0, numMeshes = p_renderableObject->m_model.getNumMeshes(); meshIndex < numMeshes; meshIndex++)
+	{
+		// Update per-mesh uniforms
+		p_shader->getUniformUpdater().updateMesh(*m_rendererState);
+
+		// Iterate over all materials and bind them
+		for(int matType = 0; matType < Model::NumOfModelMaterials; matType++)
+		{
+			// Get texture handle
+			unsigned int textureHandle = p_renderableObject->m_materials[matType][p_renderableObject->m_model[meshIndex].m_materialIndex].getHandle();
+
+			// If texture handle is not already bound, bind it
+			//if(textureHandle != m_boundTextureHandles[matType])
+			{
+				glActiveTexture(GL_TEXTURE0 + matType);
+				glBindTexture(GL_TEXTURE_2D, textureHandle);
+				m_boundTextureHandles[matType] = textureHandle;
+			}
+		}
+
+		// Draw the actual geometry
+		glDrawElementsBaseVertex(GL_PATCHES,
+								 p_renderableObject->m_model[meshIndex].m_numIndices,
+								 GL_UNSIGNED_INT,
+								 (void*)(sizeof(unsigned int) * p_renderableObject->m_model[meshIndex].m_baseIndex),
+								 p_renderableObject->m_model[meshIndex].m_baseVertex);
+	}
+}

+ 134 - 0
Praxis3D/Source/DeferredRenderer.h

@@ -0,0 +1,134 @@
+#pragma once
+
+#include "Config.h"
+#include "Loaders.h"
+#include "GeometryBuffer.h"
+#include "ModelGraphicsObjects.h"
+#include "Renderer.h"
+
+class DeferredRenderer : public Renderer
+{
+	friend class DeferredRendererState;
+public: 
+	DeferredRenderer();
+	virtual ~DeferredRenderer();
+
+	virtual ErrorCode init();
+
+	virtual void beginRenderCycle(const float p_deltaTime);
+	virtual void endRenderCycle(const float p_deltaTime);
+
+	// Renders a complete frame
+	virtual void renderFrame(const SceneObjects &p_sceneObjects, const float p_deltaTime);
+
+protected:
+	// Uniform buffer handles for light buffers
+	enum LightUniformBuffers : unsigned int
+	{
+		PointLightBindingPoint = 0,
+		SpotLightBindingPoint
+	};
+
+	// An empty buffer used to render a single triangle that covers the whole screen
+	// (triangles vertices are calculated inside the shader, hence empty buffer)
+	class SingleTriangle
+	{
+	public:
+		SingleTriangle() : m_vao(0), m_vbo(0) { }
+		~SingleTriangle()
+		{
+			glDeleteBuffers(1, &m_vbo);
+			glDeleteBuffers(1, &m_vao);
+		}
+
+		// Creates an empty buffer, assigns a VAO
+		inline void load()
+		{
+			glGenBuffers(1, &m_vbo);
+			glGenVertexArrays(1, &m_vao);
+			glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
+			glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
+			glBindBuffer(GL_ARRAY_BUFFER, 0);
+		}
+		// Binds VAO and VBO
+		inline void bind() const
+		{
+			glBindVertexArray(m_vao);
+			glBindVertexBuffer(0, m_vbo, 0, 0);
+		}
+		// Issues a draw call
+		inline void render() const { glDrawArrays(GL_TRIANGLES, 0, 3); }
+
+	private:
+		GLuint	m_vao,
+				m_vbo;
+	};
+
+	// Renders meshes and populates the geometry buffers
+	virtual void geometryPass(const SceneObjects &p_sceneObjects, const float p_deltaTime);
+
+	// Calculates lighting in a screen-space pass
+	virtual void lightingPass(const SceneObjects &p_sceneObjects, const float p_deltaTime);
+
+	// Renders the objects that are unaffected by lighting
+	virtual void postLightingPass(const SceneObjects &p_sceneObjects, const float p_deltaTime);
+
+	// Copies the final buffer to the screen by blitting it
+	virtual void finalPass();
+
+	// Updates frame-dependent variables (like view, projection matrices)
+	virtual void update();
+
+	void drawModelObject(const RenderableObjectData *p_renderableObject, const ShaderLoader::ShaderProgram *p_shader);
+	void drawTessellatedObject(const RenderableObjectData *p_renderableObject, const ShaderLoader::ShaderProgram *p_shader);
+
+	// Shaders
+	ShaderLoader::ShaderProgram	*m_shaderGeometry,
+								*m_shaderLightPass;
+
+	// Framebuffers
+	GeometryBuffer *m_gbuffer;
+	
+	// Light buffer handles (on VRAM)
+	unsigned int m_pointLightBufferHandle,
+				 m_spotLightBufferHandle;
+
+	// Current number of lights in the light buffers
+	size_t	m_numPointLights,
+			m_numSpotLights;
+
+	// Currently bound object handles
+	unsigned int m_boundTextureHandles[Model::NumOfModelMaterials],
+				 m_boundShaderHandle;
+	
+	DirectionalLightDataSet m_directionalLight;
+	DeferredRendererState *m_rendererState;
+	static SingleTriangle m_fullscreenTriangle;
+};
+
+class DeferredRendererState : public RendererState
+{
+	friend class DeferredRenderer;
+public:
+	~DeferredRendererState() { }
+
+	const virtual Math::Vec3f getFogColor()				const { return Math::Vec3f(Config::graphicsVar().fog_color_x, Config::graphicsVar().fog_color_y, Config::graphicsVar().fog_color_z); }
+	const virtual float getFogDensity()					const { return Config::graphicsVar().fog_density; }
+
+	const virtual Math::Vec3f &getDirLightColor()		const { return m_deferredRenderer->m_directionalLight.m_color;		}
+	const virtual Math::Vec3f &getDirLightDirection()	const { return m_deferredRenderer->m_directionalLight.m_direction;	}
+	const virtual float getDirLightintensity()			const { return m_deferredRenderer->m_directionalLight.m_intensity;	}
+	const virtual unsigned int getNumPointLights()		const { return (unsigned int)m_deferredRenderer->m_numPointLights;	}
+	const virtual unsigned int getNumSpotLights()		const { return (unsigned int)m_deferredRenderer->m_numSpotLights;	}
+	
+	const virtual unsigned int getBlurBufferPos()		const { return GeometryBuffer::GBufferTextureType::GBufferBlur;		}
+	const virtual unsigned int getDiffuseBufferPos()	const { return GeometryBuffer::GBufferTextureType::GBufferDiffuse;	}
+	const virtual unsigned int getEmissiveBufferPos()	const { return GeometryBuffer::GBufferTextureType::GBufferEmissive; }
+	const virtual unsigned int getNormalBufferPos()		const { return GeometryBuffer::GBufferTextureType::GBufferNormal;	}
+	const virtual unsigned int getPositionBufferPos()	const { return GeometryBuffer::GBufferTextureType::GBufferPosition; }
+	
+protected:
+	DeferredRendererState(DeferredRenderer *p_renderer) : RendererState(p_renderer), m_deferredRenderer(p_renderer) { }
+
+	DeferredRenderer *m_deferredRenderer;
+};

+ 238 - 0
Praxis3D/Source/Engine.cpp

@@ -0,0 +1,238 @@
+
+#include <GL\glew.h>
+#include <sstream>
+
+#include <iostream>
+
+#include "ClockLocator.h"
+#include "Engine.h"
+#include "TaskManagerLocator.h"
+#include "WindowLocator.h"
+
+int Engine::m_instances = 0;
+
+#define ENUMTOSTRING(ENUM) #ENUM
+
+Engine::Engine()
+{
+	m_instances++;
+	m_initialized = false;
+}
+
+Engine::~Engine()
+{
+	// Delete systems
+	delete m_taskManager;
+	delete m_errorHandler;
+	delete m_clock;
+}
+
+// Some of the initialization sequences are order sensitive. Do not change the order of calls.
+ErrorCode Engine::init()
+{
+	// Allow only one instance. If there's more, someone is doing something wrong.
+	if(m_instances > 1)
+	{
+		printf("Error: Attempting to create multiple engine instances.\n");
+		return ErrorCode::Failure;
+	}
+
+	//  ___________________________________
+	// |								   |
+	// |  SERVICE LOCATORS INITIALIZATION  |
+	// |___________________________________|
+	// Initialize all locators before providing them with real instances
+	ErrHandlerLoc::init();
+	ClockLocator::init();
+	TaskManagerLocator::init();
+	WindowLocator::init();
+
+	//  ___________________________________
+	// |								   |
+	// |	ERROR HANDLER INITIALIZATION   |
+	// |___________________________________|
+	// Create and initialize error handler
+	m_errorHandler = new ErrorHandler();
+	ErrorCode errHandlerError = m_errorHandler->init();
+	
+	// If error handler was initialized successfully, pass it to the locator, if not, log an error
+	if(errHandlerError == ErrorCode::Success)
+	{
+		ErrHandlerLoc::provide(m_errorHandler);
+	}
+	else
+		printf("Error: Error handler has failed to initialize. Error code: %i\n", errHandlerError);
+
+	//  ___________________________________
+	// |								   |
+	// |    SET CONFIGURATION VARIABLES    |
+	// |___________________________________|
+	// Initialize configuration variables
+	Config::init();
+	Config::loadFromFile(Config::configFileVar().config_file);
+	
+	//  ___________________________________
+	// |								   |
+	// |	  CLOCK INITIALIZATION		   |
+	// |___________________________________|
+	// Initialize clock. Still continue if there's an error, nothing would move but the engine would still run.
+	// The error handler will be responsible for outputting error and asking user if they want to continue in this case.
+	m_clock = new Clock();
+	ErrorCode clockError = m_clock->init();
+
+	// Check if clock was initialized successfully
+	if(clockError == ErrorCode::Success)
+		ClockLocator::provide(m_clock);
+	else
+		ErrHandlerLoc::get().log(clockError, ErrorSource::Source_Engine);
+
+	//  ___________________________________
+	// |								   |
+	// |	  WINDOW INITIALIZATION		   |
+	// |___________________________________|
+	// Initialize window system and then attempt to spawn a window
+	m_window = new Window();
+	ErrorCode windowError = m_window->init();
+
+	if(windowError == ErrorCode::Success)
+	{
+		windowError = m_window->createWindow();
+
+		// If the window creation failed, we don't want to continue with the engine, so return an error
+		if(windowError != ErrorCode::Success)
+		{
+			ErrHandlerLoc::get().log(windowError, ErrorSource::Source_Engine);
+			return windowError;
+		}
+
+		// Assign window class to the service locator
+		WindowLocator::provide(m_window);
+	}
+	else
+		ErrHandlerLoc::get().log(windowError, ErrorSource::Source_Engine);
+
+	//  ___________________________________
+	// |								   |
+	// |	  GLEW INITIALIZATION		   |
+	// |___________________________________|
+	glewExperimental = GL_TRUE;
+	GLenum glewError = glewInit();
+
+	// It falsely gives error 1280, putting a call here clears the error, so it won't trigger anything
+	glGetError();
+		
+	// If GLEW failed to initialize, return failure, as the engine would not be able to continue
+	if(glewError != GLEW_OK)
+	{
+		// Get the GLEW error before returning
+		std::stringstream stringstreamGlewError;
+		stringstreamGlewError << glewGetErrorString(glewError);
+		ErrHandlerLoc::get().log(Glew_failed, ErrorSource::Source_Engine, stringstreamGlewError.str());
+
+		return ErrorCode::Failure;
+	}
+
+	//  ___________________________________
+	// |								   |
+	// |	TASK MANAGER INITIALIZATION	   |
+	// |___________________________________|
+	// Create new task manager and initialize it
+	m_taskManager = new TaskManager();
+	ErrorCode taskMgrError = m_taskManager->init();
+
+	// If task manager initialized successfully, provide it to the locator, otherwise log an error
+	if(taskMgrError == ErrorCode::Success)
+		TaskManagerLocator::provide(m_taskManager);
+	else
+		ErrHandlerLoc::get().log(taskMgrError, ErrorSource::Source_Engine);
+
+
+	//  ___________________________________
+	// |								   |
+	// |	  LOADERS INITIALIZATION	   |
+	// |___________________________________|
+	// Initialize all global loaders and check for errors
+	ErrorCode loaderError = ErrorCode::Success;
+	// Initialize model loaders
+	loaderError = Loaders::model().init();
+	if(loaderError != ErrorCode::Success)
+		ErrHandlerLoc::get().log(loaderError, ErrorSource::Source_Engine);
+	
+	// Initialize shader loader
+	loaderError = Loaders::shader().init();
+	if(loaderError != ErrorCode::Success)
+		ErrHandlerLoc::get().log(loaderError, ErrorSource::Source_Engine);
+
+	// Initialize texture loader
+	loaderError = Loaders::texture().init();
+	if(loaderError != ErrorCode::Success)
+		ErrHandlerLoc::get().log(loaderError, ErrorSource::Source_Engine);
+	
+	//  ___________________________________
+	// |								   |
+	// |	ENGINE STATE INITIALIZATION	   |
+	// |___________________________________|
+	// Initialize the play state, return failure if it wasn't successful
+	if(m_playstate.init(m_taskManager) == ErrorCode::Success)
+		m_currentState = &m_playstate;
+	else
+		return ErrorCode::Failure;
+	
+	// If this point is reached, all initializations passed, mark the engine as initialized
+	m_initialized = true;
+
+	return ErrorCode::Success;
+}
+
+void Engine::run()
+{
+	// Make sure the engine is initialized before entering the main loop
+	if(!m_initialized)
+		return;
+
+	// Infinite main loop
+	while(true)
+	{
+		// Update the clock
+		m_clock->update();
+
+		// Handle window and input events
+		m_window->handleEvents();
+
+		// If engine is still running
+		if(Config::engineVar().running == true)
+		{
+			// Call update on the current engine state
+			m_currentState->update(*this);
+
+			// Swap buffers. If v-sync is enabled, this call should halt for appropriate time
+			m_window->swapBuffers();
+		}
+		else
+		{
+			// If engine is not running anymore, break the loop
+			break;
+		}
+	}
+
+	// Call shutdown before returning
+	shutdown();
+}
+
+void Engine::shutdown()
+{
+	// Cancel all the tasks in background threads
+	m_taskManager->cancelBackgroundThreads();
+
+	// Make sure they are canceled, by waiting for them
+	m_taskManager->waitForBackgroundThreads();
+
+	// Shutdown engine states
+	//m_playstate.shutdown();
+
+	// Shutdown the task manager
+	m_taskManager->shutdown();
+
+	// Set the initialized flag to false, so the engine is not run without initializing again
+	m_initialized = false;
+}

+ 56 - 0
Praxis3D/Source/Engine.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include "Clock.h"
+#include "Config.h"
+#include "ErrorCodes.h"
+#include "ErrorHandlerLocator.h"
+#include "PlayState.h"
+#include "TaskManager.h"
+#include "Window.h"
+
+// Class containing a complete one instance of the engine.
+class Engine
+{
+public:
+	Engine();
+	~Engine();
+
+	// Initializes required systems and data. Required before entering main loop
+	ErrorCode init();
+
+	// Enters the main loop. Returns only after exiting the engine
+	void run();
+
+private:
+	// Shuts all the systems, etc. down, called before returning from run()
+	void shutdown();
+
+	// Currently being executed state
+	EngineState *m_currentState;
+
+	// Different execution states
+	PlayState m_playstate;
+
+	// Various required systems
+	ErrorHandler *m_errorHandler;
+	Window *m_window;
+	Clock *m_clock;
+
+	// Task manager and scheduler for multi-threading
+	TaskManager *m_taskManager;
+	TaskScheduler *m_scheduler;
+
+	// Universal change controller, stores subject-observer messages between linked objects
+	UniversalScene *m_changeCtrlScene;
+
+	// Scene and object change controllers, distribute changes
+	ChangeController *m_sceneChangeController;
+	ChangeController *m_objectChangeController;
+
+	// Flag to make sure engine is not run without initialization call
+	bool m_initialized;
+
+	// Make sure there is only one instance running
+	static int m_instances;
+};
+

+ 7 - 0
Praxis3D/Source/EngineDefinitions.h

@@ -0,0 +1,7 @@
+
+// Preprocessor definitions as various settings for the engine.
+// Affects engine only when recompiled, to eliminate run-time checks.
+
+// Enable multithreading
+#define SETTING_MULTITHREADING_ENABLED
+#define SETTING_ATOMIC_VARIABLES_ENABLED

+ 19 - 0
Praxis3D/Source/EngineState.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Config.h"
+#include "ErrorCodes.h"
+#include "TaskManager.h"
+
+class Engine;
+
+class EngineState
+{
+public:
+	virtual ~EngineState() { }
+
+	virtual ErrorCode init(TaskManager *p_taskManager) = 0;
+	virtual void update(Engine &p_engine) = 0;
+
+	virtual void shutdown() = 0;
+};
+

+ 34 - 0
Praxis3D/Source/EnumFactory.h

@@ -0,0 +1,34 @@
+#include <string.h>
+
+// expansion macro for enum value definition
+#define ENUM_VALUE(name,assign) name assign,
+
+// expansion macro for enum to string conversion
+#define ENUM_CASE(name,assign) case name: return #name;
+
+// expansion macro for string to enum conversion
+#define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;
+
+/// declare the access function and define enum values
+#define DECLARE_ENUM(EnumType,ENUM_DEF) \
+  enum EnumType { \
+    ENUM_DEF(ENUM_VALUE) \
+    }; \
+  const char *GetString(EnumType dummy); \
+  EnumType Get##EnumType##Value(const char *string); \
+
+/// define the access function names
+#define DEFINE_ENUM(EnumType,ENUM_DEF) \
+  const char *GetString(EnumType value) \
+    { \
+    switch(value) \
+	    { \
+      ENUM_DEF(ENUM_CASE) \
+      default: return ""; /* handle input error */ \
+	    } \
+    } \
+  EnumType Get##EnumType##Value(const char *str) \
+    { \
+    ENUM_DEF(ENUM_STRCMP) \
+    return (EnumType)0; /* handle input error */ \
+    }

+ 121 - 0
Praxis3D/Source/EnvironmentMapObjects.h

@@ -0,0 +1,121 @@
+#pragma once
+
+#include "Loaders.h"
+#include "System.h"
+#include "TextureLoader.h"
+
+class EnvironmentMapStatic : public SystemObject
+{
+public:
+	EnvironmentMapStatic(SystemScene *p_systemScene, const std::string &p_name, TextureLoaderCubemap::TextureCubemapHandle &p_cubemap, Properties::PropertyID p_objectType = Properties::EnvironmentMapStatic)
+		: SystemObject(p_systemScene, p_name, p_objectType), m_cubemap(p_cubemap) { }
+	virtual ~EnvironmentMapStatic() 
+	{
+		m_cubemapHandle = 0;
+	}
+
+	ErrorCode init()
+	{
+		return ErrorCode::Success;
+	}
+
+	// Loads cubemap from files to memory
+	void loadToMemory()
+	{
+		m_cubemap.loadToMemory();
+	}
+
+	// Loads cubemap to video memory; should only be called by renderer thread
+	ErrorCode loadToVideoMemory()
+	{
+		ErrorCode returnError;
+
+		returnError = m_cubemap.loadToVideoMemory();
+
+		//if(returnError == ErrorCode::Success)
+		//	m_cubemapHandle = m_cubemap.getHandle();
+
+		return returnError;
+	}
+
+	// Exports all the data of the object as a PropertySet
+	PropertySet exportObject()
+	{
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::EnvironmentMapStatic);
+		propertySet.addProperty(Properties::Position, m_position);
+
+		// Iterate over each cubemap face and add its material filename
+		for(unsigned int face = CubemapFace_PositiveX; face < CubemapFace_NumOfFaces; face++)
+		{
+			auto &materialEntry = propertySet.addPropertySet(Properties::ArrayEntry);
+			materialEntry.addProperty(Properties::Filename, m_cubemap.getFaceFilename(face));
+		}
+
+		return propertySet;
+	}
+
+	void update(const float p_deltaTime)
+	{
+
+	}
+
+	// Setters
+	inline void setCubemap(const TextureLoaderCubemap::TextureCubemapHandle &p_cubemap) { m_cubemap = p_cubemap; }
+	inline void setPosition(const Math::Vec3f &p_position) { m_position = p_position; }
+
+	// Getters
+	const inline unsigned int getCubemapHandle() const { return m_cubemap.getHandle(); }
+
+	BitMask getSystemType() { return Systems::Graphics; }
+
+	virtual BitMask getDesiredSystemChanges() { return Systems::Changes::Spacial::All; }
+	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::None; }
+
+	// Processes any spacial changes
+	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+	{
+		if(p_changeType & Systems::Changes::Spacial::Position)
+		{
+		}
+
+		if(p_changeType & Systems::Changes::Spacial::Rotation)
+		{
+		}
+
+		if(p_changeType & Systems::Changes::Spacial::Scale)
+		{
+		}
+	}
+
+	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spacial::Position:
+			return m_position;
+			break;
+		}
+
+		return ObservedSubject::getVec3(p_observer, p_changedBits);
+	}
+
+	const virtual bool getBool(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		//switch(p_changedBits)
+		//{
+		//}
+
+		return ObservedSubject::getBool(p_observer, p_changedBits);
+	}
+
+protected:
+	unsigned int m_cubemapHandle;
+
+	Math::Vec3f m_position;
+
+	TextureLoaderCubemap::TextureCubemapHandle m_cubemap;
+};

+ 7 - 0
Praxis3D/Source/ErrorCodes.cpp

@@ -0,0 +1,7 @@
+#include "ErrorCodes.h"
+
+DEFINE_ENUM(ErrorType, ERROR_TYPES)
+
+DEFINE_ENUM(ErrorCode, ERROR_CODES)
+
+DEFINE_ENUM(ErrorSource, ERROR_SOURCE)

+ 94 - 0
Praxis3D/Source/ErrorCodes.h

@@ -0,0 +1,94 @@
+#pragma once
+
+#include "EnumFactory.h"
+
+/*   ___________________________________________________________________________________________________
+	|										|					 |										|
+	|										|	 ERROR TYPES:	 |										|
+	|										|____________________|										|
+	|																									|
+	| Info		 - not actually an error, conveys a piece of information.								|
+	|				User should not be notified unless in debugging.									|
+	|---------------------------------------------------------------------------------------------------|
+	| Warning	 - low priority error, silent and easily coped with.									|
+	|				User should not be notified unless in debugging.									|
+	|---------------------------------------------------------------------------------------------------|
+	| Error		 - high priority error, can only be coped with by removing some functionality.			|
+	|				User should be notified or asked if to continue by an error window.					|
+	|---------------------------------------------------------------------------------------------------|
+	| FatalError - top priority error, cannot be dealt with, thus the whole engine must be shut down.	|
+	|				User should be notified with an error code.											|
+	|___________________________________________________________________________________________________|
+*/
+
+#define ERROR_TYPES(Code) \
+    Code(Info,) \
+    Code(Warning,) \
+    Code(Error,) \
+	Code(FatalError, ) \
+	Code(NumberOfErrorTypes, )
+DECLARE_ENUM(ErrorType, ERROR_TYPES)
+
+#define ERROR_CODES(Code) \
+	/* General errors */ \
+    Code(Undefined,) \
+    Code(Success,) \
+    Code(Failure,) \
+	/* General engine errors */\
+    Code(Destroy_obj_not_found,) \
+    Code(Glew_failed,) \
+    Code(Ifstream_failed,) \
+	/* Config Loader */\
+	/* Clock errors */ \
+	Code(Clock_QueryFrequency,) \
+	/* Frame-buffer errors */ \
+	Code(Framebuffer_failed,) \
+	Code(Geometrybuffer_failed,) \
+	/* Model loader errors */ \
+	Code(AssimpScene_failed,) \
+	/* Object pool errors */ \
+	Code(ObjectPool_full,) \
+	/* Property loader errors */ \
+	Code(Property_no_filename,) \
+	/* Shader loader errors */ \
+	Code(Shader_attach_failed,) \
+	Code(Shader_compile_failed,) \
+	Code(Shader_creation_failed,) \
+	Code(Shader_link_failed,) \
+	Code(Shader_loading_failed,) \
+	/* Texture loader errors */ \
+	Code(Texture_not_found,) \
+	Code(Texture_empty,) \
+	/* Window errors */ \
+	Code(Invalid_num_vid_displays,) \
+	Code(SDL_video_init_failed,) \
+	Code(SDL_vsync_failed,) \
+	Code(Window_creation_failed,) \
+	Code(NumberOfErrorCodes,) \
+	Code(CachedError,)
+DECLARE_ENUM(ErrorCode, ERROR_CODES)
+
+#define ERROR_SOURCE(Code) \
+    Code(Source_Unknown,) \
+    Code(Source_General,) \
+    Code(Source_Engine,) \
+    Code(Source_Renderer,) \
+    Code(Source_Scripting,) \
+    Code(Source_Config,) \
+    Code(Source_ConfigLoader,) \
+    Code(Source_TextureLoader,) \
+    Code(Source_ModelLoader,) \
+    Code(Source_ShaderLoader,) \
+    Code(Source_FileLoader,) \
+    Code(Source_SceneLoader,) \
+    Code(Source_GeometryBuffer,) \
+    Code(Source_GraphicsObject,) \
+    Code(Source_ScriptObject,) \
+    Code(Source_PlayerObject,) \
+    Code(Source_GameObject,) \
+    Code(Source_SkyObject,) \
+    Code(Source_LightObject,) \
+    Code(Source_PropertyLoader,) \
+    Code(Source_Window,) \
+    Code(Source_NumberOfErrorSources,) 
+DECLARE_ENUM(ErrorSource, ERROR_SOURCE)

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