Browse Source

Started reimplementing all game objects, scenes and maps to be based on an entity component system.
Added the main GameObject class, that will tie all the components together.
Added model, shader and camera components to render objects.
Added an object register, that provides unique IDs and keeps a list of all objects.
Added a WorldScene system, that will be responsible for loading / creating objects.
Added matrix transform function and fixed an error in matrix rotation calculation in Math.
Added a combine function in PropertySet.
Redid the bitmasks of types of "Changes".

Paul A 4 years ago
parent
commit
2eea6808bf
60 changed files with 7994 additions and 411 deletions
  1. 1 0
      .gitignore
  2. 1439 0
      Praxis3D/Data/Maps/SampleScene WITH ASSETS.pmap
  3. 1439 0
      Praxis3D/Data/Maps/SampleScene.pmap
  4. 1570 0
      Praxis3D/Data/Maps/SampleScene2.pmap
  5. 438 0
      Praxis3D/Data/Maps/componentTest.pmap
  6. 270 0
      Praxis3D/Data/Maps/converter-test.pmap
  7. 6 6
      Praxis3D/Data/config.ini
  8. 47 44
      Praxis3D/Data/error-strings-eng.data
  9. 19 2
      Praxis3D/Praxis3D.vcxproj
  10. 63 4
      Praxis3D/Praxis3D.vcxproj.filters
  11. 6 0
      Praxis3D/Source/BaseGraphicsComponent.cpp
  12. 35 0
      Praxis3D/Source/BaseGraphicsComponent.h
  13. 17 17
      Praxis3D/Source/BaseGraphicsObjects.h
  14. 1 1
      Praxis3D/Source/BaseScriptObject.h
  15. 24 0
      Praxis3D/Source/CameraComponent.h
  16. 6 6
      Praxis3D/Source/CameraScript.h
  17. 2 2
      Praxis3D/Source/ChangeController.cpp
  18. 1 1
      Praxis3D/Source/Clock.h
  19. 2 1
      Praxis3D/Source/Config.cpp
  20. 97 31
      Praxis3D/Source/Config.h
  21. 35 0
      Praxis3D/Source/Containers.h
  22. 2 2
      Praxis3D/Source/DebugMoveScript.h
  23. 5 0
      Praxis3D/Source/ErrorCodes.h
  24. 4 28
      Praxis3D/Source/ErrorHandler.cpp
  25. 247 0
      Praxis3D/Source/GameObject.h
  26. 58 0
      Praxis3D/Source/GraphicsDataSets.h
  27. 395 0
      Praxis3D/Source/GraphicsObject.h
  28. 3 2
      Praxis3D/Source/LenseFlarePass.h
  29. 18 0
      Praxis3D/Source/LightComponent.cpp
  30. 315 0
      Praxis3D/Source/LightComponent.h
  31. 7 9
      Praxis3D/Source/LoaderBase.h
  32. 26 15
      Praxis3D/Source/Math.cpp
  33. 31 9
      Praxis3D/Source/Math.h
  34. 212 0
      Praxis3D/Source/ModelComponent.h
  35. 0 19
      Praxis3D/Source/ModelGraphicsObjects.h
  36. 3 3
      Praxis3D/Source/ModelLoader.cpp
  37. 6 19
      Praxis3D/Source/ModelLoader.h
  38. 6 0
      Praxis3D/Source/ObjectDirectory.cpp
  39. 78 20
      Praxis3D/Source/ObjectDirectory.h
  40. 2 2
      Praxis3D/Source/ObjectPool.h
  41. 353 0
      Praxis3D/Source/ObjectRegister.h
  42. 9 8
      Praxis3D/Source/ObserverBase.cpp
  43. 10 7
      Praxis3D/Source/ObserverBase.h
  44. 24 0
      Praxis3D/Source/PropertySet.h
  45. 2 2
      Praxis3D/Source/RendererFrontend.cpp
  46. 245 6
      Praxis3D/Source/RendererScene.cpp
  47. 6 1
      Praxis3D/Source/RendererScene.h
  48. 2 2
      Praxis3D/Source/SceneLoader.cpp
  49. 54 0
      Praxis3D/Source/ShaderComponent.h
  50. 0 1
      Praxis3D/Source/ShaderLoader.cpp
  51. 9 4
      Praxis3D/Source/System.cpp
  52. 21 8
      Praxis3D/Source/System.h
  53. 45 101
      Praxis3D/Source/TextureLoader.h
  54. 5 20
      Praxis3D/Source/Universal.cpp
  55. 5 6
      Praxis3D/Source/Universal.h
  56. 16 2
      Praxis3D/Source/Utilities.h
  57. 134 0
      Praxis3D/Source/WorldScene.cpp
  58. 77 0
      Praxis3D/Source/WorldScene.h
  59. 19 0
      Praxis3D/Source/WorldTask.cpp
  60. 22 0
      Praxis3D/Source/WorldTask.h

+ 1 - 0
.gitignore

@@ -20,3 +20,4 @@ VC/
 *.idb
 *.res
 *.recipe
+*.tiff

+ 1439 - 0
Praxis3D/Data/Maps/SampleScene WITH ASSETS.pmap

@@ -0,0 +1,1439 @@
+{
+    "LoadInBackground": "0",
+    "ObjectLinks": [
+        {
+            "Observer": "Camera 1",
+            "Subject": "Free Camera 1"
+        }
+    ],
+    "Systems": {
+        "Graphics": {
+            "Objects": [
+                {
+                    "Color": "1.0f, 1.0f, 1.0f",
+                    "Direction": "0.706434f, 0.477714f, 0.0308436f",
+                    "Intensity": "1.0f",
+                    "Name": "Directional Light",
+                    "Type": "DirectionalLight"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Drywall_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "DrywallPainted_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.fbx"
+                    },
+                    "Name": "drywall_panel",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Ground_Albedo.tif",
+                                "Index": "2"
+                            },
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Ground_Normal.tif",
+                                "Index": "2"
+                            },
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "2"
+                            },
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "3"
+                            },
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "4"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.fbx"
+                    },
+                    "Name": "ground",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "OBS_Albedo.tif",
+                                "Index": "5"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "OBS_Normal.tif",
+                                "Index": "5"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "OSB_Panel_RMHAO.tiff",
+                                "Index": "5"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.fbx"
+                    },
+                    "Name": "OSB_Panel",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, -0.1f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_frame_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.fbx"
+                    },
+                    "Name": "stud_frame",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.05f, 1.22f, 1.78f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_pile_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.fbx"
+                    },
+                    "Name": "stud_pile",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "Attenuation": "0.0f, 0.0f, 0.0f",
+                    "Color": "1.0f, 1.0f, 1.0f",
+                    "CutoffAngle": "1.0f",
+                    "Direction": "0.0f, 0.707107f, 0.0f",
+                    "Intensity": "1.0f",
+                    "Name": "Spot Light",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.061f, 1.467f, 0.0f",
+                    "Type": "SpotLight"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Jigsaw_Albedo.tif",
+                                "Index": "6"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Jigsaw_Normal.tif",
+                                "Index": "6"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "power_jigsaw_RMHAO.tiff",
+                                "Index": "6"
+                            },
+                            {
+                                "Filename": "power_jigsaw_RMHAO.tiff",
+                                "Index": "4"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Jigsaw.fbx"
+                    },
+                    "Name": "power_jigsaw",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.131418f, 0.3802f, -0.342975f",
+                    "Rotation": "0.0f, -0.996113f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Hammer_Albedo.tif",
+                                "Index": "7"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Hammer_Normal.tif",
+                                "Index": "7"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "hammer_RMHAO.tiff",
+                                "Index": "7"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Hammer.fbx"
+                    },
+                    "Name": "hammer",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.1759f, 0.0161f, 0.0092f",
+                    "Rotation": "0.569456f, 0.415037f, -0.41919f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "SafetyHat_Albedo.tif",
+                                "Index": "8"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "SafetyHat_Normal.tif",
+                                "Index": "8"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "safety_hat_RMHAO.tiff",
+                                "Index": "8"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "SafetyHat.fbx"
+                    },
+                    "Name": "safety_hat",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.113042f, 0.535803f, 0.0585274f",
+                    "Rotation": "0.119232f, -0.264276f, -0.100705f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "10"
+                            },
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "11"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "10"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "11"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "level_RMHAO.tiff",
+                                "Index": "10"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "MagneticLevel.fbx"
+                    },
+                    "Name": "level",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0696f, 0.00170001f, -0.0012f",
+                    "Rotation": "0.0f, 0.670158f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "15"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "Elastic_Normal.tif",
+                                "Index": "15"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "safety_goggles_RMHAO.tiff",
+                                "Index": "15"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "SafetyGoggles.fbx"
+                    },
+                    "Name": "safety_goggles",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.055f, 0.3786f, 0.249f",
+                    "Rotation": "0.0f, 0.148268f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paint1G_Albedo.tif",
+                                "Index": "16"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paint1G_Normal.tif",
+                                "Index": "16"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_1G_bucket_RMHAO.tiff",
+                                "Index": "16"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "PaintBucket.fbx"
+                    },
+                    "Name": "paint_1G_bucket",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.308f, 0.0f, -0.085f",
+                    "Rotation": "0.0f, -0.981006f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paint1G_Albedo.tif",
+                                "Index": "16"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paint1G_Normal.tif",
+                                "Index": "16"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_1G_lid_RMHAO.tiff",
+                                "Index": "16"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "PaintBucket.fbx"
+                    },
+                    "Name": "paint_1G_lid",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.088f, 0.0024f, -0.296f",
+                    "Rotation": "-1.0f, -1.87254e-07f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "17"
+                            },
+                            {
+                                "Filename": "Paint5G_AlbedoSmoothness.tif",
+                                "Index": "18"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "17"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_5G_bucket_RMHAO.tiff",
+                                "Index": "4"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "PaintBucket.fbx"
+                    },
+                    "Name": "paint_5G_bucket",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.379319f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paintbrush_Albedo.tif",
+                                "Index": "19"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paintbrush_Normal.tif",
+                                "Index": "19"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "brush_RMHAO.tiff",
+                                "Index": "19"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Brush.fbx"
+                    },
+                    "Name": "brush",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.0669f, 0.0101f, -0.2977f",
+                    "Rotation": "0.448508f, -0.543519f, -0.560751f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "20"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Legs_RMHAO.tiff",
+                                "Index": "20"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight.fbx"
+                    },
+                    "Name": "Legs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight.fbx"
+                    },
+                    "Name": "Light_Bulbs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.0163461f, 1.47104f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "20"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Light_Heads_RMHAO.tiff",
+                                "Index": "20"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight.fbx"
+                    },
+                    "Name": "Light_Heads",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "20"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Legs_Low_RMHAO.tiff",
+                                "Index": "20"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight_Low.fbx"
+                    },
+                    "Name": "Legs_Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.122491f, 0.630871f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight_Low.fbx"
+                    },
+                    "Name": "Light_Bulbs_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.0163461f, 1.47118f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "20"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Light_Heads_Low_RMHAO.tiff",
+                                "Index": "20"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "ConstructionLight_Low.fbx"
+                    },
+                    "Name": "Light_Heads_Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.0580053f, 1.46265f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_bottom_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench.fbx"
+                    },
+                    "Name": "bench_bottom",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.01f, -0.029f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_legs_RMHAO.tiff",
+                                "Index": "4"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench.fbx"
+                    },
+                    "Name": "bench_legs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, -0.0122171f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Albedo.tif",
+                                "Index": "23"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Normal.tif",
+                                "Index": "23"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_top_RMHAO.tiff",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "bench_top_RMHAO.tiff",
+                                "Index": "23"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench.fbx"
+                    },
+                    "Name": "bench_top",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.35f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "11"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "11"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "supports_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench.fbx"
+                    },
+                    "Name": "supports",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-4.88281e-06f, 0.185861f, -6.34575e-05f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "upper_covers_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench.fbx"
+                    },
+                    "Name": "upper_covers",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.150578f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_bottom_low_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench_Low.fbx"
+                    },
+                    "Name": "bench_bottom_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, -0.029f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "9"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "4"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_legs_low_RMHAO.tiff",
+                                "Index": "4"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench_Low.fbx"
+                    },
+                    "Name": "bench_legs_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, -0.0122171f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Albedo.tif",
+                                "Index": "23"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "13"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Normal.tif",
+                                "Index": "23"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_top_low_RMHAO.tiff",
+                                "Index": "22"
+                            },
+                            {
+                                "Filename": "bench_top_low_RMHAO.tiff",
+                                "Index": "23"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench_Low.fbx"
+                    },
+                    "Name": "bench_top_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.35f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "11"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "11"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "supports_low_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench_Low.fbx"
+                    },
+                    "Name": "supports_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "22"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "upper_covers_low_RMHAO.tiff",
+                                "Index": "22"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Workbench_Low.fbx"
+                    },
+                    "Name": "upper_covers_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.150578f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Stud.fbx"
+                    },
+                    "Name": "stud",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.134f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 0.81247f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Stud.fbx"
+                    },
+                    "Name": "stud_short",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.79f, -0.82f, 0.0f",
+                    "Rotation": "-0.162427f, -0.688199f, 0.162427f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Stud.fbx"
+                    },
+                    "Name": "stud_short",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.757f, -0.71f, 0.153f",
+                    "Rotation": "-0.502074f, -0.558014f, 0.445038f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Stud.fbx"
+                    },
+                    "Name": "stud_short",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.79f, -1.062f, -0.038f",
+                    "Rotation": "0.66832f, -0.230971f, -0.668321f",
+                    "Scale": "1.0f, 1f, 1f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "Name": "Camera 1",
+                    "Type": "Camera"
+                }
+            ],
+            "Scene": {
+                "ModelPoolSize": "40",
+                "PointLightPoolSize": "24",
+                "ShaderPoolSize": "10",
+                "SpotLightPoolSize": "24"
+            }
+        },
+        "Scripting": {
+            "Objects": [
+                {
+                    "Angle": "26.727f, -0.14598f",
+                    "Keybindings": {
+                        "BackwardKey": "22",
+                        "ForwardKey": "26",
+                        "LeftStrafeKey": "4",
+                        "RightStrafeKey": "7",
+                        "SprintKey": "225"
+                    },
+                    "LowerLimit": "0.0f",
+                    "Name": "Free Camera 1",
+                    "Position": "-90.6949f, 7.47164f, 11.0953f",
+                    "Speed": "2.0f",
+                    "SprintSpeed": "500.0f",
+                    "Type": "FreeCamera",
+                    "UpperLimit": "500000.0f"
+                },
+                {
+                    "Keybindings": {
+                        "CloseKey": "41",
+                        "DebugCaptureMouseKey": "67",
+                        "DebugFullscreenKey": "66",
+                        "LeftStraDebugVertSyncKeyfeKey": "68"
+                    },
+                    "Name": "Debug UI Script 1",
+                    "Type": "DebugUIScript"
+                }
+            ],
+            "Scene": ""
+        }
+    }
+}

+ 1439 - 0
Praxis3D/Data/Maps/SampleScene.pmap

@@ -0,0 +1,1439 @@
+{
+    "LoadInBackground": "0",
+    "ObjectLinks": [
+        {
+            "Observer": "Camera 1",
+            "Subject": "Free Camera 1"
+        }
+    ],
+    "Systems": {
+        "Graphics": {
+            "Objects": [
+                {
+                    "Color": "1.0f, 1.0f, 1.0f",
+                    "Direction": "0.706434f, 0.477714f, 0.0308436f",
+                    "Intensity": "10.0f",
+                    "Name": "Directional Light",
+                    "Type": "DirectionalLight"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Drywall_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "DrywallPainted_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "drywall_panel.obj"
+                    },
+                    "Name": "Drywall Panel",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.00000f, 0.00000f, -0.00000f",
+                    "Rotation": "0.00000f, 90.0000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Ground_Albedo.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Ground_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "ground_RMHAO.tiff",
+                                "Index": "2"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "ground.obj"
+                    },
+                    "Name": "Ground",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.00000f, 0.00000f, -0.00000f",
+                    "Rotation": "0.00000f, 90.0000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "OBS_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "OBS_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "OSB_Panel_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "OSB_Panel.obj"
+                    },
+                    "Name": "OSB Panel",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "-0.100000f, 0.00000f, -1.19209e-08f",
+                    "Rotation": "0.00000f, 90.0000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_frame_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud_frame.obj"
+                    },
+                    "Name": "Stud Frame",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.78000f, 1.22000f, 0.0500002f",
+                    "Rotation": "0.00000f, 90.0000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_pile_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud_pile.obj"
+                    },
+                    "Name": "Stud Pile",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.00000f, 0.00000f, -0.00000f",
+                    "Rotation": "0.00000f, 90.0000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "Attenuation": "0.0f, 0.0f, 0.0f",
+                    "Color": "1.0f, 1.0f, 1.0f",
+                    "CutoffAngle": "1.0f",
+                    "Direction": "0.00000f, 334.000f, 0.00000f",
+                    "Intensity": "1.0f",
+                    "Name": "Spot Light",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.996748f, 1.16436f, -0.270000f",
+                    "Type": "SpotLight"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Jigsaw_Albedo.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Jigsaw_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "power_jigsaw_RMHAO.tiff",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "power_jigsaw_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "power_jigsaw.obj"
+                    },
+                    "Name": "Jigsaw",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.54191f, 0.760200f, -1.38648f",
+                    "Rotation": "0.00000f, -38.7972f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Hammer_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Hammer_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "hammer_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "hammer.obj"
+                    },
+                    "Name": "Hammer",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.83656f, 0.396100f, -1.18851f",
+                    "Rotation": "90.0000f, -156.224f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "SafetyHat_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "SafetyHat_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "safety_hat_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "safety_hat.obj"
+                    },
+                    "Name": "Safety Hat",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.83241f, 0.915803f, -1.10872f",
+                    "Rotation": "10.0046f, 98.7386f, -14.9895f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "level_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "level.obj"
+                    },
+                    "Name": "Magnetic Level",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.66735f, 0.381700f, -1.01034f",
+                    "Rotation": "-0.00000f, -144.746f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Elastic_Normal.tif",
+                                "Index": "3"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "safety_goggles_RMHAO.tiff",
+                                "Index": "3"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "safety_goggles.obj"
+                    },
+                    "Name": "Safety Goggles",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.86550f, 0.758600f, -0.856879f",
+                    "Rotation": "0.00000f, 148.149f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paint1G_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paint1G_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_1G_bucket_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "paint_1G_bucket.obj"
+                    },
+                    "Name": "Paint 1G Bucket",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.818923f, 0.00000f, 0.703241f",
+                    "Rotation": "0.00000f, 30.7243f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paint1G_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paint1G_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_1G_lid_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "paint_1G_lid.obj"
+                    },
+                    "Name": "Paint 1G Lid",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.855533f, 0.00240001f, 0.400618f",
+                    "Rotation": "-9.24281e-14f, 53.0941f, 180.000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Paint5G_AlbedoSmoothness.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "paint_5G_bucket_RMHAO.tiff",
+                                "Index": "2"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "paint_5G_bucket.obj"
+                    },
+                    "Name": "Paint 5G Bucket",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.566000f, 0.00000f, 0.508000f",
+                    "Rotation": "-0.00000f, -82.3230f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Paintbrush_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Paintbrush_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "brush_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "brush.obj"
+                    },
+                    "Name": "Brush",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.844221f, 0.0101000f, 0.382725f",
+                    "Rotation": "-90.0000f, -23.4908f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Legs_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Legs.obj"
+                    },
+                    "Name": "Legs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.27333f, 0.00000f, 2.59100f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "Light_Bulbs.obj"
+                    },
+                    "Name": "Light_Bulbs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.28049f, 1.47104f, 2.60569f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Light_Heads_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Light_Heads.obj"
+                    },
+                    "Name": "Light_Heads",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.27333f, 0.00000f, 2.59100f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Legs_Low_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Legs_Low.obj"
+                    },
+                    "Name": "Legs_Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.32702f, 0.630871f, 2.70109f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "Light_Bulbs_low.obj"
+                    },
+                    "Name": "Light_Bulbs_low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.28049f, 1.47118f, 2.60569f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "ConstructionLight_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [],
+                        "RMHAO": [
+                            {
+                                "Filename": "Light_Heads_Low_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "Light_Heads_Low.obj"
+                    },
+                    "Name": "Light_Heads_Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.29876f, 1.46265f, 2.64314f",
+                    "Rotation": "-0.00000f, -116.000f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_bottom_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_bottom.obj"
+                    },
+                    "Name": "Bench Bottom",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.72057f, 0.351000f, -1.06954f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_legs_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_legs.obj"
+                    },
+                    "Name": "Bench Legs",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.367783f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Albedo.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Normal.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_top_RMHAO.tiff",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "bench_top_RMHAO.tiff",
+                                "Index": "2"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_top.obj"
+                    },
+                    "Name": "Bench Top",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.730000f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "supports_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "supports.obj"
+                    },
+                    "Name": "Supports",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71396f, 0.565861f, -1.06204f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "upper_covers_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "upper_covers.obj"
+                    },
+                    "Name": "Upper Covers",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.530578f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_bottom_low_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_bottom_low.obj"
+                    },
+                    "Name": "Bench Bottom Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.351000f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Metal_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_legs_low_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_legs_low.obj"
+                    },
+                    "Name": "Bench Legs Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.367783f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Albedo.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "PlasticRidges_Normal.tif",
+                                "Index": "2"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "bench_top_low_RMHAO.tiff",
+                                "Index": "1"
+                            },
+                            {
+                                "Filename": "bench_top_low_RMHAO.tiff",
+                                "Index": "2"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "bench_top_low.obj"
+                    },
+                    "Name": "Bench Top Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.730000f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plastic_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "supports_low_RMHAO.tiff",
+                                "Index": "1"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "supports_low.obj"
+                    },
+                    "Name": "Supports Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.380000f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "PlasticRough_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plastic_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "upper_covers_low_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "upper_covers_low.obj"
+                    },
+                    "Name": "Upper Covers Low",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.71400f, 0.530578f, -1.06200f",
+                    "Rotation": "0.00000f, 131.096f, 0.00000f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud.obj"
+                    },
+                    "Name": "Stud",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "2.67384f, 0.813000f, -0.819701f",
+                    "Rotation": "0.0f, 221.0957f, 90.0000f",
+                    "Scale": "1.00000f, 0.812470f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud_short.obj"
+                    },
+                    "Name": "Stud Short 0",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.95489f, 0.0230000f, -1.44678f",
+                    "Rotation": "90.0f, 104.5361f, 0.0f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud_short.obj"
+                    },
+                    "Name": "Stud Short 1",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.93722f, 0.0560000f, -1.25918f",
+                    "Rotation": "82.69953f, 38.25293f, 351.4568f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Plywood_Albedo.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "Plywood_Normal.tif",
+                                "Index": "0"
+                            }
+                        ],
+                        "RMHAO": [
+                            {
+                                "Filename": "stud_short_RMHAO.tiff",
+                                "Index": "0"
+                            }
+                        ]
+                    },
+                    "Models": {
+                        "Filename": "stud_short.obj"
+                    },
+                    "Name": "Stud Short 2",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "1.79750f, 0.0230000f, -1.63449f",
+                    "Rotation": "90.0f, 272.9655f, 0.0f",
+                    "Scale": "1.00000f, 1.00000f, 1.00000f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+                {
+                    "Name": "Camera 1",
+                    "Type": "Camera"
+                }
+            ],
+            "Scene": {
+                "ModelPoolSize": "40",
+                "PointLightPoolSize": "24",
+                "ShaderPoolSize": "10",
+                "SpotLightPoolSize": "24"
+            }
+        },
+        "Scripting": {
+            "Objects": [
+                {
+                    "Angle": "-0.5f, -0.5f",
+                    "Keybindings": {
+                        "BackwardKey": "22",
+                        "ForwardKey": "26",
+                        "LeftStrafeKey": "4",
+                        "RightStrafeKey": "7",
+                        "SprintKey": "225"
+                    },
+                    "LowerLimit": "0.0f",
+                    "Name": "Free Camera 1",
+                    "Position": "2.5, 2.0f, -2.5f",
+                    "Speed": "2.0f",
+                    "SprintSpeed": "50.0f",
+                    "Type": "FreeCamera",
+                    "UpperLimit": "500000.0f"
+                },
+                {
+                    "Keybindings": {
+                        "CloseKey": "41",
+                        "DebugCaptureMouseKey": "67",
+                        "DebugFullscreenKey": "66",
+                        "LeftStraDebugVertSyncKeyfeKey": "68"
+                    },
+                    "Name": "Debug UI Script 1",
+                    "Type": "DebugUIScript"
+                }
+            ],
+            "Scene": ""
+        }
+    }
+}

+ 1570 - 0
Praxis3D/Data/Maps/SampleScene2.pmap

@@ -0,0 +1,1570 @@
+{
+	"Systems": 
+	{
+		"Graphics": {
+			"Objects": [
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.706434f, 0.477714f, 0.0308436f",
+					"Intensity": "1.0f",
+					"Name": "Directional Light",
+					"Type": "DirectionalLight"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Drywall_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Drywall_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "DrywallPainted_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Drywall_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": []
+						}
+					],
+					"Models": {
+						"Filename": "Workshop_Set.fbx"
+					},
+					"Name": "drywall_panel",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Ground_Albedo.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "2"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Ground_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "2"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "ground_RMHAO.tiff",
+									"Index": "0"
+								},
+								{
+									"Filename": "ground_RMHAO.tiff",
+									"Index": "1"
+								},
+								{
+									"Filename": "ground_RMHAO.tiff",
+									"Index": "2"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workshop_Set.fbx"
+					},
+					"Name": "ground",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "OBS_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "OBS_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "OSB_Panel_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workshop_Set.fbx"
+					},
+					"Name": "OSB_Panel",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, -0.1f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_frame_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workshop_Set.fbx"
+					},
+					"Name": "stud_frame",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.05f, 1.22f, 1.78f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_pile_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workshop_Set.fbx"
+					},
+					"Name": "stud_pile",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"Attenuation": "0.0f, 0.0f, 0.0f",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"CutoffAngle": "1.0f",
+					"Direction": "0.0f, 0.707107f, 0.0f",
+					"Intensity": "1.0f",
+					"Name": "Spot Light",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.061f, 1.467f, 0.0f",
+					"Type": "SpotLight"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Jigsaw_Albedo.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Jigsaw_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "power_jigsaw_RMHAO.tiff",
+									"Index": "0"
+								},
+								{
+									"Filename": "power_jigsaw_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Jigsaw.fbx"
+					},
+					"Name": "power_jigsaw",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.131418f, 0.3802f, -0.342975f",
+					"Rotation": "0.0f, -0.996113f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Hammer_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Hammer_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "hammer_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Hammer.fbx"
+					},
+					"Name": "hammer",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.1759f, 0.0161f, 0.0092f",
+					"Rotation": "0.569456f, 0.415037f, -0.41919f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "SafetyHat_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "SafetyHat_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "safety_hat_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "SafetyHat.fbx"
+					},
+					"Name": "safety_hat",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.113042f, 0.535803f, 0.0585274f",
+					"Rotation": "0.119232f, -0.264276f, -0.100705f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "2"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "2"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "level_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "MagneticLevel.fbx"
+					},
+					"Name": "level",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0696f, 0.00170001f, -0.0012f",
+					"Rotation": "0.0f, 0.670158f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "3"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Elastic_Normal.tif",
+									"Index": "3"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "safety_goggles_RMHAO.tiff",
+									"Index": "3"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "SafetyGoggles.fbx"
+					},
+					"Name": "safety_goggles",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.055f, 0.3786f, 0.249f",
+					"Rotation": "0.0f, 0.148268f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Paint1G_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Paint1G_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "paint_1G_bucket_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "PaintBucket.fbx"
+					},
+					"Name": "paint_1G_bucket",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.308f, 0.0f, -0.085f",
+					"Rotation": "0.0f, -0.981006f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Paint1G_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Paint1G_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "paint_1G_lid_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "PaintBucket.fbx"
+					},
+					"Name": "paint_1G_lid",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.088f, 0.0024f, -0.296f",
+					"Rotation": "-1.0f, -1.87254e-07f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Paint5G_AlbedoSmoothness.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "2"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "2"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "paint_5G_bucket_RMHAO.tiff",
+									"Index": "2"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "PaintBucket.fbx"
+					},
+					"Name": "paint_5G_bucket",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.379319f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Paintbrush_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Paintbrush_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "brush_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Brush.fbx"
+					},
+					"Name": "brush",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.0669f, 0.0101f, -0.2977f",
+					"Rotation": "0.448508f, -0.543519f, -0.560751f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "ConstructionLight_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": [
+								{
+									"Filename": "Legs_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight.fbx"
+					},
+					"Name": "Legs",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": []
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight.fbx"
+					},
+					"Name": "Light_Bulbs",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.0163461f, 1.47104f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "ConstructionLight_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": [
+								{
+									"Filename": "Light_Heads_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight.fbx"
+					},
+					"Name": "Light_Heads",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "ConstructionLight_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": [
+								{
+									"Filename": "Legs_Low_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight_Low.fbx"
+					},
+					"Name": "Legs_Low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.122491f, 0.630871f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": []
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight_Low.fbx"
+					},
+					"Name": "Light_Bulbs_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.0163461f, 1.47118f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "ConstructionLight_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [],
+							"RMHAO": [
+								{
+									"Filename": "Light_Heads_Low_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "ConstructionLight_Low.fbx"
+					},
+					"Name": "Light_Heads_Low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.0580053f, 1.46265f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_bottom_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench.fbx"
+					},
+					"Name": "bench_bottom",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.01f, -0.029f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_legs_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench.fbx"
+					},
+					"Name": "bench_legs",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, -0.0122171f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "PlasticRidges_Albedo.tif",
+									"Index": "2"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "PlasticRidges_Normal.tif",
+									"Index": "2"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_top_RMHAO.tiff",
+									"Index": "1"
+								},
+								{
+									"Filename": "bench_top_RMHAO.tiff",
+									"Index": "2"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench.fbx"
+					},
+					"Name": "bench_top",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.35f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "supports_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench.fbx"
+					},
+					"Name": "supports",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-4.88281e-06f, 0.185861f, -6.34575e-05f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "upper_covers_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench.fbx"
+					},
+					"Name": "upper_covers",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.150578f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_bottom_low_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench_Low.fbx"
+					},
+					"Name": "bench_bottom_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, -0.029f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Metal_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_legs_low_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench_Low.fbx"
+					},
+					"Name": "bench_legs_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, -0.0122171f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "PlasticRidges_Albedo.tif",
+									"Index": "2"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "1"
+								},
+								{
+									"Filename": "PlasticRidges_Normal.tif",
+									"Index": "2"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "bench_top_low_RMHAO.tiff",
+									"Index": "1"
+								},
+								{
+									"Filename": "bench_top_low_RMHAO.tiff",
+									"Index": "2"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench_Low.fbx"
+					},
+					"Name": "bench_top_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.35f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plastic_AlbedoSmoothness.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "1"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								},
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "1"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "supports_low_RMHAO.tiff",
+									"Index": "1"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench_Low.fbx"
+					},
+					"Name": "supports_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.0f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "PlasticRough_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plastic_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "upper_covers_low_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Workbench_Low.fbx"
+					},
+					"Name": "upper_covers_low",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.150578f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Stud.fbx"
+					},
+					"Name": "stud",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "0.0f, 0.134f, 0.0f",
+					"Rotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "1.0f, 0.81247f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_short_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Stud.fbx"
+					},
+					"Name": "stud_short",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.79f, -0.82f, 0.0f",
+					"Rotation": "-0.162427f, -0.688199f, 0.162427f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_short_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Stud.fbx"
+					},
+					"Name": "stud_short",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.757f, -0.71f, 0.153f",
+					"Rotation": "-0.502074f, -0.558014f, 0.445038f",
+					"Scale": "1.0f, 1.0f, 1.0f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				},
+				{
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"Materials": [
+						{
+							"Diffuse": [
+								{
+									"Filename": "Plywood_Albedo.tif",
+									"Index": "0"
+								}
+							],
+							"Emissive": [],
+							"Normal": [
+								{
+									"Filename": "Plywood_Normal.tif",
+									"Index": "0"
+								}
+							],
+							"RMHAO": [
+								{
+									"Filename": "stud_short_RMHAO.tiff",
+									"Index": "0"
+								}
+							]
+						}
+					],
+					"Models": {
+						"Filename": "Stud.fbx"
+					},
+					"Name": "stud_short",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Position": "-0.79f, -1.062f, -0.038f",
+					"Rotation": "0.66832f, -0.230971f, -0.668321f",
+					"Scale": "1.0f, 1f, 1f",
+					"TextureTilingFactor": "1.0f",
+					"Type": "ModelObject"
+				}
+			],
+			"Scene": {
+				"ModelPoolSize": "300",
+				"PointLightPoolSize": "200",
+				"ShaderPoolSize": "100",
+				"SpotLightPoolSize": "200"
+			}
+		},
+		"Scripting": 
+			{
+			"Scene": 
+			{
+			},
+			"Objects": 
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "-90.6949f, 7.47164f, 11.0953f",
+					"Angle": "26.727f, -0.14598f",
+					"Speed": "2.0f",
+					"SprintSpeed": "500.0f",
+					"LowerLimit": "0.0f",
+					"UpperLimit": "500000.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, 5.0f, 0.0f",
+					"Radius": "70.0f",
+					"Rotation": "0.0f, 1.0f, 0.0f",
+					"Speed": "20.0f"
+				},
+				{
+					"Type": "DebugRotateScript",
+					"Name": "Debug Rotate Script 1",
+					"Axis": "0.0f, 1.0f, 0.0f",
+					"Rotation": "-0.141616f, 0.0f, 0.989922f",
+					"Speed": "100.0f"
+				},
+				{
+					"Type": "DebugRotateScript",
+					"Name": "Debug Rotate Script 2",
+					"Axis": "0.0f, 1.0f, 0.0f",
+					"Rotation": "0.141616f, 0.0f, -0.989922f",
+					"Speed": "100.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": "17",
+					"Minutes": "0",
+					"Seconds": "0.0f",
+					"Year": "2018",
+					"Month": "5",
+					"Day": "14",
+					"TimeZone": "3",
+					"Latitude": "54.7f",
+					"Longitude": "25.3f",
+					"TimeMultiplier": "150.0f",
+					"OffsetPosition": "25.0f"
+				}
+			]
+		}
+	},	
+	"ObjectLinks": 
+	[
+		{
+			"Subject": "Free Camera 1",
+			"Observer": "Camera 1"
+		}
+	],
+    "LoadInBackground": "0"
+}

+ 438 - 0
Praxis3D/Data/Maps/componentTest.pmap

@@ -0,0 +1,438 @@
+{
+	"LoadInBackground": "0",
+	"Systems": 
+	{
+		"Graphics": 
+		{
+			"Scene": 
+			{
+				"ModelPoolSize": "30",
+				"ShaderPoolSize": "10",
+				"PointLightPoolSize": "20",
+				"SpotLightPoolSize": "20"
+			},
+			"Objects": 
+			[
+				{
+					"Name": "Camera 1",
+					"LocalPosition": "-90.6949f, 7.47164f, 11.0953f",
+					"Rendering":
+					{
+						"Type": "Camera",
+						"Camera"
+						{
+							
+						}
+					}
+				},
+				{
+					"Name": "Terrain 1",
+					"ID": "3",
+					"LocalPosition": "0.0f, 50.0f, 0.0f",
+					"LocalRotation": "270.0f, 0.0f, 0.0f",
+					"LocalScale": "25.0f, 25.0f, 6.0f",
+					"Parent": "0",
+					"Children":
+					[
+						{
+							"ID": "4"
+						},
+						{
+							"ID": "5"
+						}
+					],
+					"Rendering":
+					{
+						"Type": "ModelObject",
+						"Models": 
+						[
+							{
+								"Filename": "terrain1_UV.3ds",
+								"Meshes":
+								[
+									{
+										"Index": "0",
+										"AlphaThreshold": "0.0f",
+										"HeightScale": "0.0f",
+										"Materials": 
+										{
+											"Diffuse": 
+											{
+												"Filename": "ground_FreshTilledDirt_2k_alb.tga",
+												"TextureScale": "1.0f, 1.0f"
+											},
+											"Normal": 
+											{
+												"Filename": "ground_FreshTilledDirt_2k_nY+.tga",
+												"TextureScale": "1.0f, 1.0f"
+											},
+											"Emissive": 
+											{
+											},
+											"RMHAO": 
+											{
+												"Filename": "Ground_SmoothRocks_RMHAO.tga",
+												"TextureScale": "1.0f, 1.0f"
+											}
+										}
+									}
+								]
+							}
+						],
+						"Shaders": 
+						{
+							"FragmentShader": "geometryPassInf.frag",
+							"VertexShader": "geometryPassInf.vert"
+						}
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Tank 1",
+					"Position": "-100.0f, 5.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": "10.0, 10.0f, 10.0f",
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "model.dae"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "hull_albedo.jpg",
+								"Index": "0"
+							},
+							{
+								"Filename": "turret_albedo.jpg",
+								"Index": "1"
+							},
+							{
+								"Filename": "wheels_albedo.jpg",
+								"Index": "2"
+							},
+							{
+								"Filename": "track_albedo.jpg",
+								"Index": "3"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "hull_normal.jpg",
+								"Index": "0"
+							},
+							{
+								"Filename": "turret_normal.jpg",
+								"Index": "1"
+							},
+							{
+								"Filename": "wheels_normal.jpg",
+								"Index": "2"
+							},
+							{
+								"Filename": "track_normal.jpg",
+								"Index": "3"
+							}
+						],
+						"RMHAO": 
+						[
+							{
+								"Filename": "hull_RMHAO.jpg",
+								"Index": "0"
+							},
+							{
+								"Filename": "turret_RMHAO.jpg",
+								"Index": "1"
+							},
+							{
+								"Filename": "wheels_RMHAO.jpg",
+								"Index": "2"
+							},
+							{
+								"Filename": "track_RMHAO.jpg",
+								"Index": "3"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Barrel 1",
+					"Position": "-75.0f, 0.0f, 0.0f",
+					"Rotation": "270.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "2.0f, 2.0f, 2.0f",
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "RadioactiveBarrel_01.fbx"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "SciFi_Barrels_Mat_SciFi_Barrels_02_BaseColor.png",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "SciFi_Barrels_Mat_SciFi_Barrels_02_Normal.png",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						[
+							{
+								"Filename": "SciFi_Barrels_Mat_SciFi_Barrels_02_Emissive.png",
+								"Index": "0"
+							}
+						],
+						"RMHAO": 
+						[
+							{
+								"Filename": "SciFi_Barrels_Mat_SciFi_Barrels_02_RMHAO.png",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Name": "DirectionalLight 1",
+					"Rendering":
+					{
+						"Type": "DirectionalLight",
+						"Lighting":
+						{
+							"Color": "1.0f, 1.0f, 1.0f",
+							"Direction": "0.006461f, -0.707092f, -0.707092f",
+							"Intensity": "10.0f"
+						}
+					}
+				},
+				{
+					"Name": "PointLight 1",
+					"Position": "12.971f, 5.0f, -68.7851f",
+					"Rendering":
+					{
+						"Lighting":
+						{
+							"Type": "PointLight",
+							"Color": "1.0f, 1.0f, 1.0f",
+							"Intensity": "10.0f"
+						}
+					}
+				},
+				{
+					"Name": "SpotLight 1",
+					"LocalPosition": "60.0f, 3.0f, 60.0f",
+					"LocalRotation": "-1.0f, 0.0f, 0.0f",
+					"Rendering":
+					{
+						"Lighting":
+						{
+							"Type": "SpotLight",
+							"CutoffAngle": "60.0f",
+							"Color": "1.0f, 1.0f, 1.0f",
+							"Intensity": "300.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.145991f, 0.0f, 0.989286f",
+					"Intensity": "300.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, 0.0f, 0.0f",
+					"Direction": "0.145991f, 0.0f, -0.989286f",
+					"Intensity": "300.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": "300.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": "-90.6949f, 7.47164f, 11.0953f",
+					"Angle": "26.727f, -0.14598f",
+					"Speed": "2.0f",
+					"SprintSpeed": "500.0f",
+					"LowerLimit": "0.0f",
+					"UpperLimit": "500000.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, 5.0f, 0.0f",
+					"Radius": "70.0f",
+					"Rotation": "0.0f, 1.0f, 0.0f",
+					"Speed": "20.0f"
+				},
+				{
+					"Type": "DebugRotateScript",
+					"Name": "Debug Rotate Script 1",
+					"Axis": "0.0f, 1.0f, 0.0f",
+					"Rotation": "-0.141616f, 0.0f, 0.989922f",
+					"Speed": "100.0f"
+				},
+				{
+					"Type": "DebugRotateScript",
+					"Name": "Debug Rotate Script 2",
+					"Axis": "0.0f, 1.0f, 0.0f",
+					"Rotation": "0.141616f, 0.0f, -0.989922f",
+					"Speed": "100.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": "17",
+					"Minutes": "0",
+					"Seconds": "0.0f",
+					"Year": "2018",
+					"Month": "5",
+					"Day": "14",
+					"TimeZone": "3",
+					"Latitude": "54.7f",
+					"Longitude": "25.3f",
+					"TimeMultiplier": "150.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": "Debug Rotate Script 1",
+			"Observer": "SpotLight 2"
+		},
+		{
+			"Subject": "Debug Rotate Script 2",
+			"Observer": "SpotLight 3"
+		},
+		{
+			"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"
+		}
+	]
+}

+ 270 - 0
Praxis3D/Data/Maps/converter-test.pmap

@@ -0,0 +1,270 @@
+{
+	"LoadInBackground": "0",
+	"Systems": 
+	{
+		"Graphics": 
+		{
+			"Scene": 
+			{
+				"ModelPoolSize": "30",
+				"ShaderPoolSize": "10",
+				"PointLightPoolSize": "20",
+				"SpotLightPoolSize": "20"
+			},
+			"Objects": 
+			[
+				{
+					"Type": "Camera",
+					"Name": "Camera 1"
+				},
+				{
+					"Type": "DirectionalLight",
+					"Name": "DirectionalLight 1",
+					"Color": "1.0f, 1.0f, 1.0f",
+					"Direction": "0.006461f, -0.707092f, -0.707092f",
+					"Intensity": "10.0f"
+				},
+                {
+                    "AlphaThreshold": "0.0f",
+                    "HeightScale": "0.0f",
+                    "Lighting": "1",
+                    "Materials": {
+                        "Diffuse": [
+                            {
+                                "Filename": "Drywall_AlbedoSmoothness.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Albedo.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "Emissive": [],
+                        "Normal": [
+                            {
+                                "Filename": "DrywallPainted_Normal.tif",
+                                "Index": "0"
+                            },
+                            {
+                                "Filename": "Drywall_Normal.tif",
+                                "Index": "1"
+                            }
+                        ],
+                        "RMHAO": []
+                    },
+                    "Models": {
+                        "Filename": "Workshop_Set.obj"
+                    },
+                    "Name": "drywall_panel",
+                    "OffsetPosition": "0.0f, 0.0f, 0.0f",
+                    "OffsetRotation": "0.0f, 0.0f, 0.0f",
+                    "Position": "0.0f, 0.0f, 0.0f",
+                    "Rotation": "0.0f, 0.0f, 0.0f",
+                    "Scale": "1.0f, 1.0f, 1.0f",
+                    "TextureTilingFactor": "1.0f",
+                    "Type": "ModelObject"
+                },
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain 1",
+					"Position": "0.0f, 50.0f, 0.0f",
+					"Rotation": "270.0f, 0.0f, 0.0f",
+					"OffsetPosition": "0.0f, 0.0f, 0.0f",
+					"OffsetRotation": "0.0f, 0.0f, 0.0f",
+					"Scale": "25.0f, 25.0f, 6.0f",
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "Workshop_Set.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "ground_FreshTilledDirt_2k_alb.tga",
+								"Index": "0"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "ground_FreshTilledDirt_2k_nY+.tga",
+								"Index": "0"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"RMHAO": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_RMHAO.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Terrain2",
+					"Position": "-570.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": "5.0f, 5.0f, 5.0f",
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"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": 
+						{
+						},
+						"RMHAO": 
+						[
+							{
+								"Filename": "Metal_CleanPaintedWithChips_RMHAO3.tga",
+								"Index": "0"
+							}
+						]
+					}
+				},
+				{
+					"Type": "ModelObject",
+					"Name": "Cube 1",
+					"Position": "15.0f, 5.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": "4.0f, 4.0f, 4.0f",
+					"AlphaThreshold": "0.0f",
+					"HeightScale": "0.0f",
+					"Lighting": "1",
+					"TextureTilingFactor": "1.0f",
+					"Models": 
+					{
+						"Filename": "cube.obj"
+					},
+					"Materials": 
+					{
+						"Diffuse": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_alb.tga",
+								"Index": "1"
+							}
+						],
+						"Normal": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_2k_n.tga",
+								"Index": "1"
+							}
+						],
+						"Emissive": 
+						{
+						},
+						"RMHAO": 
+						[
+							{
+								"Filename": "Ground_SmoothRocks_RMHAO.tga",
+								"Index": "1"
+							}
+						]
+					}
+				}
+			]
+		},
+		"Scripting": 
+		{
+			"Scene": 
+			{
+			},
+			"Objects": 
+			[
+				{
+					"Type": "FreeCamera",
+					"Name": "Free Camera 1",
+					"Position": "-90.6949f, 7.47164f, 11.0953f",
+					"Angle": "26.727f, -0.14598f",
+					"Speed": "2.0f",
+					"SprintSpeed": "500.0f",
+					"LowerLimit": "0.0f",
+					"UpperLimit": "500000.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": "SolarTimeScript",
+					"Name": "Solar Time Script 1",
+					"Hours": "17",
+					"Minutes": "0",
+					"Seconds": "0.0f",
+					"Year": "2018",
+					"Month": "5",
+					"Day": "14",
+					"TimeZone": "3",
+					"Latitude": "54.7f",
+					"Longitude": "25.3f",
+					"TimeMultiplier": "150.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"
+		}
+	]
+}

+ 6 - 6
Praxis3D/Data/config.ini

@@ -1,14 +1,14 @@
 window_position_x 250
 window_position_y 60
-window_size_windowed_x 1024
-window_size_windowed_y 768
+window_size_windowed_x 1600
+window_size_windowed_y 900
 window_size_fullscreen_x 1920
 window_size_fullscreen_y 1080
 fullscreen 0
 fullscreen_borderless 1
 gl_context_major_version 3
 gl_context_minor_version 3
-vertical_sync 0
+vertical_sync 1
 default_texture default.png
 generate_mipmaps 1
 gl_texture_anisotropy 16
@@ -19,9 +19,9 @@ eye_adaption_intended_brightness 0.5
 mouse_captured 1
 mouse_warp_mode 0
 fov 80
-z_near 0.1
-z_far 40000
-default_map default_lite.pmap
+z_near 0.001
+z_far 400
+default_map SampleScene.pmap
 atm_scattering_ground_frag_shader atmosphericScatteringPass_ground_simple.frag
 lens_flare_ghost_threshold 2.0
 lens_flare_halo_threshold 2.0

+ 47 - 44
Praxis3D/Data/error-strings-eng.data

@@ -1,74 +1,77 @@
 {
 	"Error Codes":
 	{
-		"Undefined"			: "Undefined",
-		"Success"			: "Succeed",
-		"Failure"			: "Failed",
+		"Undefined"					: "Undefined",
+		"Success"					: "Succeed",
+		"Failure"					: "Failed",
 		"Initialize_success"		: "Has been initialized",
 		"Initialize_failure"		: "Failed to initialized",
 		"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",
+		"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",
+		"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",
+		"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"
+		"SDL_vsync_failed"			: "Failed to change vertical synchronization mode",
+		"Window_creation_failed"	: "Unable to create a window",
+		"Invalid_object_id"			: "Invalid game object ID",
+		"Duplicate_object_id"		: "Duplicate game object ID"
 	},
 	"Error Sources":
 	{
-		"Source_Unknown"		: "Unknown",
-		"Source_General"		: "General",
-		"Source_AtmScatteringPass"	: "Atmosphere Scattering Rendering Pass",
-		"Source_BloomCompositePass"	: "Bloom Effect Composite Rendering Pass",
-		"Source_BloomPass"		: "Bloom Effect Rendering Pass",
-		"Source_BlurPass"		: "Blur Effect Rendering Pass",
- 		"Source_Config"			: "Configuration",
-		"Source_ConfigLoader"		: "Config Loader",
-		"Source_Engine"			: "Engine",
-		"Source_FileLoader"		: "File Loader",
-		"Source_FinalPass"		: "Final Rendering Pass",
-		"Source_GameObject"		: "Game Object",
-		"Source_GeometryBuffer"		: "Geometry Buffer",
-		"Source_GeometryPass"		: "Geometry Rendering Pass",
-		"Source_GraphicsObject"		: "Graphics Object",
-		"Source_HdrMappingPass"		: "HDR Mapping Rendering Pass",
+		"Source_Unknown"				: "Unknown",
+		"Source_General"				: "General",
+		"Source_AtmScatteringPass"		: "Atmosphere Scattering Rendering Pass",
+		"Source_BloomCompositePass"		: "Bloom Effect Composite Rendering Pass",
+		"Source_BloomPass"				: "Bloom Effect Rendering Pass",
+		"Source_BlurPass"				: "Blur Effect Rendering Pass",
+ 		"Source_Config"					: "Configuration",
+		"Source_ConfigLoader"			: "Config Loader",
+		"Source_Engine"					: "Engine",
+		"Source_FileLoader"				: "File Loader",
+		"Source_FinalPass"				: "Final Rendering Pass",
+		"Source_GameObject"				: "Game Object",
+		"Source_GeometryBuffer"			: "Geometry Buffer",
+		"Source_GeometryPass"			: "Geometry Rendering Pass",
+		"Source_GraphicsObject"			: "Graphics Object",
+		"Source_HdrMappingPass"			: "HDR Mapping Rendering Pass",
 		"Source_LensFlareCompositePass"	: "Lense Flare Effect Composite Rendering Pass",
-		"Source_LensFlarePass"		: "Lense Flare Effect Rendering Pass",
-		"Source_LightObject"		: "Light Object",
-		"Source_LightingPass"		: "Lighting Rendering Pass",
-		"Source_ModelLoader"		: "Model Loader",
-		"Source_PlayerObject"		: "Player Object",
-		"Source_PostProcessPass"	: "Post-process rendering pass",
-		"Source_PropertyLoader"		: "Property Loader",
-		"Source_ReflectionPass"		: "Reflection Rendering Pass",
-		"Source_Renderer"		: "Renderer",
-		"Source_SceneLoader"		: "Scene Loader",
-		"Source_Scripting"		: "Scripting System",
-		"Source_ScriptObject"		: "Script Object",
-		"Source_ShaderLoader"		: "Shader Loader",
-		"Source_SkyObject"		: "Sky Object",
-		"Source_SkyPass"		: "Sky Rendering Pass",
-		"Source_TextureLoader"		: "Texture Loader",
-		"Source_Window"			: "Window System"
+		"Source_LensFlarePass"			: "Lense Flare Effect Rendering Pass",
+		"Source_LightObject"			: "Light Object",
+		"Source_LightingPass"			: "Lighting Rendering Pass",
+		"Source_ModelLoader"			: "Model Loader",
+		"Source_PlayerObject"			: "Player Object",
+		"Source_PostProcessPass"		: "Post-process rendering pass",
+		"Source_PropertyLoader"			: "Property Loader",
+		"Source_ReflectionPass"			: "Reflection Rendering Pass",
+		"Source_Renderer"				: "Renderer",
+		"Source_SceneLoader"			: "Scene Loader",
+		"Source_Scripting"				: "Scripting System",
+		"Source_ScriptObject"			: "Script Object",
+		"Source_ShaderLoader"			: "Shader Loader",
+		"Source_SkyObject"				: "Sky Object",
+		"Source_SkyPass"				: "Sky Rendering Pass",
+		"Source_TextureLoader"			: "Texture Loader",
+		"Source_Window"					: "Window System",
+		"Source_World"					: "World"
 	},
 	"Error Types":
 	{
 		"Info"			: "Info",
 		"Warning"		: "Warning",
 		"Error"			: "Error",
-		"FatalError"		: "Fatal Error"
+		"FatalError"	: "Fatal Error"
 	}
 }

+ 19 - 2
Praxis3D/Praxis3D.vcxproj

@@ -291,6 +291,7 @@
     <ClCompile Include="main.cpp" />
     <ClCompile Include="Source\AtmScatteringModel.cpp" />
     <ClCompile Include="Source\AtmScatteringPass.cpp" />
+    <ClCompile Include="Source\BaseGraphicsComponent.cpp" />
     <ClCompile Include="Source\BaseGraphicsObjects.cpp" />
     <ClCompile Include="Source\ChangeController.cpp" />
     <ClCompile Include="Source\ClockLocator.cpp" />
@@ -304,11 +305,12 @@
     <ClCompile Include="Source\GeometryBuffer.cpp" />
     <ClCompile Include="Source\Input.cpp" />
     <ClCompile Include="Source\KeyCommand.cpp" />
+    <ClCompile Include="Source\LightComponent.cpp" />
     <ClCompile Include="Source\Loaders.cpp" />
     <ClCompile Include="Source\Math.cpp" />
     <ClCompile Include="Source\ModelLoader.cpp" />
     <ClCompile Include="Source\NullSystemObjects.cpp" />
-    <ClCompile Include="Source\ObjectDirectorycpp.cpp" />
+    <ClCompile Include="Source\ObjectDirectory.cpp" />
     <ClCompile Include="Source\ObserverBase.cpp" />
     <ClCompile Include="Source\PlayState.cpp" />
     <ClCompile Include="Source\PropertyLoader.cpp" />
@@ -337,6 +339,8 @@
     <ClCompile Include="Source\Window.cpp" />
     <ClCompile Include="Source\WindowLocator.cpp" />
     <ClCompile Include="Source\WorldEditState.cpp" />
+    <ClCompile Include="Source\WorldScene.cpp" />
+    <ClCompile Include="Source\WorldTask.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="resource.h" />
@@ -347,10 +351,12 @@
     <ClInclude Include="Source\AtmScatteringShaderDefinitions.h" />
     <ClInclude Include="Source\AtmScatteringShaderFunctions.h" />
     <ClInclude Include="Source\AtmScatteringShaderPass.h" />
+    <ClInclude Include="Source\BaseGraphicsComponent.h" />
     <ClInclude Include="Source\BaseGraphicsObjects.h" />
     <ClInclude Include="Source\BaseScriptObject.h" />
     <ClInclude Include="Source\BloomCompositePass.h" />
     <ClInclude Include="Source\BlurPass.h" />
+    <ClInclude Include="Source\CameraComponent.h" />
     <ClInclude Include="Source\CameraGraphicsObject.h" />
     <ClInclude Include="Source\CameraScript.h" />
     <ClInclude Include="Source\ChangeController.h" />
@@ -360,6 +366,7 @@
     <ClInclude Include="Source\CommonDefinitions.h" />
     <ClInclude Include="Source\Config.h" />
     <ClInclude Include="Source\ConfigLoader.h" />
+    <ClInclude Include="Source\Containers.h" />
     <ClInclude Include="Source\DebugMoveScript.h" />
     <ClInclude Include="Source\DebugRotateScript.h" />
     <ClInclude Include="Source\DebugUIScript.h" />
@@ -375,19 +382,23 @@
     <ClInclude Include="Source\Filesystem.h" />
     <ClInclude Include="Source\FinalPass.h" />
     <ClInclude Include="Source\Framebuffer.h" />
+    <ClInclude Include="Source\GameObject.h" />
     <ClInclude Include="Source\GeometryBuffer.h" />
     <ClInclude Include="Source\GeometryPass.h" />
     <ClInclude Include="Source\GraphicsDataSets.h" />
+    <ClInclude Include="Source\GraphicsObject.h" />
     <ClInclude Include="Source\HdrMappingPass.h" />
     <ClInclude Include="Source\Input.h" />
     <ClInclude Include="Source\KeyCommand.h" />
     <ClInclude Include="Source\LenseFlareCompositePass.h" />
     <ClInclude Include="Source\LenseFlarePass.h" />
+    <ClInclude Include="Source\LightComponent.h" />
     <ClInclude Include="Source\LightingGraphicsObjects.h" />
     <ClInclude Include="Source\LightingPass.h" />
     <ClInclude Include="Source\LoaderBase.h" />
     <ClInclude Include="Source\Loaders.h" />
     <ClInclude Include="Source\Math.h" />
+    <ClInclude Include="Source\ModelComponent.h" />
     <ClInclude Include="Source\ModelLoader.h" />
     <ClInclude Include="Source\ModelGraphicsObjects.h" />
     <ClInclude Include="Source\NullSystemObjects.h" />
@@ -413,6 +424,7 @@
     <ClInclude Include="Source\ScriptingSystem.h" />
     <ClInclude Include="Source\ScriptingTask.h" />
     <ClInclude Include="Source\ServiceBase.h" />
+    <ClInclude Include="Source\ShaderComponent.h" />
     <ClInclude Include="Source\ShaderGraphicsObjects.h" />
     <ClInclude Include="Source\ShaderLoader.h" />
     <ClInclude Include="Source\ShaderUniforms.h" />
@@ -427,17 +439,22 @@
     <ClInclude Include="Source\TaskScheduler.h" />
     <ClInclude Include="Source\TextureLoader.h" />
     <ClInclude Include="Source\UniformData.h" />
+    <ClInclude Include="Source\ObjectRegister.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" />
+    <ClInclude Include="Source\WorldScene.h" />
+    <ClInclude Include="Source\WorldTask.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="ClassDiagram.cd" />
     <None Include="Data\config.ini" />
-    <None Include="Data\error-strings-eng.data" />
+    <Text Include="Data\error-strings-eng.data">
+      <FileType>Document</FileType>
+    </Text>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Praxis3D1.rc" />

+ 63 - 4
Praxis3D/Praxis3D.vcxproj.filters

@@ -115,6 +115,18 @@
     <Filter Include="Renderer\Render Passes\Source Files">
       <UniqueIdentifier>{708c459e-c8f0-4a2c-b313-f0ab523d5163}</UniqueIdentifier>
     </Filter>
+    <Filter Include="World">
+      <UniqueIdentifier>{47bff9fb-661a-461d-9b39-a2364304b669}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="World\Header Files">
+      <UniqueIdentifier>{2a3e7aee-9afd-4007-8de7-83252feb3b9c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="World\Source Files">
+      <UniqueIdentifier>{ef9fc0a2-c5b9-4436-9390-d0e8b46beeb7}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="World\Objects">
+      <UniqueIdentifier>{073f3e46-6b78-459f-b76e-6a8931bf6a7d}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="main.cpp">
@@ -252,9 +264,6 @@
     <ClCompile Include="Source\BaseGraphicsObjects.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="Source\ObjectDirectorycpp.cpp">
-      <Filter>Service Locators\Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="Source\System.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -264,6 +273,21 @@
     <ClCompile Include="Source\AtmScatteringPass.cpp">
       <Filter>Renderer\Render Passes\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\WorldTask.cpp">
+      <Filter>World\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\WorldScene.cpp">
+      <Filter>World\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\ObjectDirectory.cpp">
+      <Filter>Service Locators\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BaseGraphicsComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\LightComponent.cpp">
+      <Filter>Renderer\Objects\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Source\ErrorCodes.h">
@@ -548,10 +572,42 @@
     <ClInclude Include="Source\Filesystem.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\WorldScene.h">
+      <Filter>World\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\GameObject.h">
+      <Filter>World\Objects</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\WorldTask.h">
+      <Filter>World\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\Containers.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\GraphicsObject.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ObjectRegister.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\BaseGraphicsComponent.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ModelComponent.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\ShaderComponent.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\CameraComponent.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\LightComponent.h">
+      <Filter>Renderer\Objects\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />
-    <None Include="Data\error-strings-eng.data" />
     <None Include="ClassDiagram.cd" />
   </ItemGroup>
   <ItemGroup>
@@ -567,4 +623,7 @@
       <Filter>Resource Files</Filter>
     </Image>
   </ItemGroup>
+  <ItemGroup>
+    <Text Include="Data\error-strings-eng.data" />
+  </ItemGroup>
 </Project>

+ 6 - 0
Praxis3D/Source/BaseGraphicsComponent.cpp

@@ -0,0 +1,6 @@
+#include "BaseGraphicsComponent.h"
+#include "GraphicsObject.h"
+
+void update(GraphicsObject &p_parentObject)
+{
+}

+ 35 - 0
Praxis3D/Source/BaseGraphicsComponent.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Config.h"
+#include "Containers.h"
+#include "PropertySet.h"
+#include "System.h"
+
+class GraphicsObject;
+
+class BaseGraphicsComponent// : SystemObject
+{
+public:
+	BaseGraphicsComponent() : m_empty(true), m_loaded(false) { }
+	~BaseGraphicsComponent() { }
+
+	virtual void load(PropertySet &p_properties) { }
+	virtual PropertySet export() { return PropertySet(); }
+
+	virtual void update(GraphicsObject &p_parentObject);
+	
+	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
+	
+	virtual BitMask getDesiredSystemChanges() { return Systems::Changes::None; };
+
+	const inline bool empty() const { return m_empty; }
+	const inline bool loaded() const { return m_loaded; }
+
+protected:
+	inline void setEmpty(bool p_empty) { m_empty = p_empty; };
+	inline void setLoaded(bool p_loaded) { m_loaded = p_loaded; };
+
+private:
+	bool m_empty;
+	bool m_loaded;
+};

+ 17 - 17
Praxis3D/Source/BaseGraphicsObjects.h

@@ -21,35 +21,35 @@ public:
 		
 	BitMask getSystemType() { return Systems::Graphics; }
 
-	virtual BitMask getDesiredSystemChanges()	{ return Systems::Changes::Spacial::All;	}
+	virtual BitMask getDesiredSystemChanges()	{ return Systems::Changes::Spatial::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::Spatial::WorldPosition)
 		{
 			m_baseObjectData.m_position = 
-				p_subject->getVec3(this, Systems::Changes::Spacial::Position) + m_baseObjectData.m_offsetPosition;
+				p_subject->getVec3(this, Systems::Changes::Spatial::WorldPosition) + m_baseObjectData.m_offsetPosition;
 			m_needsUpdate = true;
 		}
 
-		if(p_changeType & Systems::Changes::Spacial::Rotation)
+		if(p_changeType & Systems::Changes::Spatial::WorldRotation)
 		{
 			m_baseObjectData.m_rotation = 
-				p_subject->getVec3(this, Systems::Changes::Spacial::Rotation) + m_baseObjectData.m_offsetRotation;
+				p_subject->getVec3(this, Systems::Changes::Spatial::WorldRotation) + m_baseObjectData.m_offsetRotation;
 			m_needsUpdate = true;
 		}
 
-		if(p_changeType & Systems::Changes::Spacial::Scale)
+		if(p_changeType & Systems::Changes::Spatial::WorldScale)
 		{
-			m_baseObjectData.m_scale = p_subject->getVec3(this, Systems::Changes::Spacial::Scale);
+			m_baseObjectData.m_scale = p_subject->getVec3(this, Systems::Changes::Spatial::WorldScale);
 			m_needsUpdate = true;
 		}
 
-		if(p_changeType & Systems::Changes::Spacial::ModelMatrix)
+		if(p_changeType & Systems::Changes::Spatial::WorldModelMatrix)
 		{
-			m_baseObjectData.m_modelMat = p_subject->getMat4(this, Systems::Changes::Spacial::ModelMatrix);
+			m_baseObjectData.m_modelMat = p_subject->getMat4(this, Systems::Changes::Spatial::WorldModelMatrix);
 			m_needsUpdate = true;
 		}
 
@@ -73,13 +73,13 @@ public:
 	{
 		switch(p_changedBits)
 		{
-		case Systems::Changes::Spacial::Position:
+		case Systems::Changes::Spatial::Position:
 			return m_baseObjectData.m_position;
 			break;
-		case Systems::Changes::Spacial::Rotation:
+		case Systems::Changes::Spatial::Rotation:
 			return m_baseObjectData.m_rotation;
 			break;
-		case Systems::Changes::Spacial::Scale:
+		case Systems::Changes::Spatial::Scale:
 			return m_baseObjectData.m_scale;
 			break;
 		}
@@ -132,7 +132,7 @@ protected:
 	// Atomic, so it can be changed from different threads (loading to memory is multi-threaded)
 	std::atomic<bool> m_loadedToMemory;
 
-	// Spacial and misc data of an object
+	// Spatial and misc data of an object
 	GraphicsData m_baseObjectData;
 };
 
@@ -158,7 +158,7 @@ public:
 	// Should the object be activated after loading
 	const inline bool isActivatedAfterLoading() const { return m_activateAfterLoading; }
 
-	// Is the object active (i.e. should be drawned, updated, etc...)
+	// Is the object active (i.e. should be drawn, updated, etc...)
 	const inline bool isObjectActive() const { return m_baseGraphicsObject->isObjectActive(); }
 
 	// Getters
@@ -167,14 +167,14 @@ public:
 	const inline std::string &getName() const				{ return m_name;		}
 	const inline LoadableObjectType getObjectType() const	{ return m_objectType;	}
 
-	// Seters
+	// Setters
 	inline void setActivateAfterLoading(const bool p_activateAfterLoading)	{ m_activateAfterLoading = p_activateAfterLoading;			}
 	inline void setLoadedToVideoMemory(const bool p_loaded)					{ m_baseGraphicsObject->setLoadedToVideoMemory(p_loaded);	}
 	inline void setObjectActive(const bool p_objectIsActive)				{ m_baseGraphicsObject->setObjectActive(p_objectIsActive);	}
 
 	// Comparator operators; uses object ID to determine if the object is the same
-	bool operator==(const SystemObject &p_systemObject) const { return m_objectID == p_systemObject.getObjectID() ? true : false; }
-	bool operator==(const SystemObject *p_systemObject) const { return m_objectID == p_systemObject->getObjectID() ? true : false; }
+	bool operator==(const SystemObject &p_systemObject) const { return m_objectID == p_systemObject.getObjectID() ? true : false;	}
+	bool operator==(const SystemObject *p_systemObject) const { return m_objectID == p_systemObject->getObjectID() ? true : false;	}
 private:
 	union ObjectData
 	{

+ 1 - 1
Praxis3D/Source/BaseScriptObject.h

@@ -15,7 +15,7 @@ public:
 
 	virtual BitMask getDesiredSystemChanges() { return Systems::Changes::None; }
 
-	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::Spacial::All; }
+	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::Spatial::All; }
 
 	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
 

+ 24 - 0
Praxis3D/Source/CameraComponent.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "BaseGraphicsComponent.h"
+#include "GraphicsDataSets.h"
+
+class CameraComponent : public BaseGraphicsComponent
+{
+public:
+	CameraComponent() { }
+	~CameraComponent() { }
+
+	void load(PropertySet &p_properties) final override
+	{ 
+
+	}
+
+	PropertySet export() final override
+	{ 
+		return PropertySet(); 
+	}
+
+private:
+
+};

+ 6 - 6
Praxis3D/Source/CameraScript.h

@@ -21,10 +21,10 @@ public:
 	{
 		switch(p_changedBits)
 		{
-		case Systems::Changes::Spacial::Position:
+		case Systems::Changes::Spatial::Position:
 			return m_positionVec;
 			break;
-		case Systems::Changes::Spacial::Rotation:
+		case Systems::Changes::Spatial::Rotation:
 			return m_targetVec;
 			break;
 		}
@@ -36,7 +36,7 @@ public:
 	{
 		switch(p_changedBits)
 		{
-		case Systems::Changes::Spacial::ModelMatrix:
+		case Systems::Changes::Spatial::ModelMatrix:
 			return m_modelMatrix;
 			break;
 		}
@@ -191,9 +191,9 @@ public:
 		m_targetVec.z = m_horizontalAngle;
 		
 		// Notify listeners
-		postChanges(Systems::Changes::Spacial::Position | 
-					Systems::Changes::Spacial::Rotation | 
-					Systems::Changes::Spacial::ModelMatrix);
+		postChanges(Systems::Changes::Spatial::Position | 
+					Systems::Changes::Spatial::Rotation | 
+					Systems::Changes::Spatial::ModelMatrix);
 	}
 	
 	// Setters for the movement keys:

+ 2 - 2
Praxis3D/Source/ChangeController.cpp

@@ -127,7 +127,7 @@ ErrorCode ChangeController::registerSubject(ObservedSubject *p_subject, BitMask
 }
 ErrorCode ChangeController::unregisterSubject(ObservedSubject *p_subject, Observer *p_observer)
 {
-	// Current return error is "failed" untill the unregistering has been completed
+	// Current return error is "failed" until the unregistering has been completed
 	ErrorCode returnError = ErrorCode::Failure;
 
 	if(p_subject && p_observer)
@@ -135,7 +135,7 @@ ErrorCode ChangeController::unregisterSubject(ObservedSubject *p_subject, Observ
 		// Stop the updates and lock, during the subject registration
 		SpinWait::Lock lock(m_spinWaitUpdate);
 
-		unsigned int ID = p_subject->getID(this);
+		const unsigned int ID = p_subject->getID(this);
 
 		if(m_subjectsList.size() <= ID || m_subjectsList[ID].m_subject != p_subject)
 		{

+ 1 - 1
Praxis3D/Source/Clock.h

@@ -98,7 +98,7 @@ public:
 			// 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_tickSamples = Config::engineVar().smoothing_tick_samples;
 
 			m_tickList = new float[m_tickSamples]();
 

+ 2 - 1
Praxis3D/Source/Config.cpp

@@ -41,7 +41,7 @@ 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;
+std::unordered_map<std::string, std::size_t> Config::m_hashTable;
 
 void Config::init()
 {
@@ -57,6 +57,7 @@ void Config::init()
 	AddVariablePredef(m_engineVar, delta_time_divider);
 	AddVariablePredef(m_engineVar, gl_context_major_version);
 	AddVariablePredef(m_engineVar, gl_context_minor_version);
+	AddVariablePredef(m_engineVar, object_directory_init_pool_size);
 	AddVariablePredef(m_engineVar, smoothing_tick_samples);
 
 	// Frame-buffer variables

+ 97 - 31
Praxis3D/Source/Config.h

@@ -9,7 +9,7 @@
 #include "EnumFactory.h"
 #include "Utilities.h"
 
-typedef unsigned int BitMask;
+typedef unsigned __int64 BitMask;
 
 //#define ever ;;
 
@@ -24,46 +24,86 @@ namespace Systems
 	Code(Null, = -1) \
 	Code(Graphics,) \
 	Code(Scripting,) \
+	Code(World,) \
 	Code(NumberOfSystems,) 
 	DECLARE_ENUM(TypeID, TYPEID)
 
 	const static std::string SystemNames[NumberOfSystems] =
 	{
 		GetString(Graphics),
-		GetString(Scripting)
+		GetString(Scripting),
+		GetString(World)
 	};
 	
 	namespace Types
 	{
-		static const BitMask All = static_cast<BitMask>(-1);
-		static const BitMask Max = 32;
+		static constexpr BitMask All = static_cast<BitMask>(-1);
+		static constexpr BitMask Max = 32;
 	}
 
 	namespace Changes
 	{
+		namespace Common
+		{
+			/*static constexpr BitMask Shared0	= (BitMask)1 << 0;
+			static constexpr BitMask Shared1	= (BitMask)1 << 1;
+			static constexpr BitMask Shared2	= (BitMask)1 << 3;
+			static constexpr BitMask Shared3	= (BitMask)1 << 4;
+			static constexpr BitMask Shared4	= (BitMask)1 << 5;
+			static constexpr BitMask Shared5	= (BitMask)1 << 6;
+			static constexpr BitMask Shared6	= (BitMask)1 << 7;
+			static constexpr BitMask Shared7	= (BitMask)1 << 8;
+			static constexpr BitMask Shared8	= (BitMask)1 << 9;
+			static constexpr BitMask Shared9	= (BitMask)1 << 10;*/
+		}
 		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;
+			static constexpr BitMask CreateObject		= (BitMask)1 << 0;
+			static constexpr BitMask DeleteObject		= (BitMask)1 << 1;
+			static constexpr BitMask ExtendObject		= (BitMask)1 << 2;
+			static constexpr BitMask UnextendObject		= (BitMask)1 << 3;
+			static constexpr BitMask Name				= (BitMask)1 << 4;
+			static constexpr BitMask All				= CreateObject | DeleteObject | ExtendObject | Name;
 		}
-		namespace Spacial
+		namespace Spatial
 		{
-			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;
+			static constexpr BitMask LocalModifier			= (BitMask)1 << 5;
+			static constexpr BitMask WorldModifier			= (BitMask)1 << 6;
+			static constexpr BitMask Position				= (BitMask)1 << 7;
+			static constexpr BitMask Rotation				= (BitMask)1 << 8;
+			static constexpr BitMask Scale					= (BitMask)1 << 9;
+			static constexpr BitMask Transform				= (BitMask)1 << 10;
+
+			static constexpr BitMask LocalPosition			= LocalModifier | Position;
+			static constexpr BitMask LocalRotation			= LocalModifier | Rotation;
+			static constexpr BitMask LocalScale				= LocalModifier | Scale;
+			static constexpr BitMask LocalTransform			= LocalModifier | Transform;
+			static constexpr BitMask WorldPosition			= WorldModifier | Position;
+			static constexpr BitMask WorldRotation			= WorldModifier | Rotation;
+			static constexpr BitMask WorldScale				= WorldModifier | Scale;
+			static constexpr BitMask WorldTransform			= WorldModifier | Transform;
+
+			static constexpr BitMask AllLocalNoTransform	= LocalPosition | LocalRotation | LocalScale;
+			static constexpr BitMask AllWorldNoTransform	= WorldPosition | WorldRotation | WorldScale;
+			static constexpr BitMask AllLocal				= AllLocalNoTransform | LocalTransform;
+			static constexpr BitMask AllWorld				= AllWorldNoTransform | WorldTransform;
+			static constexpr BitMask All					= AllLocal | AllWorld;
 		}
 		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;
+			static constexpr BitMask Lighting				= (BitMask)1 << 11;
+
+			static constexpr BitMask Target					= (BitMask)1 << 12;
+			static constexpr BitMask UpVector				= (BitMask)1 << 13;
+			static constexpr BitMask AllCamera				= Target | UpVector;
+
+			static constexpr BitMask Color					= (BitMask)1 << 14;
+			static constexpr BitMask CutoffAngle			= (BitMask)1 << 15;
+			static constexpr BitMask Direction				= (BitMask)1 << 16;
+			static constexpr BitMask Intensity				= (BitMask)1 << 17;
+			static constexpr BitMask AllLightig				= Color | CutoffAngle | Direction | Intensity;
+
+			static constexpr BitMask All					= AllCamera | AllLightig;
 		}
 		namespace Physics
 		{
@@ -78,10 +118,10 @@ namespace Systems
 
 		}
 
-		static const BitMask Link = (1 << 29);
+		static constexpr BitMask Link = (BitMask)1 << 29;
 
-		static const BitMask None = 0;
-		static const BitMask All = static_cast<BitMask>(-1);
+		static constexpr BitMask None = 0;
+		static constexpr BitMask All = static_cast<BitMask>(-1);
 	}
 }
 
@@ -104,9 +144,14 @@ namespace Properties
 	/* Geometry */ \
 	Code(OffsetPosition,) \
 	Code(OffsetRotation,) \
-	Code(Position,) \
-	Code(Rotation,) \
-	Code(Scale,) \
+	Code(LocalPosition,) \
+	Code(LocalRotation,) \
+	Code(LocalRotationQuaternion,) \
+	Code(LocalScale,) \
+	Code(WorldPosition,) \
+	Code(WorldRotation,) \
+	Code(WorldRotationQuaternion,) \
+	Code(WorldScale,) \
 	/* Graphics */ \
 	Code(AlphaThreshold, ) \
 	Code(AmbientOcclusion, ) \
@@ -130,6 +175,7 @@ namespace Properties
 	Code(Lighting,) \
 	Code(Materials,) \
 	Code(Metalness,) \
+	Code(Meshes,) \
 	Code(Models,) \
 	Code(ModelObject,) \
 	Code(ModelPoolSize,) \
@@ -144,6 +190,7 @@ namespace Properties
 	Code(PositiveY,) \
 	Code(PositiveZ,) \
 	Code(PostProcess,) \
+	Code(Rendering,) \
 	Code(RMHAO,) \
 	Code(Roughness,) \
 	Code(Shaders,) \
@@ -156,6 +203,7 @@ namespace Properties
 	Code(TessControlShader,) \
 	Code(TessEvaluationShader,) \
 	Code(TextureTilingFactor,) \
+	Code(TextureScale,) \
 	Code(VertexShader,) \
 	/* Key binds */ \
 	Code(BackwardKey,) \
@@ -218,6 +266,11 @@ namespace Properties
 	Code(MouseCapture,) \
 	Code(VerticalSync,) \
 	Code(WindowTitle,) \
+	/* World */ \
+	Code(Children,) \
+	Code(GameObject,) \
+	Code(ID,) \
+	Code(Parent,) \
 	/* End of property IDs */ \
 	Code(NumberOfPropertyIDs,) 
 	DECLARE_ENUM(PropertyID, PROPERTYID)
@@ -238,9 +291,14 @@ namespace Properties
 		GetString(Type),
 		GetString(OffsetPosition),
 		GetString(OffsetRotation),
-		GetString(Position),
-		GetString(Rotation),
-		GetString(Scale),
+		GetString(LocalPosition),
+		GetString(LocalRotation),
+		GetString(LocalRotationQuaternion),
+		GetString(LocalScale),
+		GetString(WorldPosition),
+		GetString(WorldRotation),
+		GetString(WorldRotationQuaternion),
+		GetString(WorldScale),
 		GetString(AlphaThreshold),
 		GetString(AmbientOcclusion),
 		GetString(Attenuation),
@@ -263,6 +321,7 @@ namespace Properties
 		GetString(Lighting),
 		GetString(Materials),
 		GetString(Metalness),
+		GetString(Meshes),
 		GetString(Models),
 		GetString(ModelObject),
 		GetString(ModelPoolSize),
@@ -277,6 +336,7 @@ namespace Properties
 		GetString(PositiveY),
 		GetString(PositiveZ),
 		GetString(PostProcess),
+		GetString(Rendering),
 		GetString(RMHAO),
 		GetString(Roughness),
 		GetString(Shaders),
@@ -289,6 +349,7 @@ namespace Properties
 		GetString(TessControlShader),
 		GetString(TessEvaluationShader),
 		GetString(TextureTilingFactor),
+		GetString(TextureScale),
 		GetString(VertexShader),
 		GetString(BackwardKey),
 		GetString(CenterKey),
@@ -346,10 +407,13 @@ namespace Properties
 		GetString(Fullscreen),
 		GetString(MouseCapture),
 		GetString(VerticalSync),
-		GetString(WindowTitle)
+		GetString(WindowTitle),
+		GetString(Children),
+		GetString(GameObject),
+		GetString(ID),
+		GetString(Parent)
 	};
 
-
 	// 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
@@ -425,6 +489,7 @@ public:
 			delta_time_divider = 1000;
 			gl_context_major_version = 3;
 			gl_context_minor_version = 3;
+			object_directory_init_pool_size = 1000;
 			smoothing_tick_samples = 100;
 			running = true;
 		}
@@ -437,6 +502,7 @@ public:
 		int delta_time_divider;
 		int gl_context_major_version;
 		int gl_context_minor_version;
+		int object_directory_init_pool_size;
 		int smoothing_tick_samples;
 		bool running;
 	};

+ 35 - 0
Praxis3D/Source/Containers.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Math.h"
+
+// Stores all spatial data (position, rotation, scale)
+struct SpatialData
+{	
+	// Set all data to default
+	void clear()
+	{
+		m_position = Math::Vec3f();
+		m_scale = Math::Vec3f();
+		m_rotationEuler = Math::Vec3f();
+		m_rotationQuat = Math::Quaternion();
+	}
+
+	Math::Vec3f m_position,
+				m_scale,
+				m_rotationEuler;
+	Math::Quaternion m_rotationQuat;
+};
+
+// Stores all spatial data (position, rotation, scale) plus transform matrix
+struct SpatialTransformData
+{
+	// Set all data to default
+	void clear()
+	{
+		m_spatialData.clear();
+		m_transformMat.identity();
+	}
+
+	SpatialData m_spatialData;
+	Math::Mat4f m_transformMat;
+};

+ 2 - 2
Praxis3D/Source/DebugMoveScript.h

@@ -51,14 +51,14 @@ public:
 
 		m_positionVec = m_targetVec * m_radius + m_originPosVec;
 
-		postChanges(Systems::Changes::Spacial::Position);
+		postChanges(Systems::Changes::Spatial::Position);
 	}
 
 	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
 	{
 		switch(p_changedBits)
 		{
-		case Systems::Changes::Spacial::Position:
+		case Systems::Changes::Spatial::Position:
 			return m_positionVec;
 			break;
 		}

+ 5 - 0
Praxis3D/Source/ErrorCodes.h

@@ -68,6 +68,10 @@ DECLARE_ENUM(ErrorType, ERROR_TYPES)
 	Code(SDL_video_init_failed,) \
 	Code(SDL_vsync_failed,) \
 	Code(Window_creation_failed,) \
+	/* World scene errors */ \
+	Code(Invalid_object_id,) \
+	Code(Duplicate_object_id,) \
+	/* Error management */ \
 	Code(NumberOfErrorCodes,) \
 	Code(CachedError,)
 DECLARE_ENUM(ErrorCode, ERROR_CODES)
@@ -107,6 +111,7 @@ DECLARE_ENUM(ErrorCode, ERROR_CODES)
     Code(Source_SkyPass,) \
     Code(Source_TextureLoader,) \
     Code(Source_Window,) \
+    Code(Source_World,) \
     Code(Source_NumberOfErrorSources,) 
 DECLARE_ENUM(ErrorSource, ERROR_SOURCE)
 

+ 4 - 28
Praxis3D/Source/ErrorHandler.cpp

@@ -47,35 +47,10 @@ ErrorHandler::ErrorHandler()
 	AssignErrorType(SDL_video_init_failed, FatalError);
 	AssignErrorType(SDL_vsync_failed, Warning);
 	AssignErrorType(Window_creation_failed, FatalError);
+	AssignErrorType(Invalid_object_id, Error);
+	AssignErrorType(Duplicate_object_id, Error);
 	
-	// Add error sources to the hash map, and offset them by number of error codes, because they share the same hash map
-	m_errHashmap[GetString(Source_Unknown)]			= NumberOfErrorCodes + Source_Unknown;
-	m_errHashmap[GetString(Source_General)]			= NumberOfErrorCodes + Source_General;
-	m_errHashmap[GetString(Source_Engine)]			= NumberOfErrorCodes + Source_Engine;
-	m_errHashmap[GetString(Source_Renderer)]		= NumberOfErrorCodes + Source_Renderer;
-	m_errHashmap[GetString(Source_Scripting)]		= NumberOfErrorCodes + Source_Scripting;
-	m_errHashmap[GetString(Source_Config)]			= NumberOfErrorCodes + Source_Config;
-	m_errHashmap[GetString(Source_ConfigLoader)]	= NumberOfErrorCodes + Source_ConfigLoader;
-	m_errHashmap[GetString(Source_TextureLoader)]	= NumberOfErrorCodes + Source_TextureLoader;
-	m_errHashmap[GetString(Source_ModelLoader)]		= NumberOfErrorCodes + Source_ModelLoader;
-	m_errHashmap[GetString(Source_ShaderLoader)]	= NumberOfErrorCodes + Source_ShaderLoader;
-	m_errHashmap[GetString(Source_FileLoader)]		= NumberOfErrorCodes + Source_FileLoader;
-	m_errHashmap[GetString(Source_SceneLoader)]		= NumberOfErrorCodes + Source_SceneLoader;
-	m_errHashmap[GetString(Source_LightingPass)]	= NumberOfErrorCodes + Source_LightingPass;
-	m_errHashmap[GetString(Source_GeometryBuffer)]	= NumberOfErrorCodes + Source_GeometryBuffer;
-	m_errHashmap[GetString(Source_GeometryPass)]	= NumberOfErrorCodes + Source_GeometryPass;
-	m_errHashmap[GetString(Source_GraphicsObject)]	= NumberOfErrorCodes + Source_GraphicsObject;
-	m_errHashmap[GetString(Source_FinalPass)]		= NumberOfErrorCodes + Source_FinalPass;
-	m_errHashmap[GetString(Source_ReflectionPass)]	= NumberOfErrorCodes + Source_ReflectionPass;
-	m_errHashmap[GetString(Source_ScriptObject)]	= NumberOfErrorCodes + Source_ScriptObject;
-	m_errHashmap[GetString(Source_PlayerObject)]	= NumberOfErrorCodes + Source_PlayerObject;
-	m_errHashmap[GetString(Source_GameObject)]		= NumberOfErrorCodes + Source_GameObject;
-	m_errHashmap[GetString(Source_SkyObject)]		= NumberOfErrorCodes + Source_SkyObject;
-	m_errHashmap[GetString(Source_LightObject)]		= NumberOfErrorCodes + Source_LightObject;
-	m_errHashmap[GetString(Source_PropertyLoader)]	= NumberOfErrorCodes + Source_PropertyLoader;
-	m_errHashmap[GetString(Source_Window)]			= NumberOfErrorCodes + Source_Window;
-
-	
+	// Add error sources to the hash map, and offset them by number of error codes, because they share the same hash map	
     m_errHashmap[GetString(Source_Unknown)]						= NumberOfErrorCodes + Source_Unknown;
     m_errHashmap[GetString(Source_General)]						= NumberOfErrorCodes + Source_General;
     m_errHashmap[GetString(Source_AtmScatteringPass)]			= NumberOfErrorCodes + Source_AtmScatteringPass;
@@ -110,6 +85,7 @@ ErrorHandler::ErrorHandler()
     m_errHashmap[GetString(Source_SkyPass)]						= NumberOfErrorCodes + Source_SkyPass;
     m_errHashmap[GetString(Source_TextureLoader)]				= NumberOfErrorCodes + Source_TextureLoader;
     m_errHashmap[GetString(Source_Window)]						= NumberOfErrorCodes + Source_Window;
+	m_errHashmap[GetString(Source_World)]						= NumberOfErrorCodes + Source_World;
 
 	// Add error types to the hash map, and offset them by number of error codes and error sources, because they share the same hash map
 	m_errHashmap[GetString(Info)]		= NumberOfErrorCodes + Source_NumberOfErrorSources + Info;

+ 247 - 0
Praxis3D/Source/GameObject.h

@@ -0,0 +1,247 @@
+#pragma once
+
+#include "Containers.h"
+#include "GraphicsObject.h"
+#include "SceneLoader.h"
+#include "System.h"
+
+// The main object type that glues all other system objects together, forming a component system.
+// Responsible for linking objects of other systems with one another, so their data changes can be synchronized.
+class GameObject : public SystemObject
+{
+	friend class WorldScene;
+public:
+	GameObject(SystemScene *p_systemScene, std::string p_name, SceneLoader &p_sceneLoader, std::size_t p_id = 0) : SystemObject(p_systemScene, p_name, Properties::GameObject), m_sceneLoader(p_sceneLoader), m_id(p_id)
+	{
+		m_parent = nullptr;
+		m_graphicsComponent = nullptr;
+	}
+	
+	BitMask getSystemType() override final { return Systems::World; }
+	
+	void update(const float p_deltaTime)
+	{
+		// If update is needed
+		if(m_updateNeeded)
+		{
+			
+			//m_localSpace.m_transformMat = Math::createTransformMat(m_localSpace.m_transformMat.getPosition(), m_localSpace.m_rotationEuler, m_localSpace.m_transformMat.getScale());
+			//m_worldSpace.m_transformMat = Math::createTransformMat(m_worldSpace.m_transformMat.getPosition(), m_worldSpace.m_rotationEuler, m_worldSpace.m_transformMat.getScale());
+
+			// Mark as updated
+			m_updateNeeded = false;
+		}
+	}
+
+	// Get the data change types that this object is interested in
+	BitMask getDesiredSystemChanges() override { return Systems::Changes::Spatial::All; }
+
+	// Get the data change types that this object might modify
+	BitMask getPotentialSystemChanges() override { return Systems::Changes::Spatial::All; }
+
+	// Notify this object of the data that has been changed
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) override 
+	{
+		BitMask newChanges = Systems::Changes::None;
+
+		if(p_changeType & Systems::Changes::Spatial::Local)
+		{
+			m_localSpace = p_subject->getSpatialData(this, Systems::Changes::Spatial::Local);
+			newChanges = newChanges | Systems::Changes::Spatial::Local;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::World)
+		{
+			m_worldSpace = p_subject->getSpatialData(this, Systems::Changes::Spatial::World);
+			newChanges = newChanges | Systems::Changes::Spatial::World;
+		}
+		
+		if(newChanges != Systems::Changes::None)
+			postChanges(newChanges);
+
+		/*if(p_changeType & Systems::Changes::Spatial::LocalRotation)
+		{
+			const auto &newLocalRotation = p_subject->getVec3(this, Systems::Changes::Spatial::LocalRotation);
+
+			// If the world rotation isn't being changed, adjust it accordingly, so it has the latest rotation
+			if(!(p_changeType & Systems::Changes::Spatial::WorldRotation))
+			{
+				m_spatialData.m_worldRotationEuler -= m_spatialData.m_localRotationEuler;
+				m_spatialData.m_worldRotationEuler += newLocalRotation;
+				newChanges = newChanges | Systems::Changes::Spatial::WorldRotation;
+			}
+
+			m_spatialData.m_localRotationEuler = newLocalRotation;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::WorldRotation)
+		{
+			m_spatialData.m_worldRotationEuler = p_subject->getVec3(this, Systems::Changes::Spatial::WorldRotation) + m_spatialData.m_localRotationEuler;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::WorldPosition)
+		{
+			m_spatialData.m_worldMat.setPosition(p_subject->getVec3(this, Systems::Changes::Spatial::WorldPosition) + m_spatialData.m_localMat.getPosition());
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::WorldScale)
+		{
+			m_spatialData.m_worldMat.setScale(p_subject->getVec3(this, Systems::Changes::Spatial::WorldScale) * m_spatialData.m_localMat.getScale());
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::WorldModelMatrix)
+		{
+			m_spatialData.m_worldMat = p_subject->getMat4(this, Systems::Changes::Spatial::WorldModelMatrix);
+		}*/
+	}
+
+	const Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const override final
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spatial::LocalPosition:
+			return m_localSpace.m_position;
+			break;
+		case Systems::Changes::Spatial::LocalRotation:
+			return m_localSpace.m_rotationEuler;
+			break;
+		case Systems::Changes::Spatial::LocalScale:
+			return m_localSpace.m_scale;
+			break;
+		case Systems::Changes::Spatial::WorldPosition:
+			return m_worldSpace.m_position;
+			break;
+		case Systems::Changes::Spatial::WorldRotation:
+			return m_worldSpace.m_rotationEuler;
+			break;
+		case Systems::Changes::Spatial::WorldScale:
+			return m_worldSpace.m_scale;
+			break;
+		}
+
+		return ObservedSubject::getVec3(p_observer, p_changedBits);
+	}
+	const Math::Vec4f &getVec4(const Observer *p_observer, BitMask p_changedBits) const override final
+	{ 
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spatial::LocalRotation:
+			return m_localSpace.m_rotationQuat;
+			break;
+		case Systems::Changes::Spatial::WorldRotation:
+			return m_worldSpace.m_rotationQuat;
+			break;
+		}
+
+		return ObservedSubject::getVec4(p_observer, p_changedBits);
+	}
+
+	// Set the parent of this object. The object can have only one parent, thus if the parent was set already, it will be overridden
+	void setParent(GameObject *p_parent) { m_parent = p_parent; }
+
+	// Child functions
+	void addChild(GameObject &p_child) 
+	{ 
+		// Add the child to the observer list
+		m_sceneLoader.getChangeController()->createObjectLink(this, &p_child);
+
+		// Add the child to the children array
+		m_children.push_back(p_child); 
+	}
+	void removeChild(const std::size_t p_id)
+	{
+		// Loop over every child
+		for(decltype(m_children.size()) size = m_children.size(), i = 0; i < size; i++)
+		{
+			// Match the child ID
+			if(m_children[i].m_id == p_id)
+			{
+				// Unlink the child from the observer list
+				m_sceneLoader.getChangeController()->removeObjectLink(this, &m_children[i]);
+
+				// Erase the child if the IDs matched
+				m_children.erase(m_children.begin() + i);
+			}
+		}
+	}
+	void removeChild(const GameObject &p_child) { removeChild(p_child.m_id); }
+	void clearChildren() { m_children.clear(); }
+	const std::vector<GameObject&> &getChildren() const { return m_children; }
+
+	// Component functions
+	void addComponent(GraphicsObject *p_graphicsComponent) { m_graphicsComponent = p_graphicsComponent; }
+	void addComponent(SystemObject *p_component, Systems::TypeID p_componentType)
+	{
+		switch(p_componentType)
+		{
+			case Systems::TypeID::Graphics:
+			{
+				// Remove the old component if it existed
+				removeComponent(p_componentType);
+
+				// Assign the new graphics component
+				m_graphicsComponent = p_component;
+
+				// Set the graphics component as an observer of this game object
+				m_sceneLoader.getChangeController()->createObjectLink(this, m_graphicsComponent);
+				break;
+			}
+			case Systems::TypeID::Scripting:
+			{
+				
+				break;
+			}
+		}
+	}
+	void removeComponent(Systems::TypeID p_componentType) 
+	{
+		switch(p_componentType)
+		{
+			case Systems::TypeID::Graphics:
+			{
+				removeComponent(m_graphicsComponent);
+				break;
+			}
+			case Systems::TypeID::Scripting:
+			{
+
+				break;
+			}
+		}
+	}
+
+private:
+	// Set the ID of the object. The ID must be unique
+	void setID(std::size_t p_id) { m_id = p_id; }
+
+	// Remove the given component
+	void removeComponent(SystemObject *p_component)
+	{
+		// If the component exists
+		if(p_component != nullptr)
+		{
+			// Remove the component from the observers
+			m_sceneLoader.getChangeController()->removeObjectLink(this, p_component);
+
+			// Assign the component pointer as nullptr to denote that it has been removed
+			p_component = nullptr;
+		}
+	}
+
+	SceneLoader &m_sceneLoader;
+
+	// Parent and children hierarchy
+	std::vector<GameObject&> m_children;
+	GameObject *m_parent;
+
+	//Components
+	SystemObject *m_graphicsComponent;
+	SystemObject *m_scriptingComponent;
+
+	// Position data
+	SpatialData m_localSpace,
+				m_worldSpace;
+
+	// ID of a GameObject; separate from m_objectID of a SystemObject
+	std::size_t m_id;
+};

+ 58 - 0
Praxis3D/Source/GraphicsDataSets.h

@@ -3,6 +3,64 @@
 #include "Math.h"
 #include "Loaders.h"
 
+// Contains data of a single material
+struct MaterialData
+{
+
+	MaterialData(TextureLoader2D::Texture2DHandle &m_texture = Loaders::texture2D().getDefaultTexture()) : 
+		m_texture(m_texture), 
+		m_textureScale(1.0f, 1.0f), 
+		m_parallaxScale(1.0f), 
+		m_alphaCutoff(1.0f) { }
+
+	// Handle to a texture
+	TextureLoader2D::Texture2DHandle m_texture;
+	// Texture coordinates scale (for example, used for tilling)
+	Math::Vec2f m_textureScale;
+	// Texture parallax effect scale (height multiplier)
+	float m_parallaxScale;
+	// Transparency threshold after which the fragment is discarded
+	float m_alphaCutoff;
+};
+
+// Contains data of a single mesh and its materials
+struct MeshData
+{
+	MeshData(Model::Mesh &p_mesh, MaterialData p_materials[MaterialType_NumOfTypes]) : m_mesh(p_mesh), m_materials{ *p_materials } { }
+
+	// Handle to a mesh
+	Model::Mesh &m_mesh;
+
+	// An array of materials of each type
+	MaterialData m_materials[MaterialType_NumOfTypes];
+};
+
+// Contains data of a single model and its meshes
+struct ModelData
+{
+	ModelData(ModelLoader::ModelHandle &p_model) : m_model(p_model) { }
+
+	// Handle to a model
+	ModelLoader::ModelHandle m_model;
+	// An array of meshes
+	std::vector<MeshData>  m_meshes;
+};
+
+// Contains data multiple of multiple models, comprising a model component
+struct ModelComponentData
+{
+	std::vector<ModelData> m_modelData;
+};
+
+// Contains data of a single shader program
+struct ShaderData
+{
+	ShaderData(ShaderLoader::ShaderProgram &p_shader) : m_shader(p_shader) { }
+
+	// Handle to a shader program
+	ShaderLoader::ShaderProgram &m_shader;
+};
+
 // All graphics objects contain an instance of this struct, which holds the necessary spacial and other data
 struct GraphicsData
 {

+ 395 - 0
Praxis3D/Source/GraphicsObject.h

@@ -0,0 +1,395 @@
+#pragma once
+
+#include "Containers.h"
+#include "GraphicsDataSets.h"
+#include "LightComponent.h"
+#include "Loaders.h"
+#include "Math.h"
+#include "NullSystemObjects.h"
+#include "System.h"
+
+enum GraphicsComponentType : std::size_t
+{
+	GraphicsComponentType_Model = 0,
+	GraphicsComponentType_Shader,
+	GraphicsComponentType_Light,
+	GraphicsComponentType_NumOfComponents
+};
+
+class GraphicsObject : public SystemObject
+{
+public:
+	GraphicsObject(SystemScene *p_systemScene, const std::string &p_name)
+		: SystemObject(p_systemScene, p_name, Properties::Graphics) 
+	{
+		m_modelComponent = nullptr;
+		m_shaderComponent = nullptr;
+		m_lightComponent = nullptr;
+		m_updateQuaternion = false;
+	}
+	virtual ~GraphicsObject() 
+	{
+		// Iterate over all component types and delete components if they have been created
+		for(std::size_t i = 0; i < GraphicsComponentType::GraphicsComponentType_NumOfComponents; i++)
+			removeComponent(static_cast<GraphicsComponentType>(i));
+	}
+	
+	// System type is Graphics
+	BitMask getSystemType() { return Systems::Graphics; }
+		
+	void update(const float p_deltaTime)
+	{
+		if(isUpdateNeeded())
+		{
+			if(m_updateQuaternion)
+			{
+				//TODO: calculate rotation quaternion
+				m_updateQuaternion = false;
+			}
+
+			// Calculate model matrix
+			m_worldSpace.m_transformMat = Math::createTransformMat(m_worldSpace.m_spatialData.m_position, m_worldSpace.m_spatialData.m_rotationEuler, m_worldSpace.m_spatialData.m_scale);
+
+			// Mark as updated
+			updatePerformed();
+		}
+	}
+
+	virtual BitMask getDesiredSystemChanges()	{ return Systems::Changes::Spatial::AllWorld | Systems::Changes::Graphics::All;		}
+	virtual BitMask getPotentialSystemChanges() { return Systems::Changes::Spatial::WorldTransform;	}
+	
+	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+	{
+		// Track what data has been modified
+		BitMask newChanges = Systems::Changes::None;
+		
+		// Get all of the world spatial data, include the transform matrix; add up the bit-mask of changed data;
+		if(p_changeType & Systems::Changes::Spatial::AllWorld)
+		{
+			m_worldSpace.m_spatialData = p_subject->getSpatialData(this, Systems::Changes::Spatial::AllWorldNoTransform);
+			m_worldSpace.m_transformMat = p_subject->getMat4(this, Systems::Changes::Spatial::WorldTransform);
+
+			newChanges = newChanges | Systems::Changes::Spatial::AllWorld;
+		}
+		else
+		{
+			// Get world spatial data without transform matrix; add up the bit-mask of changed data; flag object to need updating
+			if(p_changeType & Systems::Changes::Spatial::AllWorldNoTransform)
+			{
+				m_worldSpace.m_spatialData = p_subject->getSpatialData(this, Systems::Changes::Spatial::AllWorldNoTransform);
+
+				newChanges = newChanges | Systems::Changes::Spatial::AllWorldNoTransform;
+
+				setUpdateNeeded(true);
+			}
+			else
+			{
+				// Get world position vector; add up the bit-mask of changed data; flag object to need updating
+				if(p_changeType & Systems::Changes::Spatial::WorldPosition)
+				{
+					m_worldSpace.m_spatialData.m_position = p_subject->getVec3(this, Systems::Changes::Spatial::WorldPosition);
+
+					newChanges = newChanges | Systems::Changes::Spatial::WorldPosition;
+				
+					setUpdateNeeded(true);
+				}
+
+				// Get world rotation vector; add up the bit-mask of changed data; flag object to need updating; flag rotation quaternion to need updating
+				if(p_changeType & Systems::Changes::Spatial::WorldRotation)
+				{
+					m_worldSpace.m_spatialData.m_rotationEuler = p_subject->getVec3(this, Systems::Changes::Spatial::WorldRotation);
+
+					newChanges = newChanges | Systems::Changes::Spatial::WorldRotation;
+				
+					setUpdateNeeded(true);
+					m_updateQuaternion = true;
+				}
+
+				// Get world scale vector; add up the bit-mask of changed data; flag object to need updating
+				if(p_changeType & Systems::Changes::Spatial::WorldScale)
+				{
+					m_worldSpace.m_spatialData.m_scale = p_subject->getVec3(this, Systems::Changes::Spatial::WorldScale);
+
+					newChanges = newChanges | Systems::Changes::Spatial::WorldScale;
+
+					setUpdateNeeded(true);
+				}
+			}
+		}
+
+		// If any new data has been left, pass it to the components
+		if(newChanges != Systems::Changes::None)
+		{
+
+		}
+			//postChanges(newChanges);
+	}
+		
+	const Math::Mat4f &getMat4(const Observer *p_observer, BitMask p_changedBits) const
+	{ 		
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spatial::WorldTransform:
+			return m_worldSpace.m_transformMat;
+			break;
+		}
+
+		return ObservedSubject::getMat4(p_observer, p_changedBits);
+	}
+	const SpatialData &getSpatialData(const Observer *p_observer, BitMask p_changedBits) const
+	{ 		
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spatial::AllWorld:
+			return m_worldSpace.m_spatialData;
+			break;
+		}
+
+		return ObservedSubject::getSpatialData(p_observer, p_changedBits);
+	}
+		
+	// Get the world spatial data (without transform matrix)
+	const inline SpatialData &getWorldSpatialData()	const					{ return m_worldSpace.m_spatialData;					}
+	// Get the world spatial transform data
+	const inline SpatialTransformData &getWorldSpatialTransformData() const	{ return m_worldSpace;									}
+	// Get the world position
+	const inline Math::Vec3f &getWorldPosition() const						{ return m_worldSpace.m_spatialData.m_position;			}
+	// Get the world rotation in Euler
+	const inline Math::Vec3f getWorldRotationEuler() const					{ return m_worldSpace.m_spatialData.m_rotationEuler;	}
+	// Get the world rotation as a quaternion
+	const inline Math::Quaternion getWorldRotationQuat() const				{ return m_worldSpace.m_spatialData.m_rotationQuat;		}
+	// Get the world scale
+	const inline Math::Vec3f &getWorldScale() const							{ return m_worldSpace.m_spatialData.m_scale;			}
+
+	// Set the world spatial data (without transform matrix)
+	inline void setWorldSpatialData(const SpatialData &p_spatialData)					{ setUpdateNeeded(true); m_worldSpace.m_spatialData = p_spatialData;												}
+	// Set the world spatial transform data
+	inline void setWorldSpatialTransformData(const SpatialTransformData &p_spatialData)	{ m_worldSpace = p_spatialData;																						}
+	// Set the world position
+	inline void setWorldPosition(const Math::Vec3f &p_position)							{ setUpdateNeeded(true); m_worldSpace.m_spatialData.m_position = p_position;										}
+	// Set the world rotation in Euler
+	inline void setWorldRotation(const Math::Vec3f &p_rotationEuler)					{ setUpdateNeeded(true); m_updateQuaternion - true; m_worldSpace.m_spatialData.m_rotationEuler = p_rotationEuler;	}
+	// Set the world rotation as a quaternion
+	inline void setWorldRotation(const Math::Quaternion &p_rotationQuat)				{ setUpdateNeeded(true); m_worldSpace.m_spatialData.m_rotationQuat = p_rotationQuat;								}
+	// Set the world scale
+	inline void setWorldScale(const Math::Vec3f &p_scale)								{ setUpdateNeeded(true); m_worldSpace.m_spatialData.m_scale = p_scale;												}
+	
+	// Component functions
+	void addComponent(DirectionalLightDataSet &p_lightDataSet)
+	{
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Light);
+		m_lightComponent = new LightComponent(p_lightDataSet);
+	}
+	void addComponent(PointLightDataSet &p_lightDataSet)
+	{
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Light);
+		m_lightComponent = new LightComponent(p_lightDataSet);
+	}
+	void addComponent(SpotLightDataSet &p_lightDataSet)
+	{
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Light);
+		m_lightComponent = new LightComponent(p_lightDataSet);
+	}	
+	void addComponent(LightComponent *p_component)
+	{
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Light);
+		m_lightComponent = p_component;
+	}
+	void addComponent(ModelComponentData *p_component) 
+	{
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Model);
+		m_modelComponent = p_component; 
+	}
+	void addComponent(ShaderData *p_component) 
+	{ 
+		// Make sure that this component isn't assigned already
+		removeComponent(GraphicsComponentType::GraphicsComponentType_Shader);
+		m_shaderComponent = p_component; 
+	}
+	void removeComponent(const GraphicsComponentType p_compType)
+	{
+		switch(p_compType)
+		{
+		case GraphicsComponentType_Light:
+		{
+			if(m_lightComponent != nullptr)
+				delete m_lightComponent;
+			break;
+		}
+		case GraphicsComponentType_Model:
+		{
+			if(m_modelComponent != nullptr)
+				delete m_modelComponent;
+			break;
+		}
+		case GraphicsComponentType_Shader:
+		{
+			if(m_shaderComponent != nullptr)
+				delete m_shaderComponent;
+			break;
+		}
+		}
+	}
+	
+private:
+	//enum 
+	/*struct
+	{
+		union
+		{
+
+		} m_component;
+	} m_graphicsComponents;*/
+
+	/*struct
+	{
+		enum LightComponentType
+		{
+
+		};
+		union
+		{
+
+		} m_light;
+	} m_lightComponent;*/
+
+	// Pointers to multiple variables, intended as a way to modify component values directly (like a simplified observer pattern).
+	// If a pointer to a value is not nullptr, then that variable is acting as a listener its value should be updated
+	struct ProxyValues
+	{
+		ProxyValues()
+		{
+			m_color = nullptr;
+		}
+
+		Math::Vec3f *m_color;
+	};
+
+	ProxyValues m_proxyValues;
+	//std::vector<BaseGraphicsComponent&> m_components;
+
+	std::vector<MeshData> m_meshData;
+	SpatialTransformData m_worldSpace;
+
+	// Components
+	ModelComponentData *m_modelComponent;
+	ShaderData *m_shaderComponent;
+	LightComponent *m_lightComponent;
+	
+	// Flag data that needs to be updated
+	bool m_updateQuaternion;
+};
+
+/*class GraphicsObject : public SystemObject
+{
+public:
+	GraphicsObject(SystemScene *p_systemScene, const std::string &p_name)
+		: SystemObject(p_systemScene, p_name, Properties::Graphics), m_needsUpdate(true), m_affectedByLighting(true) { }
+	virtual ~GraphicsObject() { }
+		
+	BitMask getSystemType() { return Systems::Graphics; }
+
+	virtual BitMask getDesiredSystemChanges()	{ return Systems::Changes::Spatial::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::Spatial::Position)
+		{
+			m_baseObjectData.m_position = 
+				p_subject->getVec3(this, Systems::Changes::Spatial::Position) + m_baseObjectData.m_offsetPosition;
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::Rotation)
+		{
+			m_baseObjectData.m_rotation = 
+				p_subject->getVec3(this, Systems::Changes::Spatial::Rotation) + m_baseObjectData.m_offsetRotation;
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::Scale)
+		{
+			m_baseObjectData.m_scale = p_subject->getVec3(this, Systems::Changes::Spatial::Scale);
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Spatial::ModelMatrix)
+		{
+			m_baseObjectData.m_modelMat = p_subject->getMat4(this, Systems::Changes::Spatial::ModelMatrix);
+			m_needsUpdate = true;
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::Lighting)
+		{
+			m_affectedByLighting = p_subject->getBool(this, Systems::Changes::Graphics::Lighting);
+		}
+	}
+
+	// Has the object been already loaded to memory (RAM)?
+	const inline bool isLoadedToMemory() const { return m_loadedToMemory; }
+
+	// Has the object been already loaded to video memory (GPU VRAM)
+	const inline bool isLoadedToVideoMemory() const { return m_loadedToVideoMemory; }
+
+	// Getters
+	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const
+	{
+		switch(p_changedBits)
+		{
+		case Systems::Changes::Spatial::Position:
+			return m_baseObjectData.m_position;
+			break;
+		case Systems::Changes::Spatial::Rotation:
+			return m_baseObjectData.m_rotation;
+			break;
+		case Systems::Changes::Spatial::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; }
+
+	// Setters for spacial data
+	inline void setScale(const Math::Vec3f &p_scale)				{ m_baseObjectData.m_scale = p_scale;				}
+	inline void setPosition(const Math::Vec3f &p_position)			{ m_baseObjectData.m_position = p_position;			}
+	inline void setRotation(const Math::Vec3f &p_rotation)			{ m_baseObjectData.m_rotation = p_rotation;			}
+	inline void setOffsetPosition(const Math::Vec3f &p_position)	{ m_baseObjectData.m_offsetPosition = p_position;	}
+	inline void setOffsetRotation(const Math::Vec3f &p_rotation)	{ m_baseObjectData.m_offsetRotation = p_rotation;	}
+
+	// Setters for misc data
+	inline void setAffectedByLighting(const bool p_flag)		{ m_affectedByLighting = p_flag;					}
+	inline void setAlphaThreshold(const float p_value)			{ m_baseObjectData.m_alphaThreshold = p_value;		}
+	inline void setEmissiveThreshold(const float p_value)		{ m_baseObjectData.m_emissiveThreshold = p_value;	}
+	inline void setHeightScale(const float p_value)				{ m_baseObjectData.m_heightScale = p_value;			}
+	inline void setTextureTilingFactor(const float p_value)		{ m_baseObjectData.m_textureTilingFactor = p_value; }
+
+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;
+
+	// Does the object need to be updated after any of its data has been changed
+	bool m_needsUpdate;
+
+	// Spatial and misc data of an object
+	GraphicsData m_baseObjectData;
+};*/

+ 3 - 2
Praxis3D/Source/LenseFlarePass.h

@@ -19,6 +19,7 @@ public:
 		m_lensFlareParam.m_haloRadius = Config::graphicsVar().lens_flare_halo_radius;
 		m_lensFlareParam.m_haloThickness = Config::graphicsVar().lens_flare_halo_thickness;
 		m_lensFlareParam.m_haloThreshold = Config::graphicsVar().lens_flare_halo_threshold;
+		//TODO: dynamic aspect ratio
 		m_lensFlareParam.m_haloAspectRatio = 1600.0f / 900.0f;// Config::graphicsVar().lens_flare_aspect_ratio;
 	}
 
@@ -98,8 +99,8 @@ public:
 		glBindTexture(GL_TEXTURE_2D, m_lensFlareGhostGradient.getHandle());
 
 		// Perform various visual effects in the post process shader
-		m_renderer.queueForDrawing(m_lenseFlareShader->getShaderHandle(), m_lenseFlareShader->getUniformUpdater(), p_sceneObjects.m_camera->getBaseObjectData().m_modelMat);
-		m_renderer.passScreenSpaceDrawCommandsToBackend();
+		//m_renderer.queueForDrawing(m_lenseFlareShader->getShaderHandle(), m_lenseFlareShader->getUniformUpdater(), p_sceneObjects.m_camera->getBaseObjectData().m_modelMat);
+		//m_renderer.passScreenSpaceDrawCommandsToBackend();
 
 		p_renderPassData.setBlurInputMap(GeometryBuffer::GBufferDiffuse);
 		p_renderPassData.setBlurOutputMap(GeometryBuffer::GBufferDiffuse);

+ 18 - 0
Praxis3D/Source/LightComponent.cpp

@@ -0,0 +1,18 @@
+
+#include "GraphicsObject.h"
+#include "LightComponent.h"
+
+void DirectionalLightComponent::update(GraphicsObject &p_parentObject)
+{
+
+}
+
+void PointLightComponent::update(GraphicsObject &p_parentObject)
+{
+
+}
+
+void SpotLightComponent::update(GraphicsObject &p_parentObject)
+{
+
+}

+ 315 - 0
Praxis3D/Source/LightComponent.h

@@ -0,0 +1,315 @@
+#pragma once
+
+#include "BaseGraphicsComponent.h"
+#include "GraphicsDataSets.h"
+#include "ObserverBase.h"
+#include "System.h"
+
+class DirectionalLightComponent : public BaseGraphicsComponent
+{
+	friend class LightComponent;
+public:
+	void load(PropertySet &p_properties) final override
+	{ 
+		// Load property data
+		for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+		{
+			switch(p_properties[i].getPropertyID())
+			{
+			case Properties::Color:
+				m_lightDataSet.m_color = p_properties[i].getVec3f();
+				break;
+
+			case Properties::Direction:
+				// Need to normalize the light direction
+				m_lightDataSet.m_direction = Math::normalize(p_properties[i].getVec3f());
+				break;
+
+			case Properties::Intensity:
+				m_lightDataSet.m_intensity = p_properties[i].getFloat();
+				break;
+				
+			case Properties::WorldRotation:
+				m_lightDataSet.m_direction = p_properties[i].getVec3f();
+				break;
+			}
+		}
+	}
+
+	PropertySet export() final override
+	{ 
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+		
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::DirectionalLight);
+		propertySet.addProperty(Properties::Color, m_lightDataSet.m_color);
+		propertySet.addProperty(Properties::Direction, m_lightDataSet.m_direction);
+		propertySet.addProperty(Properties::Intensity, m_lightDataSet.m_intensity);
+		
+		return propertySet;
+	}
+
+	void update(GraphicsObject &p_parentObject);
+	
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
+	{
+		if(p_changeType & Systems::Changes::Graphics::Color)
+		{
+			m_lightDataSet.m_color = p_subject->getVec3(nullptr, Systems::Changes::Graphics::Color);
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::Intensity)
+		{
+			m_lightDataSet.m_intensity = p_subject->getFloat(nullptr, Systems::Changes::Graphics::Intensity);
+		}
+	}
+	
+	BitMask getDesiredSystemChanges() { return Systems::Changes::Graphics::Color | Systems::Changes::Graphics::Intensity; }
+
+private:
+	//DirectionalLightComponent() { }
+	DirectionalLightComponent(DirectionalLightDataSet &p_lightDataSet) : m_lightDataSet(p_lightDataSet) { }
+	~DirectionalLightComponent() { }
+
+	DirectionalLightDataSet m_lightDataSet;
+};
+
+class PointLightComponent : public BaseGraphicsComponent
+{
+	friend class LightComponent;
+public:
+	void load(PropertySet &p_properties) final override
+	{ 
+		// Load property data
+		for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+		{
+			switch(p_properties[i].getPropertyID())
+			{
+			case Properties::Color:
+				m_lightDataSet.m_color = p_properties[i].getVec3f();
+				break;
+
+			case Properties::Intensity:
+				m_lightDataSet.m_intensity = p_properties[i].getFloat();
+				break;
+				
+			case Properties::WorldPosition:
+				m_lightDataSet.m_position = p_properties[i].getVec3f();
+				break;
+			}
+		}
+	}
+
+	PropertySet export() final override
+	{ 		
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+		
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::PointLight);
+		propertySet.addProperty(Properties::Color, m_lightDataSet.m_color);
+		propertySet.addProperty(Properties::Intensity, m_lightDataSet.m_intensity);
+
+		return propertySet;
+	}
+	
+	void update(GraphicsObject &p_parentObject);
+
+	void changeOccurred(ObservedSubject* p_subject, BitMask p_changeType)
+	{
+		if(p_changeType & Systems::Changes::Graphics::Color)
+		{
+			m_lightDataSet.m_color = p_subject->getVec3(nullptr, Systems::Changes::Graphics::Color);
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::Intensity)
+		{
+			m_lightDataSet.m_intensity = p_subject->getFloat(nullptr, Systems::Changes::Graphics::Intensity);
+		}
+	}
+
+	BitMask getDesiredSystemChanges() { return Systems::Changes::Graphics::Color | Systems::Changes::Graphics::Intensity; }
+
+private:
+	//PointLightComponent() { }
+	PointLightComponent(PointLightDataSet &p_lightDataSet) : m_lightDataSet(p_lightDataSet) { }
+	~PointLightComponent() { }
+
+	PointLightDataSet m_lightDataSet;
+};
+
+class SpotLightComponent : public BaseGraphicsComponent
+{
+	friend class LightComponent;
+public:
+	void load(PropertySet &p_properties) final override
+	{ 
+		// Load property data
+		for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+		{
+			switch(p_properties[i].getPropertyID())
+			{
+			case Properties::CutoffAngle:
+				// Convert to radians
+				m_lightDataSet.m_cutoffAngle = cosf(Math::toRadian(p_properties[i].getFloat()));
+				break;
+
+			case Properties::Color:
+				m_lightDataSet.m_color = p_properties[i].getVec3f();
+				break;
+
+			case Properties::Intensity:
+				m_lightDataSet.m_intensity = p_properties[i].getFloat();
+				break;
+				
+			case Properties::WorldPosition:
+				m_lightDataSet.m_position = p_properties[i].getVec3f();
+				break;
+
+			case Properties::WorldRotation:
+				m_lightDataSet.m_direction = p_properties[i].getVec3f();
+				break;
+			}
+		}
+	}
+
+	PropertySet export() final override
+	{ 		
+		// Create the root property set
+		PropertySet propertySet(Properties::ArrayEntry);
+
+		// Add variables
+		propertySet.addProperty(Properties::Type, Properties::SpotLight);
+		// Make sure to revert the cutoff angle back to degrees
+		propertySet.addProperty(Properties::CutoffAngle, Math::toDegree(acosf(m_lightDataSet.m_cutoffAngle)));
+		propertySet.addProperty(Properties::Color, m_lightDataSet.m_color);
+		propertySet.addProperty(Properties::Direction, m_lightDataSet.m_direction);
+		propertySet.addProperty(Properties::Intensity, m_lightDataSet.m_intensity);
+
+		return propertySet;
+	}
+	
+	void update(GraphicsObject &p_parentObject);
+
+	void changeOccurred(ObservedSubject* p_subject, BitMask p_changeType)
+	{
+		if(p_changeType & Systems::Changes::Graphics::Color)
+		{
+			m_lightDataSet.m_color = p_subject->getVec3(nullptr, Systems::Changes::Graphics::Color);
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::Intensity)
+		{
+			m_lightDataSet.m_intensity = p_subject->getFloat(nullptr, Systems::Changes::Graphics::Intensity);
+		}
+
+		if(p_changeType & Systems::Changes::Graphics::CutoffAngle)
+		{
+			m_lightDataSet.m_cutoffAngle = p_subject->getFloat(nullptr, Systems::Changes::Graphics::CutoffAngle);
+		}
+	}
+
+	BitMask getDesiredSystemChanges() { return Systems::Changes::Graphics::Color | Systems::Changes::Graphics::Intensity | Systems::Changes::Graphics::CutoffAngle; }
+
+private:
+	//SpotLightComponent() { }
+	SpotLightComponent(SpotLightDataSet &p_lightDataSet) : m_lightDataSet(p_lightDataSet) { }
+	~SpotLightComponent() { }
+
+	SpotLightDataSet m_lightDataSet;
+};
+
+class LightComponent : public BaseGraphicsComponent
+{
+public:
+	enum LightComponentType
+	{
+		LightComponentType_directional,
+		LightComponentType_point,
+		LightComponentType_spot
+	};
+
+	LightComponent(DirectionalLightDataSet &p_lightDataSet)
+	{
+		m_lightComponentType = LightComponentType::LightComponentType_directional;
+		m_lightComponent.m_directional = p_lightDataSet;
+		m_currentLightComponent = &m_lightComponent.m_directional;
+	}
+	LightComponent(PointLightDataSet &p_lightDataSet)
+	{
+		m_lightComponentType = LightComponentType::LightComponentType_point;
+		m_lightComponent.m_point = p_lightDataSet;
+		m_currentLightComponent = &m_lightComponent.m_point;
+	}
+	LightComponent(SpotLightDataSet &p_lightDataSet)
+	{
+		m_lightComponentType = LightComponentType::LightComponentType_spot;
+		m_lightComponent.m_spot = p_lightDataSet;
+		m_currentLightComponent = &m_lightComponent.m_spot;
+	}
+
+	~LightComponent() 
+	{
+		switch(m_lightComponentType)
+		{
+		case LightComponent::LightComponentType_directional:
+			//delete m_lightComponent.m_directional;
+			break;
+		case LightComponent::LightComponentType_point:
+			//delete m_lightComponent.m_point;
+			break;
+		case LightComponent::LightComponentType_spot:
+			//delete m_lightComponent.m_spot;
+			break;
+		}
+	}
+
+	void load(PropertySet& p_properties) final override { m_currentLightComponent->load(p_properties); }
+
+	PropertySet export() final override { return m_currentLightComponent->export(); }
+
+	void update(GraphicsObject &p_parentObject) final override { m_currentLightComponent->update(p_parentObject); }
+
+	void updateSpatialData(SpatialTransformData &p_data)
+	{
+		// Update each type of light individually, as their spatial data is made up of different attributes
+		switch(m_lightComponentType)
+		{
+		case LightComponent::LightComponentType_directional:
+			m_lightComponent.m_directional.m_direction = p_data.m_spatialData.m_rotationEuler;
+			break;
+		case LightComponent::LightComponentType_point:
+			m_lightComponent.m_point.m_position = p_data.m_spatialData.m_position;
+			break;
+		case LightComponent::LightComponentType_spot:
+			m_lightComponent.m_spot.m_position = p_data.m_spatialData.m_position;
+			m_lightComponent.m_spot.m_direction = p_data.m_spatialData.m_rotationEuler;
+			break;
+		}
+	}
+
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) final override { m_currentLightComponent->changeOccurred(p_subject, p_changeType); }
+
+	BitMask getDesiredSystemChanges() final override { return m_currentLightComponent->getDesiredSystemChanges(); }
+
+	LightComponentType getLightType() { return m_lightComponentType; }
+
+	DirectionalLightDataSet *getDirectionalLight() { return (m_lightComponentType == LightComponentType::LightComponentType_directional) ? &m_lightComponent.m_directional : nullptr; }
+	PointLightDataSet *getSpotLight() { return (m_lightComponentType == LightComponentType::LightComponentType_point) ? &m_lightComponent.m_point : nullptr; }
+	SpotLightDataSet *getPointLight() { return (m_lightComponentType == LightComponentType::LightComponentType_spot) ? &m_lightComponent.m_spot : nullptr; }
+
+private:
+	union m_lightComponent
+	{
+		m_lightComponent() { }
+		~m_lightComponent() { }
+
+		DirectionalLightDataSet m_directional;
+		PointLightDataSet m_point;
+		SpotLightDataSet m_spot;
+	} m_lightComponent;
+
+	LightComponentType m_lightComponentType;
+	BaseGraphicsComponent *m_currentLightComponent;
+};

+ 7 - 9
Praxis3D/Source/LoaderBase.h

@@ -23,7 +23,7 @@ public:
 		{
 			m_loadingToMemoryError = ErrorCode::Failure;
 			m_loadedToMemory = false;
-			m_loadedToVideoMemory = false;
+			m_beingLoaded = false;
 			m_refCounter = 0;
 		}
 		virtual ~UniqueObject()
@@ -47,25 +47,23 @@ public:
 		}
 
 		// Setters
-		inline void setLoadedToMemory(bool p_loaded)		{ m_loadedToMemory = p_loaded;		}
-		inline void setLoadedToVideoMemory(bool p_loaded)	{ m_loadedToVideoMemory = p_loaded; }
+		inline void setLoaded(bool p_loaded)				{ m_loadedToMemory = p_loaded;		}
 		inline void setUniqueID(unsigned int p_uniqueID)	{ m_uniqueID = p_uniqueID;			}
 
 		// Getters
-		inline const bool isLoadedToMemory()		{ return m_loadedToMemory;		}
-		inline const bool isLoadedToVideoMemory()	{ return m_loadedToVideoMemory; }
+		inline const bool isLoaded()				{ return m_loadedToMemory;		}
 		inline const unsigned int getUniqueID()		{ return m_uniqueID;			}
 		inline std::string getFilename()			{ return m_filename;			}
 
 		// Equality operator; compares filenames
 		inline bool operator==(std::string p_string) { return m_filename == p_string; }
 
-		inline ErrorCode unloadMemory()		 { return static_cast<TObject*>(this)->unloadMemory();		}
-		inline ErrorCode unloadVideoMemory() { return ErrorCode::Success; /*return static_cast<TObject*>(this)->unloadVideoMemory();*/ }
+		inline ErrorCode unload()		 { return static_cast<TObject*>(this)->unloadMemory();		}
 
 	protected:
-		bool m_loadedToMemory;
-		bool m_loadedToVideoMemory;
+		inline const bool isBeingLoaded() { return m_isBeingLoaded; }
+
+		std::atomic_bool m_loadedToMemory;
 
 		ErrorCode m_loadingToMemoryError;
 		std::string m_filename;

+ 26 - 15
Praxis3D/Source/Math.cpp

@@ -4,15 +4,26 @@ namespace Math
 {
 	Vec2f::Vec2f(const Vec3f p_vec)
 	{
-		x = p_vec.x;	y = p_vec.y;
+		x = p_vec.x;
+		y = p_vec.y;
 	}
 	Vec2f::Vec2f(const Vec4f p_vec)
 	{
-		x = p_vec.x;	y = p_vec.y;
+		x = p_vec.x;
+		y = p_vec.y;
 	}
 	Vec3f::Vec3f(const Vec4f p_vec)
 	{
-		x = p_vec.x;	y = p_vec.y;	z = p_vec.z;
+		x = p_vec.x;
+		y = p_vec.y;
+		z = p_vec.z;
+	}
+	Vec4f::Vec4f(const Quaternion p_quaterion)
+	{
+		x = p_quaterion.x;	
+		y = p_quaterion.y;	
+		z = p_quaterion.z;	
+		w = p_quaterion.w;
 	}
 
 	void Vec3f::rotate(float p_angle, const Vec3f& p_axis)
@@ -24,9 +35,9 @@ namespace Math
 								p_axis.z * sinHalfAngle,
 								cosf(toRadian(p_angle / 2)));
 
-		Quaternion conjugateQuat = rotationQuat.getConjugated();
+		const Quaternion conjugateQuat = rotationQuat.getConjugated();
 		//ConjugateQ.normalize();
-		Quaternion ret = rotationQuat * (*this) * conjugateQuat;
+		const Quaternion ret = rotationQuat * (*this) * conjugateQuat;
 
 		x = ret.x;
 		y = ret.y;
@@ -120,10 +131,10 @@ namespace Math
 	}
 	void Mat4f::perspective(const float p_FOV, const int p_screenWidth, const int p_screenHeight, const float p_zNear, const float p_zFar)
 	{
-		float	radFOV = toRadian(p_FOV),
-				height = cosf(0.5f * radFOV) / sinf(0.5f * radFOV),
-				width = height * p_screenHeight / p_screenWidth,
-				zRange = p_zFar - p_zNear;
+		const float	radFOV = toRadian(p_FOV),
+					height = cosf(0.5f * radFOV) / sinf(0.5f * radFOV),
+					width = height * p_screenHeight / p_screenWidth,
+					zRange = p_zFar - p_zNear;
 				
 		m[0] = width;					 m[4] = 0.0f;			m[8] = 0.0f;								m[12] = 0.0f;
 		m[1] = 0.0f;					 m[5] = height;			m[9] = 0.0f;								m[13] = 0.0f;
@@ -132,12 +143,12 @@ namespace Math
 	}
 	void Mat4f::perspective(const float p_FOV, const float p_aspectRatio, const float p_zNear, const float p_zFar)
 	{
-		float	range = tanf(toRadian(p_FOV / 2.0f)) * p_zNear,
-				left = -range * p_aspectRatio,
-				right = range * p_aspectRatio,
-				bottom = -range,
-				top = range,
-				zRange = p_zFar - p_zNear;
+		const float	range = tanf(toRadian(p_FOV / 2.0f)) * p_zNear,
+					left = -range * p_aspectRatio,
+					right = range * p_aspectRatio,
+					bottom = -range,
+					top = range,
+					zRange = p_zFar - p_zNear;
 
 		m[0] = (2.0f * p_zNear) / (right - left);	 m[4] = 0.0f;									m[8] = 0.0f;								m[12] = 0.0f;
 		m[1] = 0.0f;								 m[5] = (2.0f * p_zNear) / (top - bottom);		m[9] = 0.0f;								m[13] = 0.0f;

+ 31 - 9
Praxis3D/Source/Math.h

@@ -24,6 +24,7 @@ namespace Math
 {
 	struct Vec3f;
 	struct Vec4f;
+	struct Quaternion;
 
 	struct Vec2i
 	{
@@ -302,6 +303,7 @@ namespace Math
 		{
 			x = vector_arg.x;	y = vector_arg.y;	z = p_floatZ;	w = p_floatW;
 		}
+		Vec4f(const Quaternion p_quaterion);
 	};
 
 	const inline Vec2f operator+(const Vec2f& p_left, const Vec2f& p_right)	{ return Vec2f(p_left.x + p_right.x, p_left.y + p_right.y); }
@@ -506,16 +508,18 @@ namespace Math
 		inline Vec3f getRotationY() const { return Vec3f(m[1], m[5], m[9]); }
 		inline Vec3f getRotationZ() const { return Vec3f(m[2], m[6], m[10]); }
 		inline Vec3f getPosition()  const { return Vec3f(m[3], m[7], m[11]); }
+		inline Vec3f getScale()		const { return Vec3f(m[12], m[13], m[14]); }
 
-		inline void setRotationX(Vec3f &p_rotX) { m[0] = p_rotX.x; m[4] = p_rotX.y; m[8] = p_rotX.z; }
-		inline void setRotationY(Vec3f &p_rotY) { m[1] = p_rotY.x; m[5] = p_rotY.y; m[9] = p_rotY.z; }
-		inline void setRotationZ(Vec3f &p_rotZ) { m[2] = p_rotZ.x; m[6] = p_rotZ.y; m[10] = p_rotZ.z; }
-		inline void setPosition(Vec3f &p_pos) { m[3] = p_pos.x;  m[7] = p_pos.y;  m[11] = p_pos.z; }
+		inline void setRotationX(const Vec3f &p_rotX)	{ m[0] = p_rotX.x; m[4] = p_rotX.y; m[8] = p_rotX.z;	}
+		inline void setRotationY(const Vec3f &p_rotY)	{ m[1] = p_rotY.x; m[5] = p_rotY.y; m[9] = p_rotY.z;	}
+		inline void setRotationZ(const Vec3f &p_rotZ)	{ m[2] = p_rotZ.x; m[6] = p_rotZ.y; m[10] = p_rotZ.z;	}
+		inline void setPosition(const Vec3f &p_pos)		{ m[3] = p_pos.x;  m[7] = p_pos.y;  m[11] = p_pos.z;	}
+		inline void setScale(const Vec3f &p_scl)		{ m[12] = p_scl.x;  m[13] = p_scl.y;  m[14] = p_scl.z;	}
 
-		inline void setRotationX(const Vec4f &p_rotX) { m[0] = p_rotX.x; m[4] = p_rotX.y; m[8] = p_rotX.z; m[12] = p_rotX.w; }
-		inline void setRotationY(const Vec4f &p_rotY) { m[1] = p_rotY.x; m[5] = p_rotY.y; m[9] = p_rotY.z; m[13] = p_rotY.w; }
-		inline void setRotationZ(const Vec4f &p_rotZ) { m[2] = p_rotZ.x; m[6] = p_rotZ.y; m[10] = p_rotZ.z; m[14] = p_rotZ.w; }
-		inline void setPosition(const Vec4f &p_pos) { m[3] = p_pos.x;  m[7] = p_pos.y;  m[11] = p_pos.z;  m[15] = p_pos.w; }
+		inline void setRotationX(const Vec4f &p_rotX)	{ m[0] = p_rotX.x; m[4] = p_rotX.y; m[8] = p_rotX.z; m[12] = p_rotX.w;	}
+		inline void setRotationY(const Vec4f &p_rotY)	{ m[1] = p_rotY.x; m[5] = p_rotY.y; m[9] = p_rotY.z; m[13] = p_rotY.w;	}
+		inline void setRotationZ(const Vec4f &p_rotZ)	{ m[2] = p_rotZ.x; m[6] = p_rotZ.y; m[10] = p_rotZ.z; m[14] = p_rotZ.w; }
+		inline void setPosition(const Vec4f &p_pos)		{ m[3] = p_pos.x;  m[7] = p_pos.y;  m[11] = p_pos.z;  m[15] = p_pos.w;	}
 
 		inline void Mat4f::identity()
 		{
@@ -543,7 +547,7 @@ namespace Math
 			m[0] *= p_scale.x; m[4] *= p_scale.y; m[8] *= p_scale.z;
 			m[1] *= p_scale.x; m[5] *= p_scale.y; m[9] *= p_scale.z;
 			m[2] *= p_scale.x; m[6] *= p_scale.y; m[10] *= p_scale.z;
-			m[3] *= p_scale.x; m[7] *= p_scale.y;
+			m[3] *= p_scale.x; m[7] *= p_scale.y; 
 		}
 
 		void rotate(const Vec3f& p_vec3f);
@@ -604,7 +608,25 @@ namespace Math
 						p_mat.m[2], p_mat.m[6], p_mat.m[10],p_mat.m[14],
 						p_mat.m[3], p_mat.m[7], p_mat.m[11],p_mat.m[15]);
 	}
+	const inline Mat4f createTransformMat(const Vec3f &p_position, const Vec3f &p_rotationEuler, const Vec3f &p_scale)
+	{
+		// Declare a transform matrix
+		Mat4f transformMat;
+		transformMat.identity();
+
+		// Set the position
+		transformMat.translate(p_position);
+
+		// Set the rotation
+		transformMat.rotate(Math::Vec3f(0.0f, p_rotationEuler.y, 0.0f));
+		transformMat.rotate(Math::Vec3f(p_rotationEuler.x, 0.0f, 0.0f));
+		transformMat.rotate(Math::Vec3f(0.0f, 0.0f, -p_rotationEuler.z));
 
+		// Set the scale
+		transformMat.scale(p_scale);
+
+		return transformMat;
+	}
 
 	inline Vec3f toRadian(const Vec3f p_vec3)
 	{

+ 212 - 0
Praxis3D/Source/ModelComponent.h

@@ -0,0 +1,212 @@
+#pragma once
+
+#include "BaseGraphicsComponent.h"
+#include "GraphicsDataSets.h"
+
+class ModelComponent : public BaseGraphicsComponent
+{
+public:
+	ModelComponent() { }
+	~ModelComponent() { }
+
+	void load(PropertySet &p_properties) final override
+	{ 
+		// Check if models node is present and the component hasn't been loaded already
+		if(p_properties && empty())
+		{
+			// Loop over each model entry in the node
+			for(decltype(p_properties.getNumProperties()) iModel = 0, numModels = p_properties.getNumProperties(); iModel < numModels; iModel++)
+			{
+				// Get model filename
+				auto modelName = p_properties.getPropertySet(iModel).getPropertyByID(Properties::Filename).getString();
+
+				// Add a new model data entry, and get a reference to it
+				m_modelData.m_modelData.push_back(ModelData(Loaders::model().load(modelName, false)));
+				auto &newModelData = m_modelData.m_modelData.back();
+
+				// Load the model to memory, to be able to access all of its meshes
+				auto modelLoadError = newModelData.m_model.loadToMemory();
+
+				if(modelLoadError == ErrorCode::Success)
+				{
+					// Set the component as not being empty anymore, since a model has been loaded successfully
+					setEmpty(false);
+
+					// Get the meshes array
+					auto meshesInModelArray = newModelData.m_model.getMeshArray();
+
+					// Get the meshes array
+					auto meshesProperty = p_properties.getPropertySet(iModel).getPropertySetByID(Properties::Meshes);
+
+					// Check if the meshes array node is present;
+					// If it is present, only add the meshes included in the meshes node
+					// If it is not present, add all the meshes included in the model
+					if(meshesProperty)
+					{
+						// Loop over each mesh entry in the model node
+						for(decltype(meshesProperty.getNumProperties()) iMesh = 0, numMeshes = meshesProperty.getNumProperties(); iMesh < numMeshes; iMesh++)
+						{
+							// Try to get the mesh index property node and check if it is present
+							auto meshIndexProperty = meshesProperty.getPropertySet(iMesh).getPropertyByID(Properties::Index);
+							if(meshIndexProperty)
+							{
+								// Get the mesh index, check if it is valid and within the range of mesh array that was loaded from the model
+								const int meshDataIndex = meshIndexProperty.getInt();
+								if(meshDataIndex > 0 && meshDataIndex < meshesInModelArray.size())
+								{
+									// Get material properties
+									auto materialsProperty = meshesProperty.getPropertySet(iMesh).getPropertySetByID(Properties::Materials);
+
+									// Define material data and material properties
+									MaterialData materials[MaterialType::MaterialType_NumOfTypes];
+									PropertySet materialProperties[MaterialType::MaterialType_NumOfTypes] =
+									{
+										materialsProperty.getPropertySetByID(Properties::Diffuse),
+										materialsProperty.getPropertySetByID(Properties::Normal),
+										materialsProperty.getPropertySetByID(Properties::Emissive),
+										materialsProperty.getPropertySetByID(Properties::RMHAO)
+									};
+
+									// Go over each material type
+									for(unsigned int iMatType = 0; iMatType < MaterialType::MaterialType_NumOfTypes; iMatType++)
+									{
+										// Check if an entry for the current material type was present within the properties
+										if(materialProperties[iMatType])
+										{
+											// Load the material data
+											materials[iMatType] = loadMaterialData(materialProperties[iMatType], newModelData.m_model.getMaterialArrays(), static_cast<MaterialType>(iMatType), meshDataIndex);
+										}
+									}
+
+									newModelData.m_meshes.push_back(MeshData(meshesInModelArray[iMesh], materials));
+								}
+							}
+						}
+					}
+					else
+					{
+						// Get the material arrays that were loaded from the model file
+						auto &materialArrayFromModel = newModelData.m_model.getMaterialArrays();
+
+						// Iterate over every mesh in the model
+						for(decltype(meshesInModelArray.size()) iMesh = 0, numMeshes = meshesInModelArray.size(); iMesh < numMeshes; iMesh++)
+						{
+							// Define material data and material properties
+							MaterialData materials[MaterialType::MaterialType_NumOfTypes];
+
+							// Go over each mesh in the model
+							if(iMesh > materialArrayFromModel.m_numMaterials)
+							{
+								// Go over each material type
+								for(unsigned int iMatType = 0; iMatType < MaterialType::MaterialType_NumOfTypes; iMatType++)
+								{
+									// Get the texture filename and load it to memory
+									auto textureFromModel = Loaders::texture2D().load(materialArrayFromModel.m_materials[iMatType][iMesh].m_filename, static_cast<MaterialType>(iMatType), false);
+									auto materialLoadError = textureFromModel.loadToMemory();
+
+									// Check if the texture was loaded successfully
+									if(materialLoadError == ErrorCode::Success)
+									{
+										materials[MaterialType::MaterialType_Diffuse].m_texture = textureFromModel;
+									}
+									else
+									{
+										ErrHandlerLoc::get().log(materialLoadError, ErrorSource::Source_Renderer);
+									}
+								}
+
+								// Add the data for this mesh. Include materials loaded from the model itself, if they were present, otherwise, include default textures instead
+								newModelData.m_meshes.push_back(MeshData(meshesInModelArray[iMesh], materials));
+							}
+						}
+					}
+				}
+				else
+				{
+					ErrHandlerLoc::get().log(modelLoadError, ErrorSource::Source_Renderer);
+				}
+			}
+		}
+		
+		// Set the component as loaded, because the load function was called
+		setLoaded(true);
+	}
+
+	PropertySet export() final override
+	{ 
+		return PropertySet(); 
+	}
+
+private:
+	inline MaterialData loadMaterialData(PropertySet &p_materialProperty, Model::MaterialArrays &p_materialArraysFromModel, MaterialType p_materialType, std::size_t p_meshIndex)
+	{
+		// Declare the material data that is to be returned and a flag showing whether the material data was loaded successfully
+		MaterialData newMaterialData;
+		bool materialWasLoaded = false;
+
+		// Try to load the material from the filename retrieved from properties
+		if(p_materialProperty)
+		{
+			// Get texture filename property, check if it is valid
+			auto filenameProperty = p_materialProperty.getPropertyByID(Properties::Filename);
+			if(filenameProperty.isVariableTypeString())
+			{
+				// Get texture filename string, check if it is valid
+				auto filename = filenameProperty.getString();
+				if(!filename.empty())
+				{
+					// Get the texture and load it to memory
+					auto materialHandle = Loaders::texture2D().load(filename, p_materialType, false);
+					auto materialLoadError = materialHandle.loadToMemory();
+
+					// Check if the texture was loaded successfully
+					if(materialLoadError == ErrorCode::Success)
+					{
+						newMaterialData.m_texture = materialHandle;
+						materialWasLoaded = true;
+					}
+					else
+					{
+						ErrHandlerLoc::get().log(materialLoadError, ErrorSource::Source_Renderer);
+					}
+				}
+			}
+		}
+
+		// Try to load the material from the filename retrieved from the model
+		if(!materialWasLoaded)
+		{
+			// Check if there are enough materials, and if the material isn't default
+			if(p_materialArraysFromModel.m_numMaterials > p_meshIndex 
+				&& !p_materialArraysFromModel.m_materials[p_materialType][p_meshIndex].isEmpty() 
+				&& !p_materialArraysFromModel.m_materials[p_materialType][p_meshIndex].isDefaultMaterial())
+			{
+				// Get the texture and load it to memory
+				auto materialHandle = Loaders::texture2D().load(p_materialArraysFromModel.m_materials[p_materialType][p_meshIndex].m_filename, p_materialType, false);
+				auto materialLoadError = materialHandle.loadToMemory();
+				
+				// Check if the texture was loaded successfully
+				if(materialLoadError == ErrorCode::Success)
+				{
+					newMaterialData.m_texture = materialHandle;
+					materialWasLoaded = true;
+				}
+				else
+				{
+					ErrHandlerLoc::get().log(materialLoadError, ErrorSource::Source_Renderer);
+				}
+			}
+		}
+		
+		// All attempts to load the material were unsuccessful, so load a defaul material
+		if(!materialWasLoaded)
+		{
+			newMaterialData.m_texture = Loaders::texture2D().getDefaultTexture(p_materialType);
+		}
+
+		// Return the newly loaded material data
+		return newMaterialData;
+	}
+
+	ModelComponentData m_modelData;
+};

+ 0 - 19
Praxis3D/Source/ModelGraphicsObjects.h

@@ -128,25 +128,6 @@ public:
 		setLoadedToMemory(true);
 	}
 
-	// Loads model and materials from memory to video memory; should only be called by renderer thread
-	ErrorCode loadToVideoMemory()
-	{
-		// Error code used for checking the success of loading; it is not returned
-		ErrorCode error = ErrorCode::Success;
-
-		// Load the model to video memory; log an error if it occurs
-		//if(!ErrHandlerLoc::get().ifSuccessful(m_objectData.m_model.loadToVideoMemory(), error))
-		//	ErrHandlerLoc::get().log(error);
-
-		// Iterate over all materials and load them to video memory; log an error if loading failed
-		for(decltype(m_objectData.m_numMaterials) i = 0; i < m_objectData.m_numMaterials; i++)
-			for(int matType = 0; matType < MaterialType_NumOfTypes; matType++)
-				if(!ErrHandlerLoc::get().ifSuccessful(m_objectData.m_materials[matType][i].loadToVideoMemory(), error))
-					ErrHandlerLoc::get().log(error);
-
-		return ErrorCode::Success;
-	}
-
 	// Exports all the data of the object as a PropertySet
 	PropertySet exportObject()
 	{

+ 3 - 3
Praxis3D/Source/ModelLoader.cpp

@@ -19,12 +19,12 @@ ErrorCode Model::loadToMemory()
 	// If the model is not currently already being loaded in another thread
 	if(!m_isBeingLoaded)
 	{
-		// Lock calls from other treads
-		SpinWait::Lock lock(m_mutex);
-
 		// Make sure another thread doesn't start another instance of loading
 		m_isBeingLoaded = true;
 
+		// Lock calls from other treads
+		SpinWait::Lock lock(m_mutex);
+
 		// To not cause crashes from outside code, since meshes will be modified when loading
 		//m_currentNumMeshes = 0;
 

+ 6 - 19
Praxis3D/Source/ModelLoader.h

@@ -112,21 +112,8 @@ public:
 	ErrorCode loadTextures(aiTexture **p_assimpTextures, size_t p_numTextures);
 
 	inline MaterialArrays &getMaterialArrays() { return m_materials; }
+
 	// Returns an array of pointers to buffer data
-	/*const inline void **getData()
-	{
-		void **data = nullptr;
-		//const void *data[ModelBuffer_Index + 1];
-
-		data[ModelBuffer_Position]		= &m_positions[0];
-		data[ModelBuffer_Normal]		= &m_normals[0];
-		data[ModelBuffer_TexCoord]		= &m_texCoords[0];
-		data[ModelBuffer_Tangents]		= &m_tangents[0];
-		data[ModelBuffer_Bitangents]	= &m_bitangents[0];
-		data[ModelBuffer_Index]			= &m_indices[0];
-
-		return (const void**)data;
-	}*/
 	const inline void **getData()
 	{
 		const void **data = new const void*[ModelBuffer_Index + 1];
@@ -182,7 +169,7 @@ public:
 
 	// Wrapper class for a model, to provide data needed for rendering, without exposing the
 	// private variables. Two ways of iterating over data: using subscription operator, or checking
-	// nextMeshPresent with calling operator++ in between and using getters to retrieve data.
+	// nextMeshPresent with calling operator++ in-between and using getters to retrieve data.
 	class MeshIterator
 	{
 		friend class ModelHandle;
@@ -237,15 +224,15 @@ public:
 		{
 			ErrorCode returnError = ErrorCode::Success;
 
-			// If it's not loaded to memory already, call load
-			if(!m_model->isLoadedToMemory())
+			// If it's not loaded to memory already and is not currently being loaded, call load
+			if(!m_model->isLoaded() && !m_model->isBeingLoaded())
 			{
 				returnError = m_model->loadToMemory();
 
 				// If the setLoadedToMemory flag is true and loading was successful, set the flag
 				if(p_setLoadedToMemoryFlag)
 					if(returnError == ErrorCode::Success)
-						m_model->setLoadedToMemory(true);
+						m_model->setLoaded(true);
 			}
 
 			return returnError;
@@ -265,7 +252,7 @@ public:
 		
 		// Getters
 		inline Model::MaterialArrays &getMaterialArrays() const		{ return m_model->getMaterialArrays();	}
-		inline const std::vector<Model::Mesh> getMeshArray() const	{ return m_model->m_meshPool;			}
+		inline const std::vector<Model::Mesh> &getMeshArray() const	{ return m_model->m_meshPool;			}
 		inline size_t getNumMeshes() const							{ return m_model->m_numMeshes;			}
 		inline MeshIterator getMeshIterator() const					{ return MeshIterator(*m_model);		}
 		inline const size_t getMeshSize() const						{ return m_model->m_numMeshes;			}

+ 6 - 0
Praxis3D/Source/ObjectDirectory.cpp

@@ -0,0 +1,6 @@
+#include "ObjectDirectory.h"
+
+tbb::concurrent_vector<const SystemObject*> ObjectDirectory::m_systemObjectPool;
+tbb::concurrent_queue<ObjectDirectory::ObjectListIndexType> ObjectDirectory::m_emptyObjectListElements;
+
+NullSystemObject ObjectDirectory::m_nullObject;

+ 78 - 20
Praxis3D/Source/ObjectDirectory.h

@@ -1,8 +1,11 @@
 #pragma once
 
 #include <string>
+#include <tbb/concurrent_queue.h>
+#include <tbb/concurrent_vector.h>
 #include <vector>
 
+#include "NullSystemObjects.h"
 #include "System.h"
 
 // A directory of all system objects, used to quickly get the name or the object itself.
@@ -14,51 +17,87 @@ public:
 	// Initialize the service locator
 	inline static ErrorCode init() 
 	{
-		m_nullName = "Null Object";
+		// Set the object pool size; not thread-safe
+		m_systemObjectPool.reserve(Config::engineVar().object_directory_init_pool_size);
 
 		// Reserve the first ID for null object
-		registerObject(nullptr);
+		registerObject(m_nullObject);
 
 		return ErrorCode::Success; 
 	}
 
 	// Register a system object, so it gets added to the directory. Returns a unique object ID, that can be used to retrieve the object later.
-	inline static size_t registerObject(const SystemObject *p_systemObject)
+	inline static std::size_t registerObject(const SystemObject &p_systemObject)
 	{
-		auto uniqueID = m_systemObjectList.size();
+		// Index at which the object will be stored
+		ObjectListIndexType objectIndex = 0;
 
-		m_systemObjectList.push_back(p_systemObject);
+		// Try to get an index of an empty element in the object list and check if the element is empty (null)
+		if(m_emptyObjectListElements.try_pop(objectIndex) && m_systemObjectPool[objectIndex] == nullptr)
+		{
+			// Use the index that was acquired from the empty element list
+			m_systemObjectPool[objectIndex] = &p_systemObject;
+		}
+		else
+		{
+			// There was no existing empty slot in the object list, so create a new slot
+			// Add the object to the list, get an iterator and calculate the index at which the object was inserted
+			objectIndex = (m_systemObjectPool.push_back(&p_systemObject) - m_systemObjectPool.begin());
+		}
 
-		return uniqueID;
+		// Cast the object index (int) to size_t (unsigned int)
+		return static_cast<std::size_t>(objectIndex);
 	}
-
-	// Returns the name of an object found by its ID. If the ID is incorrect or the object is not found, returns a null name
-	const inline std::string &getObjectName(size_t p_objectID)
+	
+	// Unregister object by the object ID
+	inline static void unregisterObject(const std::size_t p_objectID)
 	{
-		if(p_objectID < m_systemObjectList.size())
+		// Check if the object ID is within bounds
+		if(p_objectID < m_systemObjectPool.size())
 		{
-			const SystemObject *object = m_systemObjectList[p_objectID];
+			// Check whether the slot is not empty already
+			if(m_systemObjectPool[p_objectID] != nullptr)
+			{
+				// Make the slot empty
+				m_systemObjectPool[p_objectID] == nullptr;
 
-			if(object != nullptr)
-				return object->getName();
+				// Add the slot to the empty element pool, so it can be used for a new object
+				m_emptyObjectListElements.push(p_objectID);
+			}
 		}
+	}
 
-		return m_nullName;
+	// Unregister object by the object name
+	inline static void unregisterObject(const std::string &p_objectName)
+	{
+		unregisterObject(getSystemObjectID(p_objectName));
+	}
+
+	// Unregister object by the system object 
+	inline static void unregisterObject(const SystemObject &p_systemObject)
+	{
+		unregisterObject(p_systemObject.getObjectID());
+	}
+
+	// Returns the name of an object found by its ID. If the ID is incorrect or the object is not found, returns a null name
+	const inline static std::string &getSystemObjectName(const std::size_t p_objectID)
+	{
+		return getSystemObject(p_objectID)->getName();
 	}
 
 	// Returns object ID found by its name.
 	// This is slow, as it traverses through all the objects and compares names.
 	// Note: to find the exact object, its name has to be unique.
-	inline static size_t getSystemObjectID(const std::string &p_objectName)
+	inline static std::size_t getSystemObjectID(const std::string &p_objectName)
 	{
 		// Check if the name isn't empty
 		if(p_objectName.size() > 0)
 		{
 			// Go through all the objects
-			for(decltype(m_systemObjectList.size()) i = 0, size = m_systemObjectList.size(); i < size; i++)
+			for(decltype(m_systemObjectPool.size()) i = 0, size = m_systemObjectPool.size(); i < size; i++)
 			{
 				// Check if the object is valid and its name matches
-				if(m_systemObjectList[i] != nullptr && m_systemObjectList[i]->getName() == p_objectName)
+				if(m_systemObjectPool[i] != nullptr && m_systemObjectPool[i]->getName() == p_objectName)
 					return i;
 			}
 		}
@@ -67,9 +106,28 @@ public:
 	}
 
 private:
+	const inline static SystemObject *getSystemObject(const std::size_t p_objectID)
+	{	
+		// Default the return object to null system object
+		const SystemObject *returnObject = &m_nullObject;
+
+		// Check if the object ID is within bounds and assign the return object if it is
+		if(p_objectID < m_systemObjectPool.size())
+			returnObject = m_systemObjectPool[p_objectID];
+
+		// Return the object; "nullptr" if it wasn't found
+		return returnObject;
+	}
+
+	// System object directory index variable type
+	typedef tbb::concurrent_vector<const SystemObject *>::size_type ObjectListIndexType;
+	
 	// System object directory
-	static std::vector<const SystemObject*> m_systemObjectList;
+	static tbb::concurrent_vector<const SystemObject*> m_systemObjectPool;
 
-	// Null name that gets returned if no object is found
-	static std::string m_nullName;
+	// A list of all empty slots in the system object directory
+	static tbb::concurrent_queue<ObjectListIndexType> m_emptyObjectListElements;
+	
+	// Null system object that gets returned if no object is found
+	static NullSystemObject m_nullObject;
 };

+ 2 - 2
Praxis3D/Source/ObjectPool.h

@@ -137,7 +137,7 @@ public:
 			// Increment the total number of allocated objects
 			m_numAllocatedObjects++;
 
-			//memcpy(newObject->m_object, &p_object, sizeof(T_Object));
+			memcpy(newObject->m_object, &p_object, sizeof(T_Object));
 		}
 		else
 			returnError = ErrorCode::ObjectPool_full;
@@ -184,7 +184,7 @@ public:
 			// If the object is allocated
 			if(m_objectPool[p_index].m_allocated == true)
 			{
-				// Set is as a new first available and deallocate it
+				// Set it as a new first available and deallocate it
 				m_objectPool[p_index].setNext(m_firstAvailable);
 				m_firstAvailable = &m_objectPool[p_index];
 				m_objectPool[p_index].m_allocated = false;

+ 353 - 0
Praxis3D/Source/ObjectRegister.h

@@ -0,0 +1,353 @@
+#pragma once
+
+constexpr auto OBJECT_REGISTER_FREED_IDS_RESERVE_DIVIDER = 10;
+constexpr auto OBJECT_REGISTER_RESIZE_ADDED_OVERHEAD_MULTIPLIER = 1.5;
+
+#include <tbb/concurrent_queue.h>
+#include <tbb/concurrent_vector.h>
+#include <vector>
+
+// An object directory, used for getting unique IDs to all registered objects.
+// Also contains a list of registered object pointers, an object can be retrieved based on its ID.
+// Implementation is NOT thread-safe.
+template <class T_Object>
+class ObjectRegister
+{
+public:
+	ObjectRegister() { }
+	~ObjectRegister() { }
+
+	// Reserve internal space for given amount of objects to speed up the object registering
+	void reserveSize(const std::size_t p_size)
+	{
+		m_objectPool.reserve(p_size);
+		m_emptyObjectIDPool.reserve(p_size / OBJECT_REGISTER_FREED_IDS_RESERVE_DIVIDER);
+	}
+
+	// Returns a unique ID
+	inline std::size_t registerObject(T_Object p_object)
+	{
+		std::size_t objectID = 0;
+		
+		// Check if there is an index of an unregistered element in the object pool
+		if(!m_emptyObjectIDPool.empty())
+		{
+			// Get an index of an unregistered object
+			objectID = m_emptyObjectIDPool.back();
+			m_emptyObjectIDPool.pop_back();
+
+			// If the retrieved object ID is empty, use it for the new given object
+			// If it is not empty, recursively call registerObject to get a new object ID
+			if(m_objectPool[objectID] == nullptr)
+			{
+				// Insert the given object into an empty slot
+				m_objectPool[objectID] = p_object;
+			}
+			else
+			{
+				// Get a new object ID
+				objectID = registerObject(p_object);
+			}
+		}
+		else
+		{
+			// There was no existing unregistered ID in the object pool, so create a new ID
+			// Add the object to the pool, get an iterator and calculate the index at which the object was inserted
+			m_objectPool.push_back(p_object);
+			objectID = m_objectPool.size() - 1;
+		}
+
+		return objectID;
+	}
+
+	// Tries to register an object with the given ID
+	// Returns the ID object gets registered at. If ID is different than the given one, it was taken already
+	inline std::size_t registerObject(T_Object p_object, std::size_t p_id)
+	{
+		std::size_t objectID = 0;
+
+		// Check if ID is within object pool bounds
+		if(p_id >= m_objectPool.size())
+		{
+			// Get the current size of the pool and calculate the new size
+			decltype(m_objectPool.size()) oldSize = m_objectPool.size();
+			decltype(m_objectPool.size()) newSize = (p_id * OBJECT_REGISTER_RESIZE_ADDED_OVERHEAD_MULTIPLIER) + 1;
+
+			// Resize the object pool to fit in the given ID
+			// Add some overhead to the size, as there's a high chance that there will be more objects to be registered
+			m_objectPool.resize(newSize, nullptr);
+
+			// Add the newly created ID slots to the empty object ID pool
+			for(decltype(m_objectPool.size()) i = oldSize - 1; i < newSize; i++)
+			{
+				// Add each new element to the empty object ID pool, excluding the given ID of the given object to be registered
+				if(i != p_id)
+					m_emptyObjectIDPool.push_back(i);
+			}
+
+			// Recursively call to register the given object, as the size should now fit the given ID
+			objectID = registerObject(p_object, p_id);
+		}
+		else
+		{
+			// ID is within object pool
+			// Check if ID slot is empty
+			if(m_objectPool[p_id] == nullptr)
+			{
+				// Insert the given object into an empty slot
+				m_objectPool[p_id] = p_object;
+				objectID = p_id;
+			}
+			else
+			{
+				// Get a new ID for the object, as the given one is taken
+				objectID = registerObject(p_object);
+			}
+		}
+
+		return objectID;
+	}
+
+	// Removes object from directory, freeing up its ID
+	inline void unregisterObject(const std::size_t p_ID)
+	{
+		// Check if the object ID is within bounds
+		if(p_ID < m_objectPool.size())
+		{
+			// Check if the object slot at the given ID isn't empty already
+			if(m_objectPool[p_ID] != nullptr)
+			{
+				// Remove object from pool by setting its pointer to null
+				m_objectPool[p_ID] = nullptr;
+
+				// Add the index of removed object to empty object ID pool
+				m_emptyObjectIDPool.push_back(p_ID);
+			}
+		}
+	}
+
+	// Returns object based on its ID. Returns 'nullptr' if object wasn't found
+	inline T_Object getObject(const std::size_t p_ID) const
+	{		
+		// Default the return object to nullptr
+		T_Object *returnObject;
+		
+		// Check if the object ID is within bounds and assign the return object if it is
+		if(p_ID < m_objectPool.size())
+			returnObject = m_objectPool[p_ID];
+		
+		// Return the object; 'nullptr' if it wasn't found
+		return returnObject;
+	}
+
+	// Good to call after registering multiple objects with specified IDs, as that might create
+	// many empty ID slots, which might get filled, but not removed from internal register
+	inline void sortRegisteredObjectIDs()
+	{
+		// If the empty object ID pool has elements
+		if(!m_emptyObjectIDPool.empty())
+		{
+			// Declare a temporary pool as a copy of empty object ID pool
+			decltype(m_emptyObjectIDPool) tempPool(m_emptyObjectIDPool);
+
+			// Clear the original pool of all elements
+			m_emptyObjectIDPool.clear();
+
+			// Loop over each element in the temporary pool
+			for(decltype(tempPool.size()) i = 0, size = tempPool.size(); i < size; i++)
+			{
+				// If the element of the contained ID is empty, add it back to the empty object ID pool
+				if(m_objectPool[tempPool[i]] == nullptr)
+					m_emptyObjectIDPool.push_back(tempPool[i]);
+			}
+		}
+	}
+
+private:
+	// Finds and removes the element of empty object ID pool that contains the given ID
+	// Slow, as it compares each element and uses std::vector erase
+	void removeIDfromEmptyObjectPool(std::size_t p_id)
+	{
+		// Iterate over each element in empty object ID pool
+		for(decltype(m_emptyObjectIDPool.size()) i = 0, size = m_emptyObjectIDPool.size(); i < size; i++)
+		{
+			// If the ID of the current element matches
+			if(m_emptyObjectIDPool[i] == p_id)
+			{
+				// Erase the matched element and return from the function
+				m_emptyObjectIDPool.erase(vec.begin() + i);
+				return;
+			}
+		}
+	}
+
+	// Object pool index variable type
+	typedef std::vector<T_Object>::size_type ObjectPoolIndexType;
+
+	// All registered objects
+	std::vector<T_Object> m_objectPool;
+
+	// Indexes (IDs) of unregistered objects in the object pool
+	std::vector<ObjectPoolIndexType> m_emptyObjectIDPool;
+};
+
+// An object directory, used for getting unique IDs to all registered objects.
+// Also contains a list of registered object pointers, an object can be retrieved based on its ID.
+// Implementation is thread-safe.
+template <class T_Object>
+class ObjectRegisterConcurrent 
+{
+public:
+	ObjectRegisterConcurrent() { }
+	~ObjectRegisterConcurrent() { }
+
+	// Reserve internal space for given amount of objects to speed up the object registering
+	void reserveSize(const std::size_t p_size)
+	{
+		m_objectPool.reserve(p_size);
+	}
+
+	// Returns a unique ID
+	inline std::size_t registerObject(T_Object p_object)
+	{
+		ObjectPoolIndexType objectID = 0;
+		
+		// Try to get an index of an unregistered element in the object pool
+		if(m_emptyObjectIDPool.try_pop(objectID))
+		{
+			// If the retrieved object ID is empty, use it for the new given object
+			// If it is not empty, recursively call registerObject to get a new object ID
+			if(m_objectPool[objectID] == nullptr)
+			{
+				// Insert the given object into an empty slot
+				m_objectPool[objectID] = p_object;
+			}
+			else
+			{
+				// Get a new object ID
+				objectID = registerObject(p_object);
+			}
+		}
+		else
+		{
+			// There was no existing unregistered ID in the object pool, so create a new ID
+			// Add the object to the pool, get an iterator and calculate the index at which the object was inserted
+			objectID = (m_objectPool.push_back(p_object) - m_objectPool.begin());
+		}
+		
+		// Cast the object index (int) to size_t (unsigned int)
+		return static_cast<std::size_t>(objectID);
+	}
+	
+	// Tries to register an object with the given ID
+	// Returns the ID object gets registered at. If ID is different than the given one, it was taken already
+	inline std::size_t registerObject(T_Object p_object, std::size_t p_id)
+	{
+		std::size_t objectID = 0;
+
+		// Check if ID is within object pool bounds
+		if(p_id >= m_objectPool.size())
+		{
+			// Get the current size of the pool and calculate the new size
+			decltype(m_objectPool.size()) oldSize = m_objectPool.size();
+			decltype(m_objectPool.size()) newSize = (p_id * OBJECT_REGISTER_RESIZE_ADDED_OVERHEAD_MULTIPLIER) + 1;
+
+			// Grow the object pool to fit in the given ID and get the starting index at which it was grown from
+			// Add some overhead to the size, as there's a high chance that there will be more objects to be registered
+			ObjectPoolIndexType poolResizeStartIndex = m_objectPool.grow_to_at_least(newSize, nullptr) - m_objectPool.begin();
+
+			// Iterate over each element that was added by growing the object pool
+			for(decltype(poolResizeStartIndex) i = poolResizeStartIndex; i < newSize; i++)
+			{
+				// Add each new element to the empty object ID pool, excluding the given ID of the given object to be registered
+				if(i != p_id)
+					m_emptyObjectIDPool.push_back(i);
+			}
+
+			// Recursively call to register the given object, as the size should now fit the given ID
+			objectID = registerObject(p_object, p_id);
+		}
+		else
+		{
+			// ID is within object pool
+			// Check if ID slot is empty
+			if(m_objectPool[p_id] == nullptr)
+			{
+				// Insert the given object into an empty slot
+				m_objectPool[p_id] = p_object;
+				objectID = p_id;
+			}
+			else
+			{
+				// Get a new ID for the object, as the given one is taken
+				objectID = registerObject(p_object);
+			}
+		}
+
+		return objectID;
+	}
+
+	// Removes object from directory, freeing up its ID
+	inline void unregisterObject(const std::size_t p_ID)
+	{
+		// Check if the object ID is within bounds
+		if(p_ID < m_objectPool.size())
+		{
+			// Check if the object slot at the given ID isn't empty already
+			if(m_objectPool[p_ID] != nullptr)
+			{
+				// Remove object from pool by setting its pointer to null
+				m_objectPool[p_ID] = nullptr;
+
+				// Add the index of removed object to empty object ID pool
+				m_emptyObjectIDPool.push(p_ID);
+			}
+		}
+	}
+
+	// Returns object based on its ID. Returns 'nullptr' if object wasn't found
+	inline T_Object getObject(const std::size_t p_ID) const
+	{	
+		// Default the return object to nullptr
+		T_Object *returnObject = nullptr;
+
+		// Check if the object ID is within bounds and assign the return object if it is
+		if(p_ID < m_objectPool.size())
+			returnObject = m_objectPool[p_ID];
+
+		// Return the object; 'nullptr' if it wasn't found
+		return returnObject;
+	}
+	
+	// Good to call after registering multiple objects with specified IDs, as that might create
+	// many empty ID slots, which might get filled, but not removed from internal register
+	inline void sortRegisteredObjectIDs()
+	{
+		// Declare a temporary pool as a copy of empty object ID pool
+		decltype(m_emptyObjectIDPool) tempPool(m_emptyObjectIDPool);
+		
+		// Clear the original pool of all elements
+		m_emptyObjectIDPool.clear();
+
+		// Temporary ID, used for getting an ID from the tempPool by passing it as an argument
+		ObjectPoolIndexType tempID = 0;
+		
+		// Try to get an element from the temporary pool
+		while(tempPool.try_pop(tempID))
+		{
+			// If the element of the contained ID is empty, add it back to the empty object ID pool
+			if(m_objectPool[tempID] == nullptr)
+				m_emptyObjectIDPool.push(tempID);
+		}
+	}
+
+private:
+	// Object pool index variable type
+	typedef tbb::concurrent_vector<T_Object>::size_type ObjectPoolIndexType;
+	
+	// All registered objects
+	tbb::concurrent_vector<T_Object> m_objectPool;
+
+	// Indexes (IDs) of unregistered objects in the object pool
+	tbb::concurrent_queue<ObjectPoolIndexType> m_emptyObjectIDPool;
+};

+ 9 - 8
Praxis3D/Source/ObserverBase.cpp

@@ -8,6 +8,7 @@ const int ObservedSubject::m_nullInt = 0;
 const float ObservedSubject::m_nullFloat = 0.0f;
 const double ObservedSubject::m_nullDouble = 0.0;
 const std::string ObservedSubject::m_nullString;
+const SpatialData m_nullSpacialData;
 
 ObservedSubject::ObservedSubject()
 {
@@ -21,7 +22,7 @@ ObservedSubject::~ObservedSubject()
 // Attach an observer for this subject
 ErrorCode ObservedSubject::attach(Observer *p_observer, unsigned int p_interestedBits, unsigned int p_ID, unsigned int p_shiftBits)
 {
-	// If the concurency is enabled, lock the current subject and wait for it to be free
+	// If the concurrency is enabled, lock the current subject and wait for it to be free
 	#if ENABLE_CONCURRENT_SUBJECT_OPERATIONS
 		SpinWait::Lock lock(m_observerListMutex);
 	#endif
@@ -42,7 +43,7 @@ ErrorCode ObservedSubject::detach(Observer *p_observer)
 {
 	ErrorCode returnError = ErrorCode::Failure;
 
-	// If the concurency is enabled, lock the current subject and wait for it to be free
+	// If the concurrency is enabled, lock the current subject and wait for it to be free
 	#if ENABLE_CONCURRENT_SUBJECT_OPERATIONS 
 		SpinWait::Lock lock(m_observerListMutex); 
 	#endif
@@ -65,7 +66,7 @@ ErrorCode ObservedSubject::updateInterestBits(Observer *p_observer, unsigned int
 {
 	ErrorCode returnError = ErrorCode::Failure;
 
-	// If the concurency is enabled, lock the current subject and wait for it to be free
+	// If the concurrency is enabled, lock the current subject and wait for it to be free
 	#if ENABLE_CONCURRENT_SUBJECT_OPERATIONS
 		SpinWait::Lock lock(m_observerListMutex);
 	#endif
@@ -113,7 +114,7 @@ BitMask ObservedSubject::getID(Observer *p_observer) const
 // NOTE: If concurrent operations are enabled, the implementation of postChanges would need to be changed.
 void ObservedSubject::postChanges(BitMask p_changedBits)
 {
-	// If the concurency is enabled, lock the current subject and wait for it to be free
+	// If the concurrency is enabled, lock the current subject and wait for it to be free
 	#if ENABLE_CONCURRENT_SUBJECT_OPERATIONS
 		SpinWait::Lock lock(m_observerListMutex);
 	#endif
@@ -121,7 +122,7 @@ void ObservedSubject::postChanges(BitMask p_changedBits)
 	// Send the changes to all observers
 	for(ObserverDataList::iterator it = m_observerList.begin(); it != m_observerList.end(); it++)
 	{
-		BitMask changedInterestedBits = getBitsToPost(*it, p_changedBits);
+		const BitMask changedInterestedBits = getBitsToPost(*it, p_changedBits);
 
 		// Check if there are any changes to the data we are interested in
 		if(changedInterestedBits)
@@ -138,7 +139,7 @@ void ObservedSubject::preDestruct()
 	// Thus if it ever gets called concurrently, locking must be added.
 	for(ObserverDataList::iterator it = m_observerList.begin(); it != m_observerList.end(); it++)
 	{
-		it->m_observer->changeOccurred(this, 0);
+		//it->m_observer->changeOccurred(this, 0);
 	}
 
 	m_observerList.clear();
@@ -146,9 +147,9 @@ void ObservedSubject::preDestruct()
 
 namespace Interface
 {
-	inline BitMask getBitsToPost(ObservedSubject::ObserverData &p_observer, BitMask p_changedBits)
+	inline BitMask getBitsToPost(const ObservedSubject::ObserverData &p_observer, BitMask p_changedBits)
 	{
-		BitMask changedInterestedBits = p_observer.m_interestedBits & p_changedBits;
+		const BitMask changedInterestedBits = p_observer.m_interestedBits & p_changedBits;
 
 		return changedInterestedBits;
 	}

+ 10 - 7
Praxis3D/Source/ObserverBase.h

@@ -25,6 +25,7 @@
 #include <list>
 
 #include "Config.h"
+#include "Containers.h"
 #include "Math.h"
 #include "SpinWait.h"
 
@@ -33,7 +34,7 @@ class ObservedSubject;
 class Observer
 {
 public:
-	virtual ~Observer() { }
+	virtual ~Observer() = 0;
 
 	// This method gets called when data that we are interested changed in observed subject
 	virtual void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) = 0;
@@ -55,17 +56,18 @@ public:
 	virtual void postChanges(BitMask p_changedBits);
 	virtual void preDestruct();
 
-	const static unsigned int g_invalidID = (unsigned int)(-1);
+	const static unsigned int g_invalidID = static_cast<unsigned int>(-1);
 
 	const virtual Math::Vec3f &getVec3(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullVec3; }
 	const virtual Math::Vec4f &getVec4(const Observer *p_observer, BitMask p_changedBits) const { return m_nullVec4; }
 	const virtual Math::Mat4f &getMat4(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullMat4; }
 
-	const virtual bool			getBool(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullBool;	}
-	const virtual int			getInt(const Observer *p_observer, BitMask p_changedBits) const		{ return m_nullInt;		}
-	const virtual float			getFloat(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullFloat;	}
-	const virtual double		&getDouble(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullDouble;	}
-	const virtual std::string	&getString(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullString;	}
+	const virtual bool			getBool(const Observer *p_observer, BitMask p_changedBits) const			{ return m_nullBool;		}
+	const virtual int			getInt(const Observer *p_observer, BitMask p_changedBits) const				{ return m_nullInt;			}
+	const virtual float			getFloat(const Observer *p_observer, BitMask p_changedBits) const			{ return m_nullFloat;		}
+	const virtual double		&getDouble(const Observer *p_observer, BitMask p_changedBits) const			{ return m_nullDouble;		}
+	const virtual std::string	&getString(const Observer *p_observer, BitMask p_changedBits) const			{ return m_nullString;		}
+	const virtual SpatialData	&getSpatialData(const Observer *p_observer, BitMask p_changedBits) const	{ return m_nullSpacialData;	}
 
 	// Stores observer information required for postChanges messages
 	struct ObserverData
@@ -104,4 +106,5 @@ private:
 	const static float		 m_nullFloat;
 	const static double		 m_nullDouble;
 	const static std::string m_nullString;
+	const static SpatialData m_nullSpacialData;
 };

+ 24 - 0
Praxis3D/Source/PropertySet.h

@@ -533,6 +533,9 @@ public:
 	// Bool operator; returns true if the property is valid and false if the property is null
 	inline explicit operator bool() const { return m_propertyID != Properties::Null; }
 
+	// Returns true if the native variable type of the property is string (meaning the string that was retrieved from the property is
+	const inline bool isVariableTypeString() const { return (m_variableType == VariableType::Type_string); }
+
 	// Converts the property to text (used for saving properties to text files)
 	const inline std::string toString() { return "\"" + std::string(GetString(m_propertyID)) + "\": \"" + getString() + "\""; }
 private:
@@ -722,6 +725,27 @@ public:
 	const inline bool operator==(const PropertySet &p_propertySet) const { return (m_propertyID == p_propertySet.m_propertyID); }
 	const inline bool operator<(const PropertySet &p_propertySet) const { return (m_propertyID < p_propertySet.m_propertyID); }
 
+	// Adds two PropertySets together (including their pripertySets and individual properties)
+	inline PropertySet operator+(const PropertySet &p_propertySet) const 
+	{
+		// Create a new propertySet that is a copy of the currentProperty set
+		PropertySet combinedSet(*this);
+
+		// Add all propertySets from the given propertySet
+		for(decltype(p_propertySet.m_numPropertySets) i = 0, size = p_propertySet.m_numPropertySets; i < size; i++)
+			combinedSet.addPropertySet(p_propertySet.getPropertySet(i));
+		
+		// Add all properties from the given propertySet
+		for(decltype(p_propertySet.m_numProperties) i = 0, size = p_propertySet.m_numProperties; i < size; i++)
+			combinedSet.addProperty(p_propertySet[i]);
+
+		// Optimize the new propertySet
+		combinedSet.optimizeForSearch();
+
+		// Returned the combined set
+		return combinedSet;
+	}
+
 	// Setters
 	const inline void setPropertyID(Properties::PropertyID p_propertyID) { m_propertyID = p_propertyID; }
 

+ 2 - 2
Praxis3D/Source/RendererFrontend.cpp

@@ -214,10 +214,10 @@ void RendererFrontend::renderFrame(const SceneObjects &p_sceneObjects, const flo
 	std::cout << m_frameData.m_viewMatrix.m[12] << " : " << m_frameData.m_viewMatrix.m[13] << " : " << m_frameData.m_viewMatrix.m[14] << " : " << m_frameData.m_viewMatrix.m[15] << std::endl;
 	*/
 	// Set the camera position
-	m_frameData.m_cameraPosition = p_sceneObjects.m_camera->getVec3(nullptr, Systems::Changes::Spacial::Position);
+	m_frameData.m_cameraPosition = p_sceneObjects.m_camera->getVec3(nullptr, Systems::Changes::Spatial::Position);
 	
 	// Set the camera target vector
-	m_frameData.m_cameraTarget = p_sceneObjects.m_camera->getVec3(nullptr, Systems::Changes::Spacial::Rotation);
+	m_frameData.m_cameraTarget = p_sceneObjects.m_camera->getVec3(nullptr, Systems::Changes::Spatial::Rotation);
 
 	// Assign directional light values and also normalize its direction, so it's not neccessary to do it in a shader
 	m_frameData.m_dirLightColor = p_sceneObjects.m_directionalLight->m_color;

+ 245 - 6
Praxis3D/Source/RendererScene.cpp

@@ -511,13 +511,252 @@ void RendererScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeT
 
 BitMask RendererScene::getDesiredSystemChanges()
 {
-	return Systems::Changes::Spacial::All;
+	return Systems::Changes::Spatial::All;
 }
 BitMask RendererScene::getPotentialSystemChanges()
 {
-	return Systems::Changes::Spacial::All;
+	return Systems::Changes::Spatial::All;
 }
 
+
+
+ModelComponentData *RendererScene::loadModelComponent(const PropertySet &p_properties)
+{
+	ModelComponentData *newComponent = nullptr;
+		
+	// Check if models node is present
+	if(p_properties)
+	{
+		// Create the model component
+		newComponent = new ModelComponentData();
+
+		// Loop over each model entry in the node
+		for(decltype(p_properties.getNumProperties()) iModel = 0, numModels = p_properties.getNumProperties(); iModel < numModels; iModel++)
+		{
+			// Get model filename
+			auto modelName = p_properties.getPropertySet(iModel).getPropertyByID(Properties::Filename).getString();
+
+			// Add a new model data entry, and get a reference to it
+			newComponent->m_modelData.push_back(ModelData(Loaders::model().load(modelName, false)));
+			auto &newModelData = newComponent->m_modelData.back();
+
+			// Load the model to memory, to be able to access all of its meshes
+			newModelData.m_model.loadToMemory();
+
+			// Get the meshes array
+			auto meshesInModelArray = newModelData.m_model.getMeshArray();
+
+			// Get the meshes array
+			auto meshesProperty = p_properties.getPropertySet(iModel).getPropertySetByID(Properties::Meshes);
+
+			// Check if the meshes array node is present;
+			// If it is present, only add the meshes included in the meshes node
+			// If it is not present, add all the meshes included in the model
+			if(meshesProperty)
+			{
+				// Loop over each mesh entry in the model node
+				for(decltype(meshesProperty.getNumProperties()) iMesh = 0, numMeshes = meshesProperty.getNumProperties(); iMesh < numMeshes; iMesh++)
+				{
+					// Try to get the mesh index property node and check if it is present
+					auto meshIndexProperty = meshesProperty.getPropertySet(iMesh).getPropertyByID(Properties::Index);
+					if(meshIndexProperty)
+					{
+						// Get the mesh index, check if it is valid and within the range of mesh array that was loaded from the model
+						const int meshDataIndex = meshIndexProperty.getInt();
+						if(meshDataIndex > 0 && meshDataIndex < meshesInModelArray.size())
+						{
+							// Get material properties
+							auto materialsProperty = meshesProperty.getPropertySet(iMesh).getPropertySetByID(Properties::Materials);
+
+							// Define material data and material properties
+							MaterialData materials[MaterialType::MaterialType_NumOfTypes];
+							PropertySet materialProperties[MaterialType::MaterialType_NumOfTypes] = 
+							{
+								materialsProperty.getPropertySetByID(Properties::Diffuse),
+								materialsProperty.getPropertySetByID(Properties::Normal),
+								materialsProperty.getPropertySetByID(Properties::Emissive),
+								materialsProperty.getPropertySetByID(Properties::RMHAO)
+							};
+							
+							// Go over each material type
+							for(unsigned int iMatType = 0; iMatType < MaterialType::MaterialType_NumOfTypes; iMatType++)
+							{
+								// Check if an entry for the current material type was present within the properties
+								if(materialProperties[iMatType])
+								{
+									// Load the material data
+									materials[iMatType] = loadMaterialData(materialProperties[iMatType], newModelData.m_model.getMaterialArrays(), static_cast<MaterialType>(iMatType), meshDataIndex);
+								}
+							}
+
+							/*if(materialsProperty)
+							{
+								auto diffuseMatProperty = materialsProperty.getPropertySetByID(Properties::Diffuse);
+								materials[MaterialType_Diffuse] = loadMaterialData(diffuseMatProperty, newModelData.m_model.getMaterialArrays(), MaterialType_Diffuse, meshDataIndex);
+
+								//newModelData.m_model.getMaterialArrays()
+								//materials[MaterialType_Diffuse] = &materialProperty.getPropertySetByID(Properties::Diffuse);
+								//materials[MaterialType_Normal] = &materialProperty.getPropertySetByID(Properties::Normal);
+								//materials[MaterialType_Emissive] = &materialProperty.getPropertySetByID(Properties::Emissive);
+								//materials[MaterialType_Combined] = &materialProperty.getPropertySetByID(Properties::RMHAO);
+							}*/
+							
+							newModelData.m_meshes.push_back(MeshData(meshesInModelArray[iMesh], materials));
+						}
+					}
+				}
+			}
+			else
+			{
+				// Get the material arrays that were loaded from the model file
+				auto &materialArrayFromModel = newModelData.m_model.getMaterialArrays();
+
+				// Iterate over every mesh in the model
+				for(decltype(meshesInModelArray.size()) iMesh = 0, numMeshes = meshesInModelArray.size(); iMesh < numMeshes; iMesh++)
+				{			
+					// Define material data and material properties
+					MaterialData materials[MaterialType::MaterialType_NumOfTypes];
+
+					// Go over each mesh in the model
+					if(iMesh > materialArrayFromModel.m_numMaterials)
+					{
+						// Go over each material type
+						for(unsigned int iMatType = 0; iMatType < MaterialType::MaterialType_NumOfTypes; iMatType++)
+						{
+							// Get the texture filename and load it to memory
+							auto textureFromModel = Loaders::texture2D().load(materialArrayFromModel.m_materials[iMatType][iMesh].m_filename, static_cast<MaterialType>(iMatType), false);
+							auto materialLoadError = textureFromModel.loadToMemory();
+														
+							// Check if the texture was loaded successfully
+							if(materialLoadError == ErrorCode::Success)
+							{
+								materials[MaterialType::MaterialType_Diffuse].m_texture = textureFromModel;
+							}
+							else
+							{
+								ErrHandlerLoc::get().log(materialLoadError, ErrorSource::Source_Renderer);
+							}
+						}
+						
+						// Add the data for this mesh. Include materials loaded from the model itself, if they were present, otherwise, include default textures instead
+						newModelData.m_meshes.push_back(MeshData(meshesInModelArray[iMesh], materials));
+					}
+				}
+			}
+		}
+	}
+
+	return newComponent;
+}
+ShaderData *RendererScene::loadShaderComponent(const PropertySet &p_properties)
+{
+	ShaderData *newComponent = nullptr;
+
+	// Check if shaders node is valid
+	if(p_properties)
+	{
+		// Get nodes for different shader types
+		auto fragmentShaderNode = p_properties.getPropertyByID(Properties::FragmentShader);
+		auto vertexShaderNode = p_properties.getPropertyByID(Properties::VertexShader);
+
+		// Check if any of the shader nodes are present
+		if(fragmentShaderNode || vertexShaderNode)
+		{
+			// Load shader program
+			auto shaderProgram = Loaders::shader().load(p_properties);
+
+			// If shader is not default (i.e. at least one of the shader types was loaded)
+			if(!shaderProgram->isDefaultProgram())
+			{
+				// Load the shader to memory and assign it to the new shader component
+				shaderProgram->loadToMemory();
+				newComponent = new ShaderData(*shaderProgram);
+			}
+		}
+	}
+
+	return newComponent;
+}
+LightComponent *RendererScene::loadLightComponent(const PropertySet &p_properties)
+{
+	LightComponent *newComponent = nullptr;
+
+	// Check if the property node is valid
+	if(p_properties)
+	{
+		Math::Vec3f color;
+		float	intensity = 0.0f,
+				cutoffAngle = 0.0f;
+		Properties::PropertyID type = Properties::PropertyID::Null;
+
+		// Load property data
+		for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+		{
+			switch(p_properties[i].getPropertyID())
+			{
+			case Properties::Color:
+				color = p_properties[i].getVec3f();
+				break;
+
+			case Properties::CutoffAngle:
+				// Convert to radians
+				cutoffAngle = cosf(Math::toRadian(p_properties[i].getFloat()));
+				break;
+
+			case Properties::Intensity:
+				intensity = p_properties[i].getFloat();
+				break;
+
+			case Properties::Type:
+				type = p_properties[i].getID();
+				break;
+			}
+		}
+
+		// Create light component based on light type
+		switch(type)
+		{
+		case Properties::DirectionalLight:
+			{
+				// Create and setup the directional light data set
+				DirectionalLightDataSet dirLightDataSet;
+				dirLightDataSet.m_color = color;
+				dirLightDataSet.m_intensity = intensity;
+
+				// Create the component of the directional light type
+				newComponent = new LightComponent(dirLightDataSet);
+			}
+			break;
+		case Properties::PointLight:
+			{
+				// Create and setup the point light data set
+				PointLightDataSet pointLightDataSet;
+				pointLightDataSet.m_color = color;
+				pointLightDataSet.m_intensity = intensity;
+
+				// Create the component of the point light type
+				newComponent = new LightComponent(pointLightDataSet);
+			}
+			break;
+		case Properties::SpotLight:
+			{
+				// Create and setup the spot light data set
+				SpotLightDataSet spotLightDataSet;
+				spotLightDataSet.m_color = color;
+				spotLightDataSet.m_cutoffAngle = cutoffAngle;
+				spotLightDataSet.m_intensity = intensity;
+
+				// Create the component of the spot light type
+				newComponent = new LightComponent(spotLightDataSet);
+			}
+			break;
+		}
+	}
+
+	return newComponent;
+}
+
+
 ModelObject *RendererScene::loadModelObject(const PropertySet &p_properties)
 {
 	// Get model properties
@@ -774,9 +1013,9 @@ PointLightObject *RendererScene::loadPointLight(const PropertySet &p_properties)
 				newObject->setOffsetPosition(p_properties[i].getVec3f());
 				break;
 
-			case Properties::Position:
+			/*case Properties::Position:
 				newObject->setPosition(p_properties[i].getVec3f());
-				break;
+				break;*/
 			}
 		}
 
@@ -841,9 +1080,9 @@ SpotLightObject *RendererScene::loadSpotLight(const PropertySet &p_properties)
 				newObject->setOffsetRotation(p_properties[i].getVec3f());
 				break;
 
-			case Properties::Position:
+			/*case Properties::Position:
 				newObject->setPosition(p_properties[i].getVec3f());
-				break;
+				break;*/
 			}
 		}
 

+ 6 - 1
Praxis3D/Source/RendererScene.h

@@ -7,6 +7,7 @@
 #include "CameraGraphicsObject.h"
 #include "EnvironmentMapObjects.h"
 #include "GraphicsDataSets.h"
+#include "LightComponent.h"
 #include "LightingGraphicsObjects.h"
 #include "Loaders.h"
 #include "ObjectPool.h"
@@ -128,7 +129,11 @@ private:
 		return nullptr;
 	}
 
-	// Object creators (factories)
+	// Object component creators (factories)
+	ModelComponentData *loadModelComponent(const PropertySet &p_properties);
+	ShaderData *loadShaderComponent(const PropertySet &p_properties);
+	LightComponent *loadLightComponent(const PropertySet &p_properties);
+
 	ModelObject *loadModelObject(const PropertySet &p_properties);
 	CameraObject *loadCameraObject(const PropertySet &p_properties);
 	EnvironmentMapObject *loadEnvironmentMap(const PropertySet &p_properties);

+ 2 - 2
Praxis3D/Source/SceneLoader.cpp

@@ -135,14 +135,14 @@ ErrorCode SceneLoader::saveToFile(const std::string p_filename)
 		auto &objLinksPropertySet = propertySet.addPropertySet(Properties::ObjectLinks);
 
 		// Get object links from the change controller
-		auto &objLinkList = m_changeController->getObjectLinksList();
+		/*auto &objLinkList = m_changeController->getObjectLinksList();
 
 		for(UniversalScene::ObjectLinkList::const_iterator it = objLinkList.begin(); it != objLinkList.end(); it++)
 		{
 			auto &objectLinkEntry = objLinksPropertySet.addPropertySet(Properties::ArrayEntry);
 			objectLinkEntry.addProperty(Properties::Subject, it->m_observerName);
 			objectLinkEntry.addProperty(Properties::Observer, it->m_subjectName);
-		}
+		}*/
 
 		// Save properties to a file
 		PropertyLoader savedProperties(Config::filepathVar().map_path + filename);

+ 54 - 0
Praxis3D/Source/ShaderComponent.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include "BaseGraphicsComponent.h"
+#include "GraphicsDataSets.h"
+
+class ShaderComponent : public BaseGraphicsComponent
+{
+public:
+	ShaderComponent() { }
+	~ShaderComponent() 
+	{ 
+		delete m_shaderData;
+	}
+
+	void load(PropertySet &p_properties) final override
+	{ 
+		// Check if shaders node is valid and the component hasn't been loaded already
+		if(p_properties && empty())
+		{
+			// Get nodes for different shader types
+			auto fragmentShaderNode = p_properties.getPropertyByID(Properties::FragmentShader);
+			auto vertexShaderNode = p_properties.getPropertyByID(Properties::VertexShader);
+
+			// Check if any of the shader nodes are present
+			if(fragmentShaderNode || vertexShaderNode)
+			{
+				// Load shader program
+				auto shaderProgram = Loaders::shader().load(p_properties);
+
+				// If shader is not default (i.e. at least one of the shader types was loaded)
+				if(!shaderProgram->isDefaultProgram())
+				{
+					// Set the component as not being empty anymore, since at least one of the shaders has been loaded successfully
+					setEmpty(false);
+
+					// Load the shader to memory and assign it to the new shader component
+					shaderProgram->loadToMemory();
+					m_shaderData = new ShaderData(*shaderProgram);
+				}
+			}
+		}
+
+		// Set the component as loaded, because the load function was called
+		setLoaded(true);
+	}
+
+	PropertySet export() final override
+	{ 
+		return PropertySet(); 
+	}
+
+private:
+	ShaderData *m_shaderData;
+};

+ 0 - 1
Praxis3D/Source/ShaderLoader.cpp

@@ -88,7 +88,6 @@ ShaderLoader::ShaderProgram *ShaderLoader::load(const PropertySet &p_properties)
 
 		if(numberOfShaders > 0)
 		{
-
 			// If program name was not specified, combine all shader names into one program name
 			if(programName.empty())
 			{

+ 9 - 4
Praxis3D/Source/System.cpp

@@ -1,14 +1,19 @@
 #include "ObjectDirectory.h"
 #include "System.h"
 
-SystemObject::SystemObject() : m_initialized(false), m_systemScene(nullptr), m_objectType(Properties::Null), m_ID(0)
+SystemObject::SystemObject() : m_initialized(false), m_active(false), m_updateNeeded(false), m_systemScene(nullptr), m_objectType(Properties::Null), m_objectID(0)
 {
 	setName("Null Object");
-	m_ID = ObjectDirectory::registerObject(this);
+	m_objectID = ObjectDirectory::registerObject(*this);
 }
 
-SystemObject::SystemObject(SystemScene * p_systemScene, const std::string & p_name, Properties::PropertyID p_objectType) : m_initialized(false), m_systemScene(p_systemScene), m_objectType(p_objectType), m_ID(0)
+SystemObject::SystemObject(SystemScene * p_systemScene, const std::string & p_name, Properties::PropertyID p_objectType) : m_initialized(false), m_active(false), m_updateNeeded(false), m_systemScene(p_systemScene), m_objectType(p_objectType), m_objectID(0)
 {
 	setName(p_name);
-	m_ID = ObjectDirectory::registerObject(this);
+	m_objectID = ObjectDirectory::registerObject(*this);
+}
+
+SystemObject::~SystemObject()
+{
+	ObjectDirectory::unregisterObject(*this);
 }

+ 21 - 8
Praxis3D/Source/System.h

@@ -82,6 +82,7 @@ class SystemObject : public ObservedSubject, public Observer
 public:
 	SystemObject();
 	SystemObject(SystemScene *p_systemScene, const std::string &p_name, Properties::PropertyID p_objectType);
+	~SystemObject();
 
 	virtual ErrorCode init() = 0;
 
@@ -95,14 +96,18 @@ public:
 
 	// Exports all the data of the object as a PropertySet (for example, used for saving to map file)
 	virtual PropertySet exportObject() { return PropertySet(Properties::Null); }
+	
+	// Is the object active (i.e. should be drawn, updated, etc...)
+	const inline bool isObjectActive() const { return m_active; }
 
 	// Getters
-	inline size_t getObjectID() const					{ return m_ID;			}
-	inline void *getParent() const						{ return m_parent;		}
-	inline bool isInitialized() const					{ return m_initialized; }
-	const inline std::string &getName() const			{ return m_name;		}
-	inline SystemScene *getSystemScene() const			{ return m_systemScene; }
-	inline Properties::PropertyID getObjectType() const { return m_objectType;	}
+	inline std::size_t getObjectID() const				{ return m_objectID;		}
+	inline void *getParent() const						{ return m_parent;			}
+	inline bool isInitialized() const					{ return m_initialized;		}
+	inline bool isUpdateNeeded() const					{ return m_updateNeeded;	}
+	const inline std::string &getName() const			{ return m_name;			}
+	inline SystemScene *getSystemScene() const			{ return m_systemScene;		}
+	inline Properties::PropertyID getObjectType() const { return m_objectType;		}
 	const virtual std::string &getString(const Observer *p_observer, BitMask p_changedBits) const
 	{
 		switch(p_changedBits)
@@ -119,6 +124,7 @@ public:
 	inline void setName(std::string p_name)								 { m_name = p_name;					}
 	inline void setSystemScene(SystemScene *p_systemScene)				 { m_systemScene = p_systemScene;	}
 	inline void setObjectType(const Properties::PropertyID p_objectType) { m_objectType = p_objectType;		}
+	inline void setActive(const bool p_isActive)						 { m_active = p_isActive;			}
 
 	// Bool operator; returns true if the system object is not null (i.e. valid derived object)
 	// Implemented to allow requested objects to be checked if valid and ability to return 
@@ -126,19 +132,26 @@ public:
 	virtual operator bool() const { return false; }
 
 	// Comparator operator; uses object ID to determine if the object is the same
-	bool operator==(const SystemObject& p_systemObject) const { return m_ID == p_systemObject.m_ID ? true : false; }
+	bool operator==(const SystemObject &p_systemObject) const { return m_objectID == p_systemObject.m_objectID ? true : false; }
 
 protected:
+	inline void setUpdateNeeded(bool p_updateNeeded) { m_updateNeeded = p_updateNeeded; }
+
+	// Sets the 'update needed' flag to false
+	inline void updatePerformed() { setUpdateNeeded(false); }
+
 	Properties::PropertyID m_objectType;
 
 	void *m_parent;
 	bool m_initialized;
+	bool m_active;
+	bool m_updateNeeded;
 
 	std::string m_name;
 	SystemScene *m_systemScene;
 
 private:
-	size_t m_ID;
+	std::size_t m_objectID;
 };
 
 class SystemTask

+ 45 - 101
Praxis3D/Source/TextureLoader.h

@@ -68,7 +68,7 @@ protected:
 		SpinWait::Lock lock(m_mutex);
 
 		// Texture might have already been loaded when called from a different thread. Check if it was
-		if(!isLoadedToMemory())
+		if(!isLoaded())
 		{
 			// Read the format of the texture
 			FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileType((Config::PathsVariables().texture_path + m_filename).c_str(), 0);
@@ -215,114 +215,22 @@ public:
 	public:
 		~Texture2DHandle() { m_textureData->decRefCounter(); }
 
-		// Bind texture for rendering if it is loaded
-		// Load the texture to GPU memory instead if it's not loaded
-		void bind(unsigned int p_activeTextureID = GL_TEXTURE0)
-		{
-			// Declare the handle to bind at the start here, and bind it at the end of the function,
-			// so there is only one path to bind function and the branch can be predicted easier on CPU
-			/*decltype(m_textureData->m_handle) handle = m_textureData->m_handle;
-
-			// If the texture is already loaded (to GPU), just bind it (the handle is already assigned).
-			// Otherwise load it to GPU. This way, the texture loading is hidden away,
-			// so graphics system doesn't have to deal with it. The thread that calls bind
-			// on a texture is guaranteed to be rendering thread, so it is save to load it.
-			if(!m_textureData->loadedToVideoMemory())
-			{
-				if(m_textureData->loadedToMemory())
-				{
-					// Reassign the handle, since it has been changed while loading to video memory
-					handle = m_textureData->m_handle;
-
-					// If loading to memory failed, log an error
-					//ErrorCode error = m_textureData->loadToVideoMemory();
-					//if(error != ErrorCode::Success)
-					//	ErrHandlerLoc::get().log(error, ErrorSource::Source_TextureLoader, m_textureData->getFilename());
-
-					// Set loaded to video memory flag to true even if loading failed.
-					// This way, the failed loading attempt is not repeated every frame.
-					// (And since loaded to memory flag is already true, it is not the cause,
-					// and it will not "fix" itself)
-					m_textureData->setLoadedToVideoMemory(true);
-				}
-				else
-				{
-					// Set handle to zero if the texture is not yet ready to be used
-					handle = 0;
-				}
-			}
-
-			// Set the active texture position and bind the handle
-			glActiveTexture(GL_TEXTURE0 + p_activeTextureID);
-			glBindTexture(GL_TEXTURE_2D, handle);*/
-		}
-
-		// Upload the texture to GPU memory
-		ErrorCode preload()
-		{
-			ErrorCode returnError = ErrorCode::Success;
-
-			if(!m_textureData->isLoadedToVideoMemory())
-			{
-				if(m_textureData->isLoadedToMemory())
-				{
-				//	returnError = m_textureData->loadToVideoMemory();
-					m_textureData->setLoadedToVideoMemory(true);
-				}
-				else
-				{
-					returnError = m_textureData->loadToMemory();
-					if(returnError == ErrorCode::Success)
-					{
-						m_textureData->setLoadedToMemory(true);
-						//returnError = m_textureData->loadToVideoMemory();
-						m_textureData->setLoadedToVideoMemory(true);
-					}
-				}
-			}
-
-			return returnError;
-		}
-
 		// Loads data from HDD to RAM and restructures it to be used to fill buffers later
 		ErrorCode loadToMemory()
 		{
 			ErrorCode returnError = ErrorCode::Success;
 
 			// If it's not loaded to memory already, call load
-			if(!m_textureData->isLoadedToMemory())
+			if(!m_textureData->isLoaded())
 				returnError = m_textureData->loadToMemory();
 
 			return returnError;
 		}
-
-		// NOT USED
-		// Loads data from RAM to buffer and uploads them to VRAM
-		// WARNING: should probably only be called from rendering thread, since this code deals with graphics API
-		ErrorCode loadToVideoMemory()
-		{
-			ErrorCode returnError = ErrorCode::Success;
-
-			// If it's not loaded to video memory already, and has been loaded to memory, call load
-			//if(!m_textureData->loadedToVideoMemory())
-				//if(m_textureData->loadedToMemory())
-				//	returnError = m_textureData->loadToVideoMemory();
-
-			return returnError;
-		}
 		
-		void setWrapMode(TextureWrapMode p_wrapMode)
-		{
-			bind();
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_wrapMode);
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_wrapMode);
-		}
-
 		void setColorChannel(const Texture2DHandle &p_textureHandle, TextureColorChannelOffset p_destChannel, TextureColorChannelOffset p_sourceChannel = ColorOffset_Red)
 		{
-			// If both textures are not loaded to video memory already and have pixel data
-			if(m_textureData->m_loadedToVideoMemory == false && m_textureData->m_pixelData &&
-			   p_textureHandle.m_textureData->m_loadedToVideoMemory == false && p_textureHandle.m_textureData->m_pixelData)
+			// If both textures have pixel data loaded
+			if(m_textureData->m_pixelData && p_textureHandle.m_textureData->m_pixelData)
 			{
 				// Set the color channel of the internal texture
 				m_textureData->setColorChannel(p_textureHandle.m_textureData->m_pixelData,
@@ -340,34 +248,36 @@ public:
 		{
 			m_textureData->decRefCounter();
 			m_textureData = p_textureHandle.m_textureData;
+			m_handle = p_textureHandle.m_handle;
 			m_textureData->incRefCounter();
 			return *this;
 		}
 		
 		// Has the texture been already loaded to video memory (GPU VRAM)
-		const inline bool isLoadedToVideoMemory() const { return m_textureData->isLoadedToVideoMemory(); }
+		//const inline bool isLoadedToVideoMemory() const { return m_textureData->isLoadedToVideoMemory(); }
 
 		// Getters
 		inline unsigned int getTextureHeight() const { return m_textureData->m_textureHeight; }
 		inline unsigned int getTextureWidth() const { return m_textureData->m_textureWidth; }
-		inline unsigned int getHandle() const { return m_textureData->m_handle; }
+		inline unsigned int getHandle() const { return m_handle; }
 		inline int getMipmapLevel() const { return m_textureData->m_mipmapLevel; }
 		inline std::string getFilename() const { return m_textureData->m_filename; }
 		inline TextureFormat getTextureFormat() const { return m_textureData->m_textureFormat; }
 
 		// Setters
-		inline void setLoadedToMemory(bool p_loaded)		{ m_textureData->setLoadedToMemory(p_loaded);		}
-		inline void setLoadedToVideoMemory(bool p_loaded)	{ m_textureData->setLoadedToVideoMemory(p_loaded);	}
+		inline void setLoadedToMemory(bool p_loaded)		{ m_textureData->setLoaded(p_loaded);		}
+		//inline void setLoadedToVideoMemory(bool p_loaded)	{ m_textureData->setLoadedToVideoMemory(p_loaded);	}
 
 	private:
 		// Increment the reference counter when creating a handle
-		Texture2DHandle(Texture2D *p_textureData) : m_textureData(p_textureData) { m_textureData->incRefCounter(); }
+		Texture2DHandle(Texture2D *p_textureData) : m_textureData(p_textureData), m_handle(m_textureData->m_handle) { m_textureData->incRefCounter(); }
 
 		inline unsigned int &getHandleRef() { return m_textureData->m_handle; }
 
 		// Returns a void pointer to the pixel data
 		const inline void *getData() { return m_textureData->getData(); }
 
+		unsigned int m_handle;
 		Texture2D *m_textureData;
 	};
 		
@@ -380,6 +290,40 @@ public:
 	Texture2DHandle load(const std::string &p_filename, unsigned int p_textureHandle);
 	Texture2DHandle load(const std::string &p_filename);
 	
+	Texture2DHandle getDefaultTexture(MaterialType p_materialType = MaterialType::MaterialType_Diffuse)
+	{
+		Texture2D *returnTexture = m_default2DTexture;
+
+		switch(p_materialType)
+		{
+		case MaterialType_Diffuse:
+			break;
+		case MaterialType_Normal:
+			returnTexture = m_defaultNormal;
+			break;
+		case MaterialType_Emissive:
+			returnTexture = m_defaultEmissive;
+			break;
+		case MaterialType_Combined:
+			break;
+		case MaterialType_Roughness:
+			break;
+		case MaterialType_Metalness:
+			break;
+		case MaterialType_Height:
+			returnTexture = m_defaultHeight;
+			break;
+		case MaterialType_AmbientOcclusion:
+			break;
+		case MaterialType_NumOfTypes_Extended:
+			break;
+		default:
+			break;
+		}
+
+		return Texture2DHandle(returnTexture);
+	}
+
 protected:
 	// Default textures used in place of missing ones when loading textures
 	Texture2D *m_default2DTexture;

+ 5 - 20
Praxis3D/Source/Universal.cpp

@@ -176,7 +176,7 @@ UniversalObject *UniversalScene::getObject(std::string p_name)
 	return returnObject;
 }
 
-void UniversalScene::createObjectLink(SystemObject *p_subject, SystemObject *p_observer)
+void UniversalScene::createObjectLink(ObservedSubject *p_subject, SystemObject *p_observer)
 {
 	// Get the correlating potential and desired changes
 	BitMask changes = p_subject->getPotentialSystemChanges() & p_observer->getDesiredSystemChanges();
@@ -186,29 +186,14 @@ void UniversalScene::createObjectLink(SystemObject *p_subject, SystemObject *p_o
 	{
 		m_objectChangeController->registerSubject(p_subject, changes, p_observer);
 		
-		ObjectLinkData linkData = { p_subject, p_observer, p_subject->getName(), p_observer->getName() };
-		m_objectLinks.push_back(linkData);
-
 		// Inform the subject about established link
 		p_subject->postChanges(Systems::Changes::Link);
 	}
 }
-void UniversalScene::createObjectLink(UniversalObject *p_subject, SystemObject *p_observer)
-{
-	// Get the correlating potential and desired changes
-	BitMask changes = p_subject->getPotentialSystemChanges() & p_observer->getDesiredSystemChanges();
-
-	// If any changes happened
-	if(changes)
-	{
-		m_objectChangeController->registerSubject(p_subject, changes, p_observer);
 
-		ObjectLinkData linkData = { p_subject, p_observer, p_subject->getName(), p_observer->getName() };
-		m_objectLinks.push_back(linkData);
-
-		// Inform the subject about established link
-		p_subject->postChanges(Systems::Changes::Link);
-	}
+void UniversalScene::removeObjectLink(ObservedSubject *p_subject, SystemObject *p_observer)
+{
+	m_objectChangeController->unregisterSubject(p_subject, p_observer);
 }
 
 void UniversalScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changes)
@@ -325,7 +310,7 @@ void UniversalObject::unextend(SystemScene *p_systemScene)
 	SystemObjectMap::iterator systemObjectIterator = m_objectExtensions.find(systemSceneType);
 	_ASSERT(systemObjectIterator != m_objectExtensions.end());
 	// "The object to delete doesn't exist in the scene."
-	// TODO ERROR return from the function if the scene doesnt exist
+	// TODO ERROR return from the function if the scene doesn't exist
 	if(systemObjectIterator == m_objectExtensions.end())
 	{
 		return;

+ 5 - 6
Praxis3D/Source/Universal.h

@@ -24,8 +24,11 @@ public:
 
 	UniversalObject *getObject(std::string p_name);
 	
-	void createObjectLink(SystemObject *p_subject, SystemObject *p_observer);
-	void createObjectLink(UniversalObject *p_subject, SystemObject *p_observer);
+	// Creates a link between the subject and the observer, so that the observer can be notified of any data changes within the subject
+	void createObjectLink(ObservedSubject *p_subject, SystemObject *p_observer);
+	
+	// Removes the link between the subject and the observer, so that the observer will no longer be notified of any data changes within the subject
+	void removeObjectLink(ObservedSubject *p_subject, SystemObject *p_observer);
 
 	// Sends a one-off notification about a change, without requiring the object linking
 	void sendChange(SystemObject *p_subject, SystemObject *p_observer, BitMask p_changedBits)
@@ -58,11 +61,7 @@ protected:
 	ChangeController *m_objectChangeController;
 
 	SystemSceneMap		m_systemScenes;
-	ObjectLinkList		m_objectLinks;
 	UniversalObjectList	m_objects;
-
-public:
-	const ObjectLinkList &getObjectLinksList() { return m_objectLinks; }
 };
 
 class UniversalObject : public Observer, public ObservedSubject

+ 16 - 2
Praxis3D/Source/Utilities.h

@@ -200,7 +200,7 @@ namespace Utilities
 	}
 
 	// Template std::pair comparator, only compares the first element
-	template <class T1, class T2, class Pred = std::less<T2>>
+	template<class T1, class T2, class Pred = std::less<T2>>
 	struct sort_pair_first 
 	{
 		bool operator()(const std::pair<T1, T2>&left, const std::pair<T1, T2>&right) 
@@ -211,7 +211,7 @@ namespace Utilities
 	};
 
 	// Template std::pair comparator, only compares the second element
-	template <class T1, class T2, class Pred = std::less<T2>>
+	template<class T1, class T2, class Pred = std::less<T2>>
 	struct sort_pair_second 
 	{
 		bool operator()(const std::pair<T1, T2>&left, const std::pair<T1, T2>&right) 
@@ -240,4 +240,18 @@ namespace Utilities
 		else
 			return Scancode::Key_Invalid;
 	}
+
+	// Calculates value with a "1" bit at the given bit position (i.e. bit-shifting "1" by the given number of places)
+	template<class T>
+	T getBitmask(T p_bitShiftPosition)
+	{
+		return ((T)1 << p_bitShiftPosition);
+	}
+
+	// Calculates value with a "1" bit at the given bit position (i.e. bit-shifting "1" by the given number of places)
+	template<class T>
+	T getBitmask(int p_bitShiftPosition)
+	{
+		return ((T)1 << p_bitShiftPosition);
+	}
 }

+ 134 - 0
Praxis3D/Source/WorldScene.cpp

@@ -0,0 +1,134 @@
+
+#include "NullSystemObjects.h"
+#include "SceneLoader.h"
+#include "WorldScene.h"
+
+SystemObject *WorldScene::createObject(const PropertySet &p_properties)
+{
+	ErrorCode objPoolError = ErrorCode::Failure;
+	SystemObject *returnObject = nullptr;
+
+	// Try to add a new object to the pool
+	objPoolError = m_gameObjects.add(this, p_properties.getPropertyByID(Properties::Name).getString(), &m_sceneLoader);
+	
+	// If adding a new object was successful, continue to load data
+	if(objPoolError == ErrorCode::Success)
+	{
+		// The newly added object in the pool
+		auto newGameObject = m_gameObjects.getLastRawObject();
+				
+		// Load property data
+		for(decltype(p_properties.getNumProperties()) i = 0, size = p_properties.getNumProperties(); i < size; i++)
+		{
+			switch(p_properties[i].getPropertyID())
+			{
+			case Properties::ID:
+				{
+					// Get the game object ID
+					auto objectIDdesired = static_cast<std::size_t>(p_properties.getPropertyByID(Properties::ID).getInt());
+
+					// Try to register the object with the given game object ID
+					auto objectIDactual = m_objectRegister.registerObject(newGameObject, objectIDdesired);
+
+					// If the object wasn't registered with the given game object ID, log an error
+					if(objectIDdesired != objectIDactual)
+					{
+						// If an object already exists at the given game object ID, log a duplicate ID error, otherwise log an invalid ID error
+						if(m_objectRegister.getObject(objectIDdesired) != nullptr)
+							ErrHandlerLoc::get().log(ErrorCode::Duplicate_object_id, ErrorSource::Source_World, newGameObject->getName());
+						else
+							ErrHandlerLoc::get().log(ErrorCode::Invalid_object_id, ErrorSource::Source_World, newGameObject->getName());
+					}
+				}
+				break;
+			case Properties::Parent:
+				{
+					// Get the ID if the parent object
+					decltype(GameObject::m_id) parentID = p_properties[i].getInt();
+
+					// Try to get the parent game object, from the object register, by its ID
+					auto parentObject = m_objectRegister.getObject(parentID);
+
+					// Check if the retrieved parent game object is valid
+					if(parentObject != nullptr)
+					{
+						// If it is valid, set it as the parent
+						newGameObject->setParent(parentObject);
+					}
+					else
+					{
+						// If it is not valid, add it to the unassigned parent list, to be set later
+						// The game object with the parent ID probably hasn't been loaded yet
+						m_unassignedParents.emplace_back(newGameObject, parentID);
+					}
+				}
+				break;
+			case Properties::LocalPosition:
+				newGameObject->m_localSpace.m_position = p_properties[i].getVec3f();
+				break;
+			case Properties::LocalRotation:
+				newGameObject->m_localSpace.m_rotationEuler = p_properties[i].getVec3f();
+				break;
+			case Properties::LocalRotationQuaternion:
+				newGameObject->m_localSpace.m_rotationQuat = p_properties[i].getVec4f();
+				break;
+			case Properties::LocalScale:
+				newGameObject->m_localSpace.m_scale = p_properties[i].getVec3f();
+				break;
+			}
+		}
+
+		// Declare data struct for children whose IDs haven't been registered yet
+		GameObjectAndChildren unassignedChildren(newGameObject);
+
+		// Get the children property set array
+		auto &children = p_properties.getPropertySetByID(Properties::Children);
+
+		// Iterate over every child entry
+		for(decltype(children.getNumPropertySets()) i = 0, size = children.getNumPropertySets(); i < size; i++)
+		{
+			decltype(GameObject::m_id) childID = children.getPropertySet(i).getPropertyByID(Properties::ID).getInt();
+								
+			// Try to get the child game object, from the object register, by its ID
+			auto childObject = m_objectRegister.getObject(childID);
+
+			// Check if the retrieved child game object is valid
+			if(childObject != nullptr)
+			{
+				// If it is valid, set it as the parent
+				newGameObject->addChild(*childObject);
+			}
+			else
+			{
+				// If it is not valid, add it to the unassigned children list, to be set later
+				// The game object with the child ID probably hasn't been loaded yet
+				unassignedChildren.m_children.push_back(childID);
+			}
+		}
+
+		// If there were any children added to the unassigned children list, add the struct to the main unassigned children list
+		if(!unassignedChildren.m_children.empty())
+			m_unassignedChildren.emplace_back(unassignedChildren);
+
+		auto &rendering = p_properties.getPropertySetByID(Properties::Rendering);
+		if(rendering)
+		{
+			m_sceneLoader->getSystemScene(Systems::Graphics);
+		}
+		
+		auto &scripting = p_properties.getPropertySetByID(Properties::Scripting);
+		if(scripting)
+		{
+
+		}
+
+		returnObject = newGameObject;
+	}
+	else
+	{
+		// If valid type was not specified, or object creation failed, assign a null object instead
+		returnObject = g_nullSystemBase.getScene()->createObject(p_properties);
+	}
+
+    return returnObject;
+}

+ 77 - 0
Praxis3D/Source/WorldScene.h

@@ -0,0 +1,77 @@
+#pragma once
+
+#include "GameObject.h"
+#include "ObjectPool.h"
+#include "ObjectRegister.h"
+#include "System.h"
+#include "WorldTask.h"
+
+class WorldScene : public SystemScene
+{
+public:
+		
+	ErrorCode init();
+
+	ErrorCode setup(const PropertySet &p_properties);
+
+	void update(const float p_deltaTime);
+
+	ErrorCode preload();
+
+	void loadInBackground();
+
+	// Exports all the data of the scene (including all objects within) as a PropertySet (for example, used for saving to map file)
+	PropertySet exportObject();
+
+	SystemObject *createObject(const PropertySet &p_properties) override;
+	ErrorCode destroyObject(SystemObject *p_systemObject);
+
+	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType) { }
+
+	SystemTask *getSystemTask() { return m_worldTask; };
+	Systems::TypeID getSystemType() { return Systems::TypeID::World; };
+	BitMask getDesiredSystemChanges();
+
+private:
+	struct GameObjectAndParent
+	{
+		GameObjectAndParent()
+		{
+			m_gameObject = nullptr;
+			m_parent = 0;
+		}		
+		GameObjectAndParent(GameObject *p_gameObject, decltype(GameObject::m_id) p_parent)
+		{
+			m_gameObject = p_gameObject;
+			m_parent = p_parent;
+		}
+
+		GameObject *m_gameObject;
+		decltype(GameObject::m_id) m_parent;
+	};
+	struct GameObjectAndChildren
+	{
+		GameObjectAndChildren()
+		{
+			m_gameObject = nullptr;
+		}		
+		GameObjectAndChildren(GameObject *p_gameObject)
+		{
+			m_gameObject = p_gameObject;
+		}
+
+		GameObject *m_gameObject;
+		std::vector<decltype(GameObject::m_id)> m_children;
+	};
+
+	std::vector<GameObjectAndParent> m_unassignedParents;
+	std::vector<GameObjectAndChildren> m_unassignedChildren;
+
+	ObjectPool<GameObject> m_gameObjects;
+	WorldTask *m_worldTask;
+
+	ObjectRegisterConcurrent<GameObject*> m_objectRegister;
+};
+// UniqueID.h
+// UIDRegister.h
+// UniqueIDRegister.h

+ 19 - 0
Praxis3D/Source/WorldTask.cpp

@@ -0,0 +1,19 @@
+
+#include "WorldScene.h"
+#include "WorldTask.h"
+
+WorldTask::WorldTask(WorldScene *p_worldScene)
+	: SystemTask(p_worldScene), m_worldScene(p_worldScene)
+{
+
+}
+
+WorldTask::~WorldTask()
+{
+}
+
+void WorldTask::update(const float p_deltaTime)
+{
+	m_worldScene->update(p_deltaTime);
+}
+

+ 22 - 0
Praxis3D/Source/WorldTask.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "System.h"
+
+class WorldScene;
+
+class WorldTask : public SystemTask
+{
+	friend class WorldScene;
+public:
+	WorldTask(WorldScene *p_worldScene);
+	~WorldTask();
+
+	Systems::TypeID getSystemType() { return Systems::World; }
+
+	void update(const float p_deltaTime);
+
+	bool isPrimaryThreadOnly() { return false; }
+
+private:
+	WorldScene *m_worldScene;
+};