Quellcode durchsuchen

Merge remote-tracking branch 'refs/remotes/origin/development'

MichPerry-GG vor 12 Jahren
Ursprung
Commit
7414643af9
100 geänderte Dateien mit 9201 neuen und 4847 gelöschten Zeilen
  1. 0 3
      README.md
  2. 24 0
      engine/compilers/VisualStudio 2010/Torque 2D.vcxproj
  3. 78 0
      engine/compilers/VisualStudio 2010/Torque 2D.vcxproj.filters
  4. 25 0
      engine/compilers/VisualStudio 2012/Torque 2D.vcxproj
  5. 78 0
      engine/compilers/VisualStudio 2012/Torque 2D.vcxproj.filters
  6. 80 0
      engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj
  7. 1 1
      engine/compilers/Xcode/Torque2D.xcodeproj/xcshareddata/xcschemes/Torque2D.xcscheme
  8. 80 0
      engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj
  9. 1 17
      engine/source/2d/assets/AnimationAsset_ScriptBinding.h
  10. 62 57
      engine/source/2d/assets/ImageAsset.cc
  11. 2 2
      engine/source/2d/assets/ImageAsset.h
  12. 4 4
      engine/source/2d/assets/ParticleAsset.cc
  13. 2 2
      engine/source/2d/assets/ParticleAsset.h
  14. 4 4
      engine/source/2d/assets/ParticleAssetEmitter.cc
  15. 2 2
      engine/source/2d/assets/ParticleAssetEmitter.h
  16. 74 39
      engine/source/2d/assets/ParticleAssetField.cc
  17. 2 2
      engine/source/2d/assets/ParticleAssetField.h
  18. 22 19
      engine/source/2d/assets/ParticleAssetFieldCollection.cc
  19. 2 2
      engine/source/2d/assets/ParticleAssetFieldCollection.h
  20. 87 0
      engine/source/2d/controllers/AmbientForceController.cc
  21. 60 0
      engine/source/2d/controllers/AmbientForceController.h
  22. 59 0
      engine/source/2d/controllers/AmbientForceController_ScriptBinding.h
  23. 390 0
      engine/source/2d/controllers/BuoyancyController.cc
  24. 86 0
      engine/source/2d/controllers/BuoyancyController.h
  25. 23 0
      engine/source/2d/controllers/BuoyancyController_ScriptBinding.h
  26. 137 0
      engine/source/2d/controllers/PointForceController.cc
  27. 69 0
      engine/source/2d/controllers/PointForceController.h
  28. 94 0
      engine/source/2d/controllers/PointForceController_ScriptBinding.h
  29. 51 0
      engine/source/2d/controllers/core/GroupedSceneController.cc
  30. 64 0
      engine/source/2d/controllers/core/GroupedSceneController.h
  31. 23 0
      engine/source/2d/controllers/core/GroupedSceneController_ScriptBinding.h
  32. 79 0
      engine/source/2d/controllers/core/PickingSceneController.cc
  33. 76 0
      engine/source/2d/controllers/core/PickingSceneController.h
  34. 187 0
      engine/source/2d/controllers/core/PickingSceneController_ScriptBinding.h
  35. 48 0
      engine/source/2d/controllers/core/SceneController.h
  36. 129 75
      engine/source/2d/core/SpriteBatch.cc
  37. 14 35
      engine/source/2d/core/SpriteBatch.h
  38. 40 32
      engine/source/2d/core/SpriteBatchItem.cc
  39. 7 2
      engine/source/2d/core/SpriteBatchItem.h
  40. 238 0
      engine/source/2d/core/SpriteBatchQuery.cc
  41. 85 0
      engine/source/2d/core/SpriteBatchQuery.h
  42. 78 0
      engine/source/2d/core/SpriteBatchQueryResult.h
  43. 43 0
      engine/source/2d/core/Utility.cc
  44. 6 8
      engine/source/2d/core/Utility.h
  45. 3 5
      engine/source/2d/core/Vector2.cc
  46. 21 20
      engine/source/2d/core/Vector2.h
  47. 23 3
      engine/source/2d/core/Vector2_ScriptBinding.h
  48. 32 8
      engine/source/2d/gui/SceneWindow.cc
  49. 12 0
      engine/source/2d/gui/SceneWindow.h
  50. 120 1
      engine/source/2d/gui/SceneWindow_ScriptBinding.h
  51. 2 1
      engine/source/2d/gui/guiSceneObjectCtrl.cc
  52. 31 31
      engine/source/2d/scene/DebugDraw.cc
  53. 14 10
      engine/source/2d/scene/DebugDraw.h
  54. 12 0
      engine/source/2d/scene/DebugStats.h
  55. 1374 1020
      engine/source/2d/scene/Scene.cc
  56. 23 47
      engine/source/2d/scene/Scene.h
  57. 10 1
      engine/source/2d/scene/SceneRenderState.h
  58. 31 128
      engine/source/2d/scene/Scene_ScriptBinding.h
  59. 9 8
      engine/source/2d/scene/WorldQuery.cc
  60. 16 16
      engine/source/2d/sceneobject/CompositeSprite.cc
  61. 2 2
      engine/source/2d/sceneobject/CompositeSprite.h
  62. 342 0
      engine/source/2d/sceneobject/CompositeSprite_ScriptBinding.h
  63. 1 1
      engine/source/2d/sceneobject/ImageFont.cc
  64. 1 1
      engine/source/2d/sceneobject/ParticlePlayer.cc
  65. 291 219
      engine/source/2d/sceneobject/SceneObject.cc
  66. 14 17
      engine/source/2d/sceneobject/SceneObject.h
  67. 92 0
      engine/source/2d/sceneobject/SceneObjectList.cc
  68. 62 0
      engine/source/2d/sceneobject/SceneObjectList.h
  69. 296 0
      engine/source/2d/sceneobject/SceneObjectSet.cc
  70. 172 0
      engine/source/2d/sceneobject/SceneObjectSet.h
  71. 194 0
      engine/source/2d/sceneobject/SceneObjectSet_ScriptBinding.h
  72. 147 155
      engine/source/2d/sceneobject/SceneObject_ScriptBinding.h
  73. 2 2
      engine/source/2d/sceneobject/Trigger.cc
  74. 33 30
      engine/source/algorithm/hashFunction.cc
  75. 26 23
      engine/source/assets/assetQuery.cc
  76. 4 4
      engine/source/assets/assetQuery.h
  77. 45 39
      engine/source/assets/assetTagsManifest.cc
  78. 4 4
      engine/source/assets/assetTagsManifest.h
  79. 13 0
      engine/source/audio/audioFunctions.cc
  80. 120 8
      engine/source/collection/vector.h
  81. 65 55
      engine/source/component/behaviors/behaviorComponent.cpp
  82. 4 4
      engine/source/component/behaviors/behaviorComponent.h
  83. 1 3
      engine/source/component/dynamicConsoleMethodComponent.cpp
  84. 137 61
      engine/source/console/compiledEval.cc
  85. 30 52
      engine/source/console/consoleTypes.cc
  86. 1 1
      engine/source/console/consoleTypes.h
  87. 13 0
      engine/source/graphics/TextureDictionary.cc
  88. 3 2
      engine/source/graphics/TextureDictionary.h
  89. 1 1
      engine/source/graphics/TextureHandle.h
  90. 32 0
      engine/source/graphics/TextureManager.cc
  91. 2 1
      engine/source/graphics/TextureManager.h
  92. 1 1
      engine/source/math/rectClipper.cpp
  93. 3 2
      engine/source/memory/frameAllocator.h
  94. 4 8
      engine/source/module/moduleDefinition.cc
  95. 0 10
      engine/source/module/moduleDefinition.h
  96. 2513 2453
      engine/source/module/moduleManager.cc
  97. 3 0
      engine/source/module/moduleManager.h
  98. 17 19
      engine/source/module/moduleManager_ScriptBinding.h
  99. 2 2
      engine/source/module/tamlModuleIdUpdateVisitor.h
  100. 59 62
      engine/source/persistence/taml/taml.cc

+ 0 - 3
README.md

@@ -1,3 +0,0 @@
-MIT Licensed Open Source version of Torque 2D from [GarageGames LLC](http://www.garagegames.com).
-
-You can find the main Wiki page [here](https://github.com/GarageGames/Torque2D/wiki).

+ 24 - 0
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj

@@ -239,6 +239,11 @@
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetEmitter.cc" />
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetField.cc" />
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetFieldCollection.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\AmbientForceController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\BuoyancyController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\core\GroupedSceneController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\core\PickingSceneController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\PointForceController.cc" />
     <ClCompile Include="..\..\source\2d\core\BatchRender.cc" />
     <ClCompile Include="..\..\source\2d\core\CoreMath.cc" />
     <ClCompile Include="..\..\source\2d\core\ParticleSystem.cc" />
@@ -246,6 +251,7 @@
     <ClCompile Include="..\..\source\2d\core\SpriteBase.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteBatch.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteBatchItem.cc" />
+    <ClCompile Include="..\..\source\2d\core\SpriteBatchQuery.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteProxyBase.cc" />
     <ClCompile Include="..\..\source\2d\core\Utility.cc" />
     <ClCompile Include="..\..\source\2d\core\Vector2.cc" />
@@ -257,6 +263,8 @@
     <ClCompile Include="..\..\source\2d\sceneobject\ImageFont.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ParticlePlayer.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObject.cc" />
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc" />
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\Scroller.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ShapeVector.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\Sprite.cc" />
@@ -612,6 +620,17 @@
     <ClInclude Include="..\..\source\2d\assets\ParticleAssetField.h" />
     <ClInclude Include="..\..\source\2d\assets\ParticleAssetFieldCollection.h" />
     <ClInclude Include="..\..\source\2d\assets\ParticleAsset_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\SceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\core\BatchRender.h" />
     <ClInclude Include="..\..\source\2d\core\CoreMath.h" />
     <ClInclude Include="..\..\source\2d\core\ParticleSystem.h" />
@@ -621,6 +640,8 @@
     <ClInclude Include="..\..\source\2d\core\SpriteBase_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteBatch.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteBatchItem.h" />
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQuery.h" />
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQueryResult.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteProxyBase.h" />
     <ClInclude Include="..\..\source\2d\core\Utility.h" />
     <ClInclude Include="..\..\source\2d\core\Vector2.h" />
@@ -639,8 +660,11 @@
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectTimerEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller.h" />

+ 78 - 0
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj.filters

@@ -166,6 +166,12 @@
     <Filter Include="testing\tests">
       <UniqueIdentifier>{57e1271d-4358-4180-b168-4b9c2cbac907}</UniqueIdentifier>
     </Filter>
+    <Filter Include="2d\controllers">
+      <UniqueIdentifier>{9f1a4eec-8f4f-46f1-8af0-e442729cc71a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="2d\controllers\core">
+      <UniqueIdentifier>{6aeaed85-4546-4496-8415-72a392e830ba}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1269,6 +1275,30 @@
     <ClCompile Include="..\..\source\persistence\taml\tamlCustom.cc">
       <Filter>persistence\taml</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc">
+      <Filter>2d\sceneobject</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc">
+      <Filter>2d\sceneobject</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\AmbientForceController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\BuoyancyController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\PointForceController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\core\GroupedSceneController.cc">
+      <Filter>2d\controllers\core</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\core\PickingSceneController.cc">
+      <Filter>2d\controllers\core</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\core\SpriteBatchQuery.cc">
+      <Filter>2d\core</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2589,6 +2619,54 @@
     <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h">
       <Filter>sim</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet_ScriptBinding.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController_ScriptBinding.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController_ScriptBinding.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\SceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQuery.h">
+      <Filter>2d\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQueryResult.h">
+      <Filter>2d\core</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 25 - 0
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj

@@ -92,6 +92,7 @@
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <WarningLevel>Level3</WarningLevel>
       <DisableSpecificWarnings>4800;4100;4127;4512</DisableSpecificWarnings>
+      <ShowIncludes>false</ShowIncludes>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -245,6 +246,11 @@
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetEmitter.cc" />
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetField.cc" />
     <ClCompile Include="..\..\source\2d\assets\ParticleAssetFieldCollection.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\AmbientForceController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\core\GroupedSceneController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\core\PickingSceneController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\PointForceController.cc" />
+    <ClCompile Include="..\..\source\2d\controllers\BuoyancyController.cc" />
     <ClCompile Include="..\..\source\2d\core\BatchRender.cc" />
     <ClCompile Include="..\..\source\2d\core\CoreMath.cc" />
     <ClCompile Include="..\..\source\2d\core\ParticleSystem.cc" />
@@ -252,6 +258,7 @@
     <ClCompile Include="..\..\source\2d\core\SpriteBase.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteBatch.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteBatchItem.cc" />
+    <ClCompile Include="..\..\source\2d\core\SpriteBatchQuery.cc" />
     <ClCompile Include="..\..\source\2d\core\SpriteProxyBase.cc" />
     <ClCompile Include="..\..\source\2d\core\Utility.cc" />
     <ClCompile Include="..\..\source\2d\core\Vector2.cc" />
@@ -263,6 +270,8 @@
     <ClCompile Include="..\..\source\2d\sceneobject\ImageFont.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ParticlePlayer.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObject.cc" />
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc" />
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\Scroller.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ShapeVector.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\Sprite.cc" />
@@ -618,6 +627,17 @@
     <ClInclude Include="..\..\source\2d\assets\ParticleAssetField.h" />
     <ClInclude Include="..\..\source\2d\assets\ParticleAssetFieldCollection.h" />
     <ClInclude Include="..\..\source\2d\assets\ParticleAsset_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\core\SceneController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController.h" />
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\core\BatchRender.h" />
     <ClInclude Include="..\..\source\2d\core\CoreMath.h" />
     <ClInclude Include="..\..\source\2d\core\ParticleSystem.h" />
@@ -627,6 +647,8 @@
     <ClInclude Include="..\..\source\2d\core\SpriteBase_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteBatch.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteBatchItem.h" />
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQuery.h" />
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQueryResult.h" />
     <ClInclude Include="..\..\source\2d\core\SpriteProxyBase.h" />
     <ClInclude Include="..\..\source\2d\core\Utility.h" />
     <ClInclude Include="..\..\source\2d\core\Vector2.h" />
@@ -645,8 +667,11 @@
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller_ScriptBinding.h" />

+ 78 - 0
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj.filters

@@ -166,6 +166,12 @@
     <Filter Include="testing\tests">
       <UniqueIdentifier>{57e1271d-4358-4180-b168-4b9c2cbac907}</UniqueIdentifier>
     </Filter>
+    <Filter Include="2d\controllers">
+      <UniqueIdentifier>{a9e97335-bed5-4f6a-9959-12f5f41dbdcb}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="2d\controllers\core">
+      <UniqueIdentifier>{e11e344e-6418-4ed0-980a-77d66cd64d65}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1269,6 +1275,30 @@
     <ClCompile Include="..\..\source\persistence\taml\tamlCustom.cc">
       <Filter>persistence\taml</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc">
+      <Filter>2d\sceneobject</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc">
+      <Filter>2d\sceneobject</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\BuoyancyController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\PointForceController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\core\GroupedSceneController.cc">
+      <Filter>2d\controllers\core</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\core\PickingSceneController.cc">
+      <Filter>2d\controllers\core</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\controllers\AmbientForceController.cc">
+      <Filter>2d\controllers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\2d\core\SpriteBatchQuery.cc">
+      <Filter>2d\core</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2586,6 +2616,54 @@
     <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h">
       <Filter>sim</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet_ScriptBinding.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\BuoyancyController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\PointForceController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\GroupedSceneController_ScriptBinding.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\PickingSceneController_ScriptBinding.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\core\SceneController.h">
+      <Filter>2d\controllers\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\controllers\AmbientForceController_ScriptBinding.h">
+      <Filter>2d\controllers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQuery.h">
+      <Filter>2d\core</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\core\SpriteBatchQueryResult.h">
+      <Filter>2d\core</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 80 - 0
engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj

@@ -11,13 +11,21 @@
 		2A033011165D1D4100E9CD70 /* platformFileIoTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A033010165D1D4100E9CD70 /* platformFileIoTests.cc */; };
 		2A25739016A48DAC00363C6F /* ParticlePlayer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A25738E16A48DAC00363C6F /* ParticlePlayer.cc */; };
 		2A6F78CE16A4528C005C76D9 /* ParticleAssetEmitter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A6F78CC16A4528C005C76D9 /* ParticleAssetEmitter.cc */; };
+		2AA6865F16D69943003CEF0A /* SceneObjectList.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA6865A16D69943003CEF0A /* SceneObjectList.cc */; };
+		2AA6866016D69943003CEF0A /* SceneObjectSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */; };
+		2AB14A0516D7CDC300EABBF2 /* PointForceController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB14A0316D7CDC200EABBF2 /* PointForceController.cc */; };
+		2AB4C19E16DE9F0600B02479 /* GroupedSceneController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C19816DE9F0600B02479 /* GroupedSceneController.cc */; };
+		2AB4C19F16DE9F0600B02479 /* PickingSceneController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C19B16DE9F0600B02479 /* PickingSceneController.cc */; };
+		2AB4C1A316DE9F1100B02479 /* AmbientForceController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C1A116DE9F1100B02479 /* AmbientForceController.cc */; };
 		2AB97A1D16B66BC70080F940 /* tamlCustom.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB97A1B16B66BC70080F940 /* tamlCustom.cc */; };
 		2ABF5C8F16569A0C00BBBF1D /* osxMutex.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF5C8E16569A0C00BBBF1D /* osxMutex.mm */; };
 		2AC4404516B0142B00FC4091 /* ImageFont.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AC4404316B0142B00FC4091 /* ImageFont.cc */; };
 		2AC5C7E81667C85700A0D046 /* platformStringTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AC5C7E71667C85700A0D046 /* platformStringTests.cc */; };
+		2ACF5A2816E52D4B00F838D9 /* SpriteBatchQuery.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2ACF5A2516E52D4B00F838D9 /* SpriteBatchQuery.cc */; };
 		2ACFC0A8166CE1AB00FE7370 /* platformMemoryTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2ACFC0A7166CE1AB00FE7370 /* platformMemoryTests.cc */; };
 		2ADCAC1516A41E5500E07619 /* ParticleAsset.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2ADCAC1116A41E5500E07619 /* ParticleAsset.cc */; };
 		2ADCAC1716A41E5500E07619 /* ParticleAssetField.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2ADCAC1316A41E5500E07619 /* ParticleAssetField.cc */; };
+		2AE2F55D16D6B08800B6A058 /* BuoyancyController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AE2F55B16D6B08800B6A058 /* BuoyancyController.cc */; };
 		2AE5B54216A6D860006908D5 /* ParticleAssetFieldCollection.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AE5B54016A6D860006908D5 /* ParticleAssetFieldCollection.cc */; };
 		2AE851D21681E56E00193F17 /* color.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AE851D11681E56E00193F17 /* color.cc */; };
 		2AF1C54016B439BB00C1CF3A /* declaredAssets.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AF1C53C16B439BB00C1CF3A /* declaredAssets.cc */; };
@@ -451,6 +459,24 @@
 		2A25738F16A48DAC00363C6F /* ParticlePlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticlePlayer.h; sourceTree = "<group>"; };
 		2A6F78CC16A4528C005C76D9 /* ParticleAssetEmitter.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleAssetEmitter.cc; sourceTree = "<group>"; };
 		2A6F78CD16A4528C005C76D9 /* ParticleAssetEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleAssetEmitter.h; sourceTree = "<group>"; };
+		2AA6865A16D69943003CEF0A /* SceneObjectList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneObjectList.cc; sourceTree = "<group>"; };
+		2AA6865B16D69943003CEF0A /* SceneObjectList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectList.h; sourceTree = "<group>"; };
+		2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectSet_ScriptBinding.h; sourceTree = "<group>"; };
+		2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneObjectSet.cc; sourceTree = "<group>"; };
+		2AA6865E16D69943003CEF0A /* SceneObjectSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectSet.h; sourceTree = "<group>"; };
+		2AB14A0216D7CDC200EABBF2 /* PointForceController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointForceController_ScriptBinding.h; path = controllers/PointForceController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB14A0316D7CDC200EABBF2 /* PointForceController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PointForceController.cc; path = controllers/PointForceController.cc; sourceTree = "<group>"; };
+		2AB14A0416D7CDC300EABBF2 /* PointForceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointForceController.h; path = controllers/PointForceController.h; sourceTree = "<group>"; };
+		2AB4C19716DE9F0600B02479 /* GroupedSceneController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GroupedSceneController_ScriptBinding.h; path = controllers/core/GroupedSceneController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C19816DE9F0600B02479 /* GroupedSceneController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GroupedSceneController.cc; path = controllers/core/GroupedSceneController.cc; sourceTree = "<group>"; };
+		2AB4C19916DE9F0600B02479 /* GroupedSceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GroupedSceneController.h; path = controllers/core/GroupedSceneController.h; sourceTree = "<group>"; };
+		2AB4C19A16DE9F0600B02479 /* PickingSceneController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PickingSceneController_ScriptBinding.h; path = controllers/core/PickingSceneController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C19B16DE9F0600B02479 /* PickingSceneController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PickingSceneController.cc; path = controllers/core/PickingSceneController.cc; sourceTree = "<group>"; };
+		2AB4C19C16DE9F0600B02479 /* PickingSceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PickingSceneController.h; path = controllers/core/PickingSceneController.h; sourceTree = "<group>"; };
+		2AB4C19D16DE9F0600B02479 /* SceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SceneController.h; path = controllers/core/SceneController.h; sourceTree = "<group>"; };
+		2AB4C1A016DE9F1100B02479 /* AmbientForceController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AmbientForceController_ScriptBinding.h; path = controllers/AmbientForceController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C1A116DE9F1100B02479 /* AmbientForceController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AmbientForceController.cc; path = controllers/AmbientForceController.cc; sourceTree = "<group>"; };
+		2AB4C1A216DE9F1100B02479 /* AmbientForceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AmbientForceController.h; path = controllers/AmbientForceController.h; sourceTree = "<group>"; };
 		2AB97A1B16B66BC70080F940 /* tamlCustom.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tamlCustom.cc; sourceTree = "<group>"; };
 		2AB97A1C16B66BC70080F940 /* tamlCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tamlCustom.h; sourceTree = "<group>"; };
 		2ABF5C8E16569A0C00BBBF1D /* osxMutex.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxMutex.mm; sourceTree = "<group>"; };
@@ -458,6 +484,9 @@
 		2AC4404316B0142B00FC4091 /* ImageFont.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFont.cc; sourceTree = "<group>"; };
 		2AC4404416B0142B00FC4091 /* ImageFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont.h; sourceTree = "<group>"; };
 		2AC5C7E71667C85700A0D046 /* platformStringTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = platformStringTests.cc; path = ../../../source/testing/tests/platformStringTests.cc; sourceTree = "<group>"; };
+		2ACF5A2516E52D4B00F838D9 /* SpriteBatchQuery.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteBatchQuery.cc; sourceTree = "<group>"; };
+		2ACF5A2616E52D4B00F838D9 /* SpriteBatchQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatchQuery.h; sourceTree = "<group>"; };
+		2ACF5A2716E52D4B00F838D9 /* SpriteBatchQueryResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatchQueryResult.h; sourceTree = "<group>"; };
 		2ACFC0A7166CE1AB00FE7370 /* platformMemoryTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = platformMemoryTests.cc; path = ../../../source/testing/tests/platformMemoryTests.cc; sourceTree = "<group>"; };
 		2AD07B2616D15F5A0070DC79 /* simObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simObjectTimerEvent.h; sourceTree = "<group>"; };
 		2AD35A541663608E00C75F30 /* platformFileIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platformFileIO.h; sourceTree = "<group>"; };
@@ -467,6 +496,9 @@
 		2ADCAC1216A41E5500E07619 /* ParticleAsset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleAsset.h; sourceTree = "<group>"; };
 		2ADCAC1316A41E5500E07619 /* ParticleAssetField.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleAssetField.cc; sourceTree = "<group>"; };
 		2ADCAC1416A41E5500E07619 /* ParticleAssetField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleAssetField.h; sourceTree = "<group>"; };
+		2AE2F55A16D6B08800B6A058 /* BuoyancyController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuoyancyController_ScriptBinding.h; path = controllers/BuoyancyController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AE2F55B16D6B08800B6A058 /* BuoyancyController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BuoyancyController.cc; path = controllers/BuoyancyController.cc; sourceTree = "<group>"; };
+		2AE2F55C16D6B08800B6A058 /* BuoyancyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuoyancyController.h; path = controllers/BuoyancyController.h; sourceTree = "<group>"; };
 		2AE5B54016A6D860006908D5 /* ParticleAssetFieldCollection.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleAssetFieldCollection.cc; sourceTree = "<group>"; };
 		2AE5B54116A6D860006908D5 /* ParticleAssetFieldCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleAssetFieldCollection.h; sourceTree = "<group>"; };
 		2AE851D11681E56E00193F17 /* color.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = color.cc; sourceTree = "<group>"; };
@@ -1344,6 +1376,37 @@
 			name = tests;
 			sourceTree = "<group>";
 		};
+		2AB4C19616DE9EEA00B02479 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				2AB4C19716DE9F0600B02479 /* GroupedSceneController_ScriptBinding.h */,
+				2AB4C19816DE9F0600B02479 /* GroupedSceneController.cc */,
+				2AB4C19916DE9F0600B02479 /* GroupedSceneController.h */,
+				2AB4C19A16DE9F0600B02479 /* PickingSceneController_ScriptBinding.h */,
+				2AB4C19B16DE9F0600B02479 /* PickingSceneController.cc */,
+				2AB4C19C16DE9F0600B02479 /* PickingSceneController.h */,
+				2AB4C19D16DE9F0600B02479 /* SceneController.h */,
+			);
+			name = core;
+			sourceTree = "<group>";
+		};
+		2AB4F1CF16D55B7300C9A27B /* controllers */ = {
+			isa = PBXGroup;
+			children = (
+				2AB4C1A016DE9F1100B02479 /* AmbientForceController_ScriptBinding.h */,
+				2AB4C1A116DE9F1100B02479 /* AmbientForceController.cc */,
+				2AB4C1A216DE9F1100B02479 /* AmbientForceController.h */,
+				2AB4C19616DE9EEA00B02479 /* core */,
+				2AB14A0216D7CDC200EABBF2 /* PointForceController_ScriptBinding.h */,
+				2AB14A0316D7CDC200EABBF2 /* PointForceController.cc */,
+				2AB14A0416D7CDC300EABBF2 /* PointForceController.h */,
+				2AE2F55A16D6B08800B6A058 /* BuoyancyController_ScriptBinding.h */,
+				2AE2F55B16D6B08800B6A058 /* BuoyancyController.cc */,
+				2AE2F55C16D6B08800B6A058 /* BuoyancyController.h */,
+			);
+			name = controllers;
+			sourceTree = "<group>";
+		};
 		865A20BD1651589F00527C44 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
@@ -1741,6 +1804,7 @@
 		86BC7E7516518D4600D96ADF /* 2d */ = {
 			isa = PBXGroup;
 			children = (
+				2AB4F1CF16D55B7300C9A27B /* controllers */,
 				86BC7E7616518D4600D96ADF /* assets */,
 				86BC7E8016518D4600D96ADF /* core */,
 				86BC7E9616518D4600D96ADF /* gui */,
@@ -1779,6 +1843,9 @@
 		86BC7E8016518D4600D96ADF /* core */ = {
 			isa = PBXGroup;
 			children = (
+				2ACF5A2516E52D4B00F838D9 /* SpriteBatchQuery.cc */,
+				2ACF5A2616E52D4B00F838D9 /* SpriteBatchQuery.h */,
+				2ACF5A2716E52D4B00F838D9 /* SpriteBatchQueryResult.h */,
 				2AF3633716A9BBE0004ED7AA /* ParticleSystem.cc */,
 				2AF3633816A9BBE0004ED7AA /* ParticleSystem.h */,
 				86BC7E8116518D4600D96ADF /* BatchRender.cc */,
@@ -1854,6 +1921,11 @@
 		86BC7EB716518D4600D96ADF /* sceneobject */ = {
 			isa = PBXGroup;
 			children = (
+				2AA6865A16D69943003CEF0A /* SceneObjectList.cc */,
+				2AA6865B16D69943003CEF0A /* SceneObjectList.h */,
+				2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */,
+				2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */,
+				2AA6865E16D69943003CEF0A /* SceneObjectSet.h */,
 				2AC4404216B0142B00FC4091 /* ImageFont_ScriptBinding.h */,
 				2AC4404316B0142B00FC4091 /* ImageFont.cc */,
 				2AC4404416B0142B00FC4091 /* ImageFont.h */,
@@ -3198,6 +3270,14 @@
 				2AF1C54016B439BB00C1CF3A /* declaredAssets.cc in Sources */,
 				2AF1C54116B439BB00C1CF3A /* referencedAssets.cc in Sources */,
 				2AB97A1D16B66BC70080F940 /* tamlCustom.cc in Sources */,
+				2AA6865F16D69943003CEF0A /* SceneObjectList.cc in Sources */,
+				2AA6866016D69943003CEF0A /* SceneObjectSet.cc in Sources */,
+				2AE2F55D16D6B08800B6A058 /* BuoyancyController.cc in Sources */,
+				2AB14A0516D7CDC300EABBF2 /* PointForceController.cc in Sources */,
+				2AB4C19E16DE9F0600B02479 /* GroupedSceneController.cc in Sources */,
+				2AB4C19F16DE9F0600B02479 /* PickingSceneController.cc in Sources */,
+				2AB4C1A316DE9F1100B02479 /* AmbientForceController.cc in Sources */,
+				2ACF5A2816E52D4B00F838D9 /* SpriteBatchQuery.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 1 - 1
engine/compilers/Xcode/Torque2D.xcodeproj/xcshareddata/xcschemes/Torque2D.xcscheme

@@ -44,7 +44,7 @@
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
+      buildConfiguration = "Release"
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">

+ 80 - 0
engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj

@@ -7,8 +7,16 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		2AA6866A16D69968003CEF0A /* SceneObjectList.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA6866516D69968003CEF0A /* SceneObjectList.cc */; };
+		2AA6866B16D69968003CEF0A /* SceneObjectSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA6866816D69968003CEF0A /* SceneObjectSet.cc */; };
+		2AB14A0916D7CDCE00EABBF2 /* PointForceController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB14A0716D7CDCE00EABBF2 /* PointForceController.cc */; };
+		2AB4C1A716DE9F4B00B02479 /* AmbientForceController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C1A516DE9F4B00B02479 /* AmbientForceController.cc */; };
+		2AB4C1B016DE9F6700B02479 /* GroupedSceneController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C1AA16DE9F6700B02479 /* GroupedSceneController.cc */; };
+		2AB4C1B116DE9F6700B02479 /* PickingSceneController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4C1AD16DE9F6700B02479 /* PickingSceneController.cc */; };
 		2AB97A2116B66BE50080F940 /* tamlCustom.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AB97A1F16B66BE50080F940 /* tamlCustom.cc */; };
 		2AC4404E16B0144500FC4091 /* ImageFont.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AC4404C16B0144500FC4091 /* ImageFont.cc */; };
+		2ACF5A2C16E52D6A00F838D9 /* SpriteBatchQuery.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2ACF5A2916E52D6A00F838D9 /* SpriteBatchQuery.cc */; };
+		2AE2F55916D6B07200B6A058 /* BuoyancyController.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AE2F55716D6B07200B6A058 /* BuoyancyController.cc */; };
 		2AED7D9316B70102003482CF /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AED7D9216B70102003482CF /* CoreText.framework */; };
 		2AF1C54B16B439D900C1CF3A /* declaredAssets.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AF1C54716B439D900C1CF3A /* declaredAssets.cc */; };
 		2AF1C54C16B439D900C1CF3A /* referencedAssets.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AF1C54916B439D900C1CF3A /* referencedAssets.cc */; };
@@ -473,12 +481,36 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		2AA6866516D69968003CEF0A /* SceneObjectList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneObjectList.cc; sourceTree = "<group>"; };
+		2AA6866616D69968003CEF0A /* SceneObjectList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectList.h; sourceTree = "<group>"; };
+		2AA6866716D69968003CEF0A /* SceneObjectSet_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectSet_ScriptBinding.h; sourceTree = "<group>"; };
+		2AA6866816D69968003CEF0A /* SceneObjectSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneObjectSet.cc; sourceTree = "<group>"; };
+		2AA6866916D69968003CEF0A /* SceneObjectSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectSet.h; sourceTree = "<group>"; };
+		2AB14A0616D7CDCE00EABBF2 /* PointForceController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointForceController_ScriptBinding.h; path = controllers/PointForceController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB14A0716D7CDCE00EABBF2 /* PointForceController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PointForceController.cc; path = controllers/PointForceController.cc; sourceTree = "<group>"; };
+		2AB14A0816D7CDCE00EABBF2 /* PointForceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointForceController.h; path = controllers/PointForceController.h; sourceTree = "<group>"; };
+		2AB4C1A416DE9F4B00B02479 /* AmbientForceController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AmbientForceController_ScriptBinding.h; path = controllers/AmbientForceController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C1A516DE9F4B00B02479 /* AmbientForceController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AmbientForceController.cc; path = controllers/AmbientForceController.cc; sourceTree = "<group>"; };
+		2AB4C1A616DE9F4B00B02479 /* AmbientForceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AmbientForceController.h; path = controllers/AmbientForceController.h; sourceTree = "<group>"; };
+		2AB4C1A916DE9F6700B02479 /* GroupedSceneController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GroupedSceneController_ScriptBinding.h; path = controllers/core/GroupedSceneController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C1AA16DE9F6700B02479 /* GroupedSceneController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GroupedSceneController.cc; path = controllers/core/GroupedSceneController.cc; sourceTree = "<group>"; };
+		2AB4C1AB16DE9F6700B02479 /* GroupedSceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GroupedSceneController.h; path = controllers/core/GroupedSceneController.h; sourceTree = "<group>"; };
+		2AB4C1AC16DE9F6700B02479 /* PickingSceneController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PickingSceneController_ScriptBinding.h; path = controllers/core/PickingSceneController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AB4C1AD16DE9F6700B02479 /* PickingSceneController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PickingSceneController.cc; path = controllers/core/PickingSceneController.cc; sourceTree = "<group>"; };
+		2AB4C1AE16DE9F6700B02479 /* PickingSceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PickingSceneController.h; path = controllers/core/PickingSceneController.h; sourceTree = "<group>"; };
+		2AB4C1AF16DE9F6700B02479 /* SceneController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SceneController.h; path = controllers/core/SceneController.h; sourceTree = "<group>"; };
 		2AB97A1F16B66BE50080F940 /* tamlCustom.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tamlCustom.cc; sourceTree = "<group>"; };
 		2AB97A2016B66BE50080F940 /* tamlCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tamlCustom.h; sourceTree = "<group>"; };
 		2AC4404B16B0144500FC4091 /* ImageFont_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont_ScriptBinding.h; sourceTree = "<group>"; };
 		2AC4404C16B0144500FC4091 /* ImageFont.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFont.cc; sourceTree = "<group>"; };
 		2AC4404D16B0144500FC4091 /* ImageFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont.h; sourceTree = "<group>"; };
+		2ACF5A2916E52D6A00F838D9 /* SpriteBatchQuery.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteBatchQuery.cc; sourceTree = "<group>"; };
+		2ACF5A2A16E52D6A00F838D9 /* SpriteBatchQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatchQuery.h; sourceTree = "<group>"; };
+		2ACF5A2B16E52D6A00F838D9 /* SpriteBatchQueryResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatchQueryResult.h; sourceTree = "<group>"; };
 		2AD07B2716D15F8E0070DC79 /* simObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simObjectTimerEvent.h; sourceTree = "<group>"; };
+		2AE2F55616D6B07200B6A058 /* BuoyancyController_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuoyancyController_ScriptBinding.h; path = controllers/BuoyancyController_ScriptBinding.h; sourceTree = "<group>"; };
+		2AE2F55716D6B07200B6A058 /* BuoyancyController.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BuoyancyController.cc; path = controllers/BuoyancyController.cc; sourceTree = "<group>"; };
+		2AE2F55816D6B07200B6A058 /* BuoyancyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuoyancyController.h; path = controllers/BuoyancyController.h; sourceTree = "<group>"; };
 		2AED7D9216B70102003482CF /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
 		2AF1C54716B439D900C1CF3A /* declaredAssets.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = declaredAssets.cc; sourceTree = "<group>"; };
 		2AF1C54816B439D900C1CF3A /* declaredAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = declaredAssets.h; sourceTree = "<group>"; };
@@ -1414,6 +1446,37 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		2AB4C1A816DE9F5000B02479 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				2AB4C1A916DE9F6700B02479 /* GroupedSceneController_ScriptBinding.h */,
+				2AB4C1AA16DE9F6700B02479 /* GroupedSceneController.cc */,
+				2AB4C1AB16DE9F6700B02479 /* GroupedSceneController.h */,
+				2AB4C1AC16DE9F6700B02479 /* PickingSceneController_ScriptBinding.h */,
+				2AB4C1AD16DE9F6700B02479 /* PickingSceneController.cc */,
+				2AB4C1AE16DE9F6700B02479 /* PickingSceneController.h */,
+				2AB4C1AF16DE9F6700B02479 /* SceneController.h */,
+			);
+			name = core;
+			sourceTree = "<group>";
+		};
+		2AB4F1D416D55B9F00C9A27B /* controllers */ = {
+			isa = PBXGroup;
+			children = (
+				2AB4C1A816DE9F5000B02479 /* core */,
+				2AB4C1A416DE9F4B00B02479 /* AmbientForceController_ScriptBinding.h */,
+				2AB4C1A516DE9F4B00B02479 /* AmbientForceController.cc */,
+				2AB4C1A616DE9F4B00B02479 /* AmbientForceController.h */,
+				2AB14A0616D7CDCE00EABBF2 /* PointForceController_ScriptBinding.h */,
+				2AB14A0716D7CDCE00EABBF2 /* PointForceController.cc */,
+				2AB14A0816D7CDCE00EABBF2 /* PointForceController.h */,
+				2AE2F55616D6B07200B6A058 /* BuoyancyController_ScriptBinding.h */,
+				2AE2F55716D6B07200B6A058 /* BuoyancyController.cc */,
+				2AE2F55816D6B07200B6A058 /* BuoyancyController.h */,
+			);
+			name = controllers;
+			sourceTree = "<group>";
+		};
 		867BAC9116AEC8BB0033868F /* platformiOS */ = {
 			isa = PBXGroup;
 			children = (
@@ -1493,6 +1556,7 @@
 		867BACF816AEC9050033868F /* 2d */ = {
 			isa = PBXGroup;
 			children = (
+				2AB4F1D416D55B9F00C9A27B /* controllers */,
 				867BACF916AEC9050033868F /* assets */,
 				867BAD0C16AEC9050033868F /* core */,
 				867BAD2416AEC9050033868F /* gui */,
@@ -1531,6 +1595,9 @@
 		867BAD0C16AEC9050033868F /* core */ = {
 			isa = PBXGroup;
 			children = (
+				2ACF5A2916E52D6A00F838D9 /* SpriteBatchQuery.cc */,
+				2ACF5A2A16E52D6A00F838D9 /* SpriteBatchQuery.h */,
+				2ACF5A2B16E52D6A00F838D9 /* SpriteBatchQueryResult.h */,
 				867BAD0D16AEC9050033868F /* BatchRender.cc */,
 				867BAD0E16AEC9050033868F /* BatchRender.h */,
 				867BAD0F16AEC9050033868F /* CoreMath.cc */,
@@ -1606,6 +1673,11 @@
 		867BAD4516AEC9050033868F /* sceneobject */ = {
 			isa = PBXGroup;
 			children = (
+				2AA6866516D69968003CEF0A /* SceneObjectList.cc */,
+				2AA6866616D69968003CEF0A /* SceneObjectList.h */,
+				2AA6866716D69968003CEF0A /* SceneObjectSet_ScriptBinding.h */,
+				2AA6866816D69968003CEF0A /* SceneObjectSet.cc */,
+				2AA6866916D69968003CEF0A /* SceneObjectSet.h */,
 				2AC4404B16B0144500FC4091 /* ImageFont_ScriptBinding.h */,
 				2AC4404C16B0144500FC4091 /* ImageFont.cc */,
 				2AC4404D16B0144500FC4091 /* ImageFont.h */,
@@ -3334,6 +3406,14 @@
 				2AF1C54C16B439D900C1CF3A /* referencedAssets.cc in Sources */,
 				2AB97A2116B66BE50080F940 /* tamlCustom.cc in Sources */,
 				33230F1656FA2C7C493DA2D2 /* guiSliderCtrl.cc in Sources */,
+				2AA6866A16D69968003CEF0A /* SceneObjectList.cc in Sources */,
+				2AA6866B16D69968003CEF0A /* SceneObjectSet.cc in Sources */,
+				2AE2F55916D6B07200B6A058 /* BuoyancyController.cc in Sources */,
+				2AB14A0916D7CDCE00EABBF2 /* PointForceController.cc in Sources */,
+				2AB4C1A716DE9F4B00B02479 /* AmbientForceController.cc in Sources */,
+				2AB4C1B016DE9F6700B02479 /* GroupedSceneController.cc in Sources */,
+				2AB4C1B116DE9F6700B02479 /* PickingSceneController.cc in Sources */,
+				2ACF5A2C16E52D6A00F838D9 /* SpriteBatchQuery.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 1 - 17
engine/source/2d/assets/AnimationAsset_ScriptBinding.h

@@ -107,6 +107,7 @@ ConsoleMethod(AnimationAsset, getAnimationTime, F32, 2, 2,       "() Gets the to
     return object->getAnimationTime();
 }
 
+
 //-----------------------------------------------------------------------------
 
 ConsoleMethod(AnimationAsset, setAnimationCycle, void, 3, 3,     "(bool animationCycle) Sets whether the animation cycles or not.\n"
@@ -122,21 +123,4 @@ ConsoleMethod(AnimationAsset, getAnimationCycle, bool, 2, 2,     "() Gets whethe
                                                                         "@return Whether the animation cycles or not.")
 {
     return object->getAnimationCycle();
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(AnimationAsset, setRandomStart, void, 3, 3,     "(bool randomStart) Sets whether the animation starts at a random frame or not.\n"
-                                                                        "@param randomStart Whether the animation starts at a random frame or not..\n"
-                                                                        "@return No return value.")
-{
-    object->setRandomStart( dAtob(argv[2] ) );
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(AnimationAsset, getRandomStart, bool, 2, 2,     "() Gets whether the animation starts at a random frame or not.\n"
-                                                                        "@return Whether the animation starts at a random frame or not.")
-{
-    return object->getRandomStart();
 }

+ 62 - 57
engine/source/2d/assets/ImageAsset.cc

@@ -109,8 +109,8 @@ IMPLEMENT_CONOBJECT(ImageAsset);
 
 static bool explicitCellPropertiesInitialized = false;
 
-static StringTableEntry cellCustomPropertyName;
-static StringTableEntry cellAliasName;
+static StringTableEntry cellCustomNodeName;
+static StringTableEntry cellNodeName;
 static StringTableEntry cellOffsetName;
 static StringTableEntry cellWidthName;
 static StringTableEntry cellHeightName;
@@ -178,14 +178,11 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
     VECTOR_SET_ASSOCIATION( mFrames );
     VECTOR_SET_ASSOCIATION( mExplicitFrames );
 
-    // Set the default filter mode.    
-    setFilterMode( FILTER_BILINEAR );
-
     // Initialize explicit cell field names.
     if ( !explicitCellPropertiesInitialized )
     {
-        cellCustomPropertyName      = StringTable->insert( "Cells" );
-        cellAliasName               = StringTable->insert( "Cell" );
+        cellCustomNodeName          = StringTable->insert( "Cells" );
+        cellNodeName                = StringTable->insert( "Cell" );
         cellOffsetName              = StringTable->insert( "Offset" );
         cellWidthName               = StringTable->insert( "Width" );
         cellHeightName              = StringTable->insert( "Height" );
@@ -868,20 +865,20 @@ void ImageAsset::onTamlPostWrite( void )
 
 //------------------------------------------------------------------------------
 
-void ImageAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomWrite);
 
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Finish if not in explicit mode.
     if ( !mExplicitMode )
         return;
 
-    // Add cell custom property.
-    TamlCustomProperty* pCellProperty = customProperties.addProperty( cellCustomPropertyName );
+    // Add cell custom node.
+    TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeName );
 
     // Iterate explicit frames.
     for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
@@ -890,49 +887,52 @@ void ImageAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
         // Add cell alias.
-        TamlPropertyAlias* pCellAlias = pCellProperty->addAlias( cellAliasName );
+        TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
 
         // Add cell properties.
-        pCellAlias->addField( cellOffsetName, pixelArea.mPixelOffset );
-        pCellAlias->addField( cellWidthName, pixelArea.mPixelWidth );
-        pCellAlias->addField( cellHeightName, pixelArea.mPixelHeight );
+        pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
+        pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
+        pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void ImageAsset::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ImageAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
 
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find cell custom property
-    const TamlCustomProperty* pCellProperty = customProperties.findProperty( cellCustomPropertyName );
+    // Find cell custom node.
+    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeName );
 
     // Finish if we don't have explicit cells.
-    if ( pCellProperty == NULL )
+    if ( pCustomCellNodes == NULL )
         return;
 
     // Set explicit mode.
     mExplicitMode = true;
 
+    // Fetch children cell nodes.
+    const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
+
     // Iterate cells.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pCellProperty->begin(); propertyAliasItr != pCellProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch cell node.
+        TamlCustomNode* pCellNode = *cellNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch node name.
+        StringTableEntry nodeName = pCellNode->getNodeName();
 
         // Is this a valid alias?
-        if ( aliasName != cellAliasName )
+        if ( nodeName != cellNodeName )
         {
             // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom alias name of '%s'.  Only '%s' is valid.", aliasName, cellAliasName );
+            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
             continue;
         }
 
@@ -940,27 +940,30 @@ void ImageAsset::onTamlCustomRead( const TamlCustomProperties& customProperties
         S32 cellWidth = 0;
         S32 cellHeight = 0;
 
+        // Fetch fields.
+        const TamlCustomFieldVector& fields = pCellNode->getFields();
+
         // Iterate property fields.
-        for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+        for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
         {
-            // Fetch property field.
-            TamlPropertyField* pPropertyField = *propertyFieldItr;
+            // Fetch field.
+            const TamlCustomField* pField = *fieldItr;
 
-            // Fetch property field name.
-            StringTableEntry fieldName = pPropertyField->getFieldName();
+            // Fetch field name.
+            StringTableEntry fieldName = pField->getFieldName();
 
             // Check common fields.
             if ( fieldName == cellOffsetName )
             {
-                pPropertyField->getFieldValue( cellOffset );
+                pField->getFieldValue( cellOffset );
             }
             else if ( fieldName == cellWidthName )
             {
-                pPropertyField->getFieldValue( cellWidth );
+                pField->getFieldValue( cellWidth );
             }
             else if ( fieldName == cellHeightName )
             {
-                pPropertyField->getFieldValue( cellHeight );
+                pField->getFieldValue( cellHeight );
             }
             else
             {
@@ -1010,6 +1013,11 @@ void ImageAsset::calculateImage( void )
     // Clear frames.
     mFrames.clear();
 
+    // If we have an existing texture and we're setting to the same bitmap then force the texture manager
+    // to refresh the texture itself.
+    if ( !mImageTextureHandle.IsNull() && dStricmp(mImageTextureHandle.getTextureKey(), mImageFile) == 0 )
+        TextureManager::refresh( mImageFile );
+
     // Get image texture.
     mImageTextureHandle.set( mImageFile, TextureHandle::BitmapTexture, true, getForce16Bit() );
 
@@ -1021,32 +1029,29 @@ void ImageAsset::calculateImage( void )
         return;
     }
 
-    // Fetch global filter.
-    const char* pGlobalFilter = Con::getVariable( "$pref::T2D::imageAssetGlobalFilterMode" );
-
-    // Fetch global filter mode.
-    TextureFilterMode filterMode;
-
-    // Set the filter mode.
-    if ( pGlobalFilter != NULL && dStrlen(pGlobalFilter) > 0 )
-        filterMode = getFilterModeEnum( pGlobalFilter );
-    else
-        filterMode = FILTER_INVALID;
-    
-    // If global filter mode is invalid then use local filter mode.
-    if ( filterMode == FILTER_INVALID )
-        filterMode = mLocalFilterMode;
-
-    // Set filter mode.
-    if ( filterMode != FILTER_INVALID )
+    // Is the local filter mode specified?
+    if ( mLocalFilterMode != FILTER_INVALID )
     {
-        // Set filter mode if valid.
-        setTextureFilter( filterMode );
+        // Yes, so set filter mode.
+        setTextureFilter( mLocalFilterMode );
     }
     else
     {
-        // Set to nearest if invalid.
-        setTextureFilter( FILTER_NEAREST );
+        TextureFilterMode filterMode = FILTER_NEAREST;
+
+        // No, so fetch the global filter.
+        const char* pGlobalFilter = Con::getVariable( "$pref::T2D::imageAssetGlobalFilterMode" );
+
+        // Fetch the global filter mode.
+        if ( pGlobalFilter != NULL && dStrlen(pGlobalFilter) > 0 )
+            filterMode = getFilterModeEnum( pGlobalFilter );
+
+        // If global filter mode is invalid then use local filter mode.
+        if ( filterMode == FILTER_INVALID )
+            filterMode = FILTER_NEAREST;
+
+        // Set filter mode.
+        setTextureFilter( filterMode );
     }
 
     // Calculate according to mode.

+ 2 - 2
engine/source/2d/assets/ImageAsset.h

@@ -243,8 +243,8 @@ protected:
     /// Taml callbacks.
     virtual void onTamlPreWrite( void );
     virtual void onTamlPostWrite( void );
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 
 protected:

+ 4 - 4
engine/source/2d/assets/ParticleAsset.cc

@@ -474,22 +474,22 @@ void ParticleAsset::moveEmitter( S32 fromIndex, S32 toIndex )
 
 //------------------------------------------------------------------------------
 
-void ParticleAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAsset_OnTamlCustomWrite);
 
     // Write the fields.
-    mParticleFields.onTamlCustomWrite( customProperties );
+    mParticleFields.onTamlCustomWrite( customNodes );
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAsset::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAsset_OnTamlCustomRead);
 
     // Read the fields.
-    mParticleFields.onTamlCustomRead( customProperties );
+    mParticleFields.onTamlCustomRead( customNodes );
 }

+ 2 - 2
engine/source/2d/assets/ParticleAsset.h

@@ -160,8 +160,8 @@ public:
 protected:
     virtual void initializeAsset( void );
 
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 protected:
     static bool setLifetime(void* obj, const char* data)                    { static_cast<ParticleAsset*>(obj)->setLifetime(dAtof(data)); return false; }

+ 4 - 4
engine/source/2d/assets/ParticleAssetEmitter.cc

@@ -489,23 +489,23 @@ void ParticleAssetEmitter::onAssetRefreshed( AssetPtrBase* pAssetPtrBase )
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetEmitter::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAssetEmitter::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetEmitter_OnTamlCustomWrite);
 
     // Write the fields.
-    mParticleFields.onTamlCustomWrite( customProperties );
+    mParticleFields.onTamlCustomWrite( customNodes );
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetEmitter::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAssetEmitter::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetEmitter_OnTamlCustomRead);
 
     // Read the fields.
-    mParticleFields.onTamlCustomRead( customProperties );
+    mParticleFields.onTamlCustomRead( customNodes );
 }
 

+ 2 - 2
engine/source/2d/assets/ParticleAssetEmitter.h

@@ -260,8 +260,8 @@ private:
     virtual void onAssetRefreshed( AssetPtrBase* pAssetPtrBase );
 
 protected:
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
     static bool     setEmitterName(void* obj, const char* data)                         { static_cast<ParticleAssetEmitter*>(obj)->setEmitterName( data ); return false; }
     static bool     setEmitterType(void* obj, const char* data)                         { static_cast<ParticleAssetEmitter*>(obj)->setEmitterType( getEmitterTypeEnum(data) ); return false; }

+ 74 - 39
engine/source/2d/assets/ParticleAssetField.cc

@@ -50,6 +50,10 @@ static StringTableEntry particleAssetFieldDefaultValueName;
 static StringTableEntry particleAssetFieldValueScaleName;
 static StringTableEntry particleAssetFieldDataKeysName;
 
+static StringTableEntry particleAssetFieldDataKeyName;
+static StringTableEntry particleAssetFieldDataKeyTimeName;
+static StringTableEntry particleAssetFieldDataKeyValueName;
+
 ParticleAssetField::DataKey ParticleAssetField::BadDataKey( -1.0f, 0.0f );
 
 //-----------------------------------------------------------------------------
@@ -78,6 +82,10 @@ ParticleAssetField::ParticleAssetField() :
         particleAssetFieldValueScaleName   = StringTable->insert( "ValueScale" );
         particleAssetFieldDataKeysName     = StringTable->insert( "Keys" );
 
+        particleAssetFieldDataKeyName      = StringTable->insert( "Key" );
+        particleAssetFieldDataKeyTimeName  = StringTable->insert( "Time" );
+        particleAssetFieldDataKeyValueName = StringTable->insert( "Value" );
+
         // Flag as initialized.
         particleAssetFieldPropertiesInitialized = true;
     }
@@ -509,34 +517,34 @@ F32 ParticleAssetField::calculateFieldBVLE( const ParticleAssetField& base, cons
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetField::onTamlCustomWrite( TamlCustomProperty* pCustomProperty )
+void ParticleAssetField::onTamlCustomWrite( TamlCustomNode* pCustomNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetField_OnTamlCustomWrite);
 
-    // Add a alias (ignore it if there ends up being no properties).
-    TamlPropertyAlias* pPropertyAlias = pCustomProperty->addAlias( getFieldName(), true );
+    // Add a child (ignore it if there ends up being no children).
+    TamlCustomNode* pAssetField = pCustomNode->addNode( getFieldName(), true );
 
     // Sanity!
-    AssertFatal( pPropertyAlias != NULL, "ParticleAssetField::onTamlCustomWrite() - Could not create field alias." );
+    AssertFatal( pAssetField != NULL, "ParticleAssetField::onTamlCustomWrite() - Could not create field." );
 
     if ( mValueBoundsDirty && (mNotEqual( getMinValue(), 0.0f ) || mNotEqual( getMaxValue(), 0.0f )) )
     {
-        pPropertyAlias->addField( particleAssetFieldMinValueName, getMinValue() );
-        pPropertyAlias->addField( particleAssetFieldMaxValueName, getMaxValue() );
+        pAssetField->addField( particleAssetFieldMinValueName, getMinValue() );
+        pAssetField->addField( particleAssetFieldMaxValueName, getMaxValue() );
     }
     
     if ( mValueBoundsDirty && mNotEqual( getMaxTime(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldMaxTimeName, getMaxTime() );
+        pAssetField->addField( particleAssetFieldMaxTimeName, getMaxTime() );
 
     if ( mValueBoundsDirty && mNotEqual( getDefaultValue(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldDefaultValueName, getDefaultValue() );
+        pAssetField->addField( particleAssetFieldDefaultValueName, getDefaultValue() );
 
     if ( mNotEqual( getValueScale(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldValueScaleName, getValueScale() );
+        pAssetField->addField( particleAssetFieldValueScaleName, getValueScale() );
 
     if ( mNotEqual( getRepeatTime(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldRepeatTimeName, getRepeatTime() );
+        pAssetField->addField( particleAssetFieldRepeatTimeName, getRepeatTime() );
 
     // Fetch key count.
     const U32 keyCount = getDataKeyCount();
@@ -549,29 +557,24 @@ void ParticleAssetField::onTamlCustomWrite( TamlCustomProperty* pCustomProperty
     if ( keyCount == 1 && mIsEqual(mDataKeys[0].mTime, 0.0f) && mIsEqual(mDataKeys[0].mValue, mDefaultValue) )
         return;
 
-    // Format the keys,
-    char keysBuffer[MAX_TAML_PROPERTY_FIELDVALUE_LENGTH];
-    char* pKeysBuffer = keysBuffer;
-    S32 bufferSize = sizeof(keysBuffer);
-
     // Iterate the keys.
     for( U32 index = 0; index < keyCount; ++index )
     {
         // Fetch the data key.
         const DataKey& dataKey = mDataKeys[index];
 
-        // Format the key.
-        S32 written = dSprintf( pKeysBuffer, bufferSize, index == 0 ? "%.5g %.5g" : " %.5g %.5g", dataKey.mTime, dataKey.mValue );
-        pKeysBuffer += written;
-        bufferSize -= written;
-    }
+        // Add a key node.
+        TamlCustomNode* pKeyNode = pCustomNode->addNode( particleAssetFieldDataKeyName );
 
-    pPropertyAlias->addField( particleAssetFieldDataKeysName, keysBuffer );
+        // Add key fields.
+        pKeyNode->addField( particleAssetFieldDataKeyTimeName, dataKey.mTime );
+        pKeyNode->addField( particleAssetFieldDataKeyValueName, dataKey.mValue );
+    }
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAlias )
+void ParticleAssetField::onTamlCustomRead( const TamlCustomNode* pCustomNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetField_OnTamlCustomRead);
@@ -590,46 +593,49 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
     // Clear the existing keys.
     mDataKeys.clear();
 
-    // Iterate property fields.
-    for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+    // Fetch fields.
+    const TamlCustomFieldVector& fields = pCustomNode->getFields();
+
+    // Iterate fields.
+    for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
     {
-        // Fetch property field.
-        TamlPropertyField* pPropertyField = *propertyFieldItr;
+        // Fetch field.
+        TamlCustomField* pField = *fieldItr;
 
         // Fetch property field name.
-        StringTableEntry fieldName = pPropertyField->getFieldName();
+        StringTableEntry fieldName = pField->getFieldName();
 
         if ( fieldName == particleAssetFieldRepeatTimeName )
         {
-            pPropertyField->getFieldValue( repeatTime );
+            pField->getFieldValue( repeatTime );
         }
         else if ( fieldName == particleAssetFieldMaxTimeName )
         {
-            pPropertyField->getFieldValue( maxTime );
+            pField->getFieldValue( maxTime );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldMinValueName )
         {
-            pPropertyField->getFieldValue( minValue );
+            pField->getFieldValue( minValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldMaxValueName )
         {
-            pPropertyField->getFieldValue( maxValue );
+            pField->getFieldValue( maxValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldDefaultValueName )
         {
-            pPropertyField->getFieldValue( defaultValue );
+            pField->getFieldValue( defaultValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldValueScaleName )
         {
-            pPropertyField->getFieldValue( valueScale );
+            pField->getFieldValue( valueScale );
         }
         else if ( fieldName == particleAssetFieldDataKeysName )
         {
-            const char* pDataKeys = pPropertyField->getFieldValue();
+            const char* pDataKeys = pField->getFieldValue();
             const S32 elementCount = StringUnit::getUnitCount( pDataKeys, " ,\t" );
 
             // Are there a valid number of elements?
@@ -652,6 +658,39 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
         }
     }
 
+    // Fetch any children.
+    const TamlCustomNodeVector& children = pCustomNode->getChildren();
+
+    // Iterate node children.
+    for( TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr )
+    {
+        // Fetch node.
+        TamlCustomNode* pKeyNode = *childItr;
+
+        // Ignore anything that isn't a key.
+        if ( pKeyNode->getNodeName() != particleAssetFieldDataKeyName )
+            continue;
+
+        // Fetch the fields.
+        const TamlCustomField* pTimeField = pKeyNode->findField( particleAssetFieldDataKeyTimeName );
+        const TamlCustomField* pValueField = pKeyNode->findField( particleAssetFieldDataKeyValueName );
+
+        // Did we find the fields?
+        if ( pTimeField == NULL || pValueField == NULL )
+        {
+            // No, so warn.
+            Con::warnf("ParticleAssetField::onTamlCustomRead() - Found a key but it did not have a time and value field." );
+
+            continue;
+        }
+
+        // Read key.
+        DataKey key;
+        pTimeField->getFieldValue( key.mTime );
+        pValueField->getFieldValue( key.mValue );
+        keys.push_back( key );
+    }
+
     // Set the value bounds.
     setValueBounds( maxTime, minValue, maxValue, defaultValue );
 
@@ -662,9 +701,5 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
     setRepeatTime( repeatTime );
 
     // Set the data keys.
-    for ( S32 index = 0; index < keys.size(); ++index )
-    {
-        const DataKey& key = keys[index];
-        addDataKey( key.mTime, key.mValue );
-    }
+    mDataKeys = keys;
 }

+ 2 - 2
engine/source/2d/assets/ParticleAssetField.h

@@ -109,8 +109,8 @@ public:
     static F32 calculateFieldBVE( const ParticleAssetField& base, const ParticleAssetField& variation, const ParticleAssetField& effect, const F32 effectAge, const bool modulate = false, const F32 modulo = 0.0f );
     static F32 calculateFieldBVLE( const ParticleAssetField& base, const ParticleAssetField& variation, const ParticleAssetField& overlife, const ParticleAssetField& effect, const F32 effectTime, const F32 particleAge, const bool modulate = false, const F32 modulo = 0.0f );
 
-    void onTamlCustomWrite( TamlCustomProperty* pCustomProperty  );
-    void onTamlCustomRead( const TamlPropertyAlias* pPropertyAlias );
+    void onTamlCustomWrite( TamlCustomNode* pCustomNode  );
+    void onTamlCustomRead( const TamlCustomNode* pCustomNode );
 };
 
 //-----------------------------------------------------------------------------

+ 22 - 19
engine/source/2d/assets/ParticleAssetFieldCollection.cc

@@ -24,7 +24,7 @@
 
 //-----------------------------------------------------------------------------
 
-static StringTableEntry particleAssetFieldCollectionName;
+static StringTableEntry particleAssetFieldNodeName;
 
 //-----------------------------------------------------------------------------
 
@@ -32,7 +32,7 @@ ParticleAssetFieldCollection::ParticleAssetFieldCollection() :
                                     mpSelectedField( NULL )
 {
     // Set custom property name.
-    particleAssetFieldCollectionName = StringTable->insert("Fields");
+    particleAssetFieldNodeName = StringTable->insert("Fields");
 }
 
 //-----------------------------------------------------------------------------
@@ -422,7 +422,7 @@ F32 ParticleAssetFieldCollection::getValueScale( void ) const
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetFieldCollection_OnTamlCustomWrite);
@@ -431,52 +431,55 @@ void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomProperties& cust
     if ( mFields.size() == 0 )
         return;
 
-    // Add particle asset custom property.
-    TamlCustomProperty* pParticleAssetCustomProperty = customProperties.addProperty( particleAssetFieldCollectionName );
+    // Add particle asset custom node.
+    TamlCustomNode* pParticleAssetCustomNode = customNodes.addNode( particleAssetFieldNodeName );
 
     // Iterate the fields.
     for( typeFieldHash::iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
     {
-        fieldItr->value->onTamlCustomWrite( pParticleAssetCustomProperty );
+        fieldItr->value->onTamlCustomWrite( pParticleAssetCustomNode );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetFieldCollection::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAssetFieldCollection::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetFieldCollection_OnTamlCustomRead);
 
-    // Find the particle asset custom property.
-    const TamlCustomProperty* pParticleAssetCustomProperty = customProperties.findProperty( particleAssetFieldCollectionName );
+    // Find the particle asset custom node.
+    const TamlCustomNode* pParticleAssetCustomNode = customNodes.findNode( particleAssetFieldNodeName );
 
-    // Finish if we don't have a custom property.
-    if ( pParticleAssetCustomProperty == NULL )
+    // Finish if we don't have a custom node.
+    if ( pParticleAssetCustomNode == NULL )
         return;
 
+    // Fetch children.
+    const TamlCustomNodeVector& children = pParticleAssetCustomNode->getChildren();
+
     // Iterate the custom properties.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pParticleAssetCustomProperty->begin(); propertyAliasItr != pParticleAssetCustomProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator childNodeItr = children.begin(); childNodeItr != children.end(); ++childNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch child node.
+        TamlCustomNode* pChildNode = *childNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch node name.
+        StringTableEntry nodeName = pChildNode->getNodeName();
 
         // Find the field.
-        ParticleAssetField* pParticleAssetField = findField( aliasName );
+        ParticleAssetField* pParticleAssetField = findField( nodeName );
 
         // Did we find the field?
         if ( pParticleAssetField == NULL )
         {
             // No, so warn.
-            Con::warnf( "ParticleAssetFieldCollection::onTamlCustomRead() - Cannot find data field '%s'.", aliasName );
+            Con::warnf( "ParticleAssetFieldCollection::onTamlCustomRead() - Cannot find data field '%s'.", nodeName );
             continue;
         }
 
         // Read the alias.
-        pParticleAssetField->onTamlCustomRead( pPropertyAlias );
+        pParticleAssetField->onTamlCustomRead( pChildNode );
     }
 }
 

+ 2 - 2
engine/source/2d/assets/ParticleAssetFieldCollection.h

@@ -81,8 +81,8 @@ public:
     bool setValueScale( const F32 valueScale );
     F32 getValueScale( void ) const;    
 
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 };
 
 #endif // ParticleAssetFieldCollection

+ 87 - 0
engine/source/2d/controllers/AmbientForceController.cc

@@ -0,0 +1,87 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _AMBIENT_FORCE_CONTROLLER_H_
+#include "2d/controllers/AmbientForceController.h"
+#endif
+
+// Script bindings.
+#include "AmbientForceController_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(AmbientForceController);
+
+//------------------------------------------------------------------------------
+
+AmbientForceController::AmbientForceController()        
+{
+    // Reset the constant force.
+    mForce.SetZero();
+}
+
+//------------------------------------------------------------------------------
+
+AmbientForceController::~AmbientForceController()
+{
+}
+
+
+//------------------------------------------------------------------------------
+
+void AmbientForceController::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    // Force.
+    addProtectedField("Force", TypeVector2, Offset( mForce, AmbientForceController), &defaultProtectedSetFn, &defaultProtectedGetFn, "The constant force to apply.");
+}
+
+//------------------------------------------------------------------------------
+
+void AmbientForceController::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to controller.
+    AmbientForceController* pController = static_cast<AmbientForceController*>(object);
+
+    // Sanity!
+    AssertFatal(pController != NULL, "AmbientForceController::copyTo() - Object is not the correct type.");
+
+    // Copy state.
+    pController->setForce( getForce() );
+}
+
+//------------------------------------------------------------------------------
+
+void AmbientForceController::integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
+{
+    // Process all the scene objects.
+    for( SceneObjectSet::iterator itr = begin(); itr != end(); ++itr )
+    {
+        // Apply the force.
+        (*itr)->applyForce( mForce, true );
+    }
+}

+ 60 - 0
engine/source/2d/controllers/AmbientForceController.h

@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _AMBIENT_FORCE_CONTROLLER_H_
+#define _AMBIENT_FORCE_CONTROLLER_H_
+
+#ifndef _GROUPED_SCENE_CONTROLLER_H_
+#include "2d/controllers/core/GroupedSceneController.h"
+#endif
+
+#ifndef _VECTOR2_H_
+#include "2d/core/vector2.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class AmbientForceController : public GroupedSceneController
+{
+private:
+    typedef GroupedSceneController Parent;
+
+    Vector2 mForce;
+
+public:
+    AmbientForceController();
+    virtual ~AmbientForceController();
+
+    static void initPersistFields();
+    virtual void copyTo(SimObject* object);
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats );
+
+    inline void setForce( const Vector2& force ) { mForce = force; }
+    inline const Vector2& getForce( void ) const { return mForce; }
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( AmbientForceController );
+};
+
+#endif // _AMBIENT_FORCE_CONTROLLER_H_

+ 59 - 0
engine/source/2d/controllers/AmbientForceController_ScriptBinding.h

@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(AmbientForceController, setForce, void, 3, 4,     "(float x, float y) - Sets the ambient force to use.\n"
+                                                                "@param x The component of the ambient force along the horizontal (world) axis.\n"
+                                                                "@param y The component of the ambient force along the vertical (world) axis.\n"
+                                                                "@return No return value.")
+{
+    // The new force.
+    b2Vec2 force;
+
+    // Elements in the first argument.
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // ("x y")
+    if ((elementCount == 2) && (argc == 3))
+        force = Utility::mGetStringElementVector(argv[2]);
+
+    // (x, y)
+    else if ((elementCount == 1) && (argc == 4))
+        force.Set(dAtof(argv[2]), dAtof(argv[3]));
+
+    // Invalid
+    else
+    {
+        Con::warnf("AmbientForceController::setForce() - Invalid number of parameters!");
+        return;
+    }
+
+    object->setForce( force );
+} 
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(AmbientForceController, getForce, const char*, 2, 2, "() Gets the ambient force being used.\n"
+                                                                    "@return The ambient force being used.")
+{
+    return object->getForce().scriptThis();
+}
+

+ 390 - 0
engine/source/2d/controllers/BuoyancyController.cc

@@ -0,0 +1,390 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _BUOYANCY_CONTROLLER_H_
+#include "2d/controllers/BuoyancyController.h"
+#endif
+
+// Script bindings.
+#include "BuoyancyController_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(BuoyancyController);
+
+//------------------------------------------------------------------------------
+
+BuoyancyController::BuoyancyController() :
+    mFlowVelocity( 0.0f, 0.0f ),
+    mFluidDensity( 2.0f ),
+    mLinearDrag( 0.0f ),
+    mAngularDrag( 0.0f ),
+    mFluidGravity( 0.0f, -9.8f ),
+    mUseShapeDensity( true ),
+    mSurfaceNormal( 0.0f, 1.0f )
+{
+}
+
+//------------------------------------------------------------------------------
+
+BuoyancyController::~BuoyancyController()
+{
+}
+
+
+//------------------------------------------------------------------------------
+
+void BuoyancyController::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    addField( "FluidArea", Typeb2AABB, Offset(mFluidArea, BuoyancyController), "The fluid area." );
+    addField( "FluidDensity", TypeF32, Offset(mFluidDensity, BuoyancyController), "The fluid density." );
+    addField( "FlowVelocity", TypeVector2, Offset(mFlowVelocity, BuoyancyController), "The fluid flow velocity for drag calculations." );
+    addField( "LinearDrag", TypeF32, Offset(mLinearDrag, BuoyancyController), "The linear drag co-efficient for the fluid." );
+    addField( "AngularDrag", TypeF32, Offset(mAngularDrag, BuoyancyController), "The angular drag co-efficient for the fluid." );
+    addField( "FluidGravity", TypeVector2, Offset(mFluidGravity, BuoyancyController), "The gravity to use inside the fluid." );
+    addField( "UseShapeDensity", TypeBool, Offset(mUseShapeDensity, BuoyancyController), "Whether to use the collision shape densities or assume a uniform density." );
+}
+
+//------------------------------------------------------------------------------
+
+void BuoyancyController::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to controller.
+    BuoyancyController* pController = static_cast<BuoyancyController*>(object);
+
+    // Sanity!
+    AssertFatal(pController != NULL, "BuoyancyController::copyTo() - Object is not the correct type.");
+}
+
+//------------------------------------------------------------------------------
+/*
+* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
+*
+* This software is provided 'as-is', without any express or implied
+* warranty.  In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+//------------------------------------------------------------------------------
+
+void BuoyancyController::integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
+{
+    // Prepare query filter.
+    WorldQuery* pWorldQuery = prepareQueryFilter( pScene );
+
+    // Query for candidate objects.
+    pWorldQuery->anyQueryArea( mFluidArea ); 
+
+    // Fetch results.
+    typeWorldQueryResultVector& queryResults = pWorldQuery->getQueryResults();
+
+    // Iterate the results.
+    for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
+    {
+        // Fetch the scene object.
+        SceneObject* pSceneObject = queryResults[n].mpSceneObject;
+
+        // Skip if asleep.
+        if ( !pSceneObject->getAwake() )
+            continue;
+
+        // Ignore if it's a static body.
+        if ( pSceneObject->getBodyType() == b2BodyType::b2_staticBody )
+            continue;
+
+        // Fetch the shape count.
+        const U32 shapeCount = pSceneObject->getCollisionShapeCount();
+
+        // Skip if no collision shapes.
+        if ( shapeCount == 0 )
+            continue;
+
+        // Fetch the body transform.
+        const b2Transform& bodyTransform = pSceneObject->getBody()->GetTransform();;
+
+		Vector2 areaCenter(0.0f, 0.0f);
+		Vector2 massCenter(0.0f, 0.0f);
+		F32 area = 0.0f;
+		F32 mass = 0.0f;
+
+        // Yes, so iterate them.
+        for( U32 i = 0; i < shapeCount; ++i )
+        {
+            // Fetch the fixture definition.
+            const b2FixtureDef fixtureDef = pSceneObject->getCollisionShapeDefinition( i );
+
+            // Fetch the shape.
+            const b2Shape* pShape = fixtureDef.shape;
+
+			Vector2 shapeCenter(0.0f, 0.0f);
+            
+			F32 shapeArea = 0.0f;
+
+            // Calculate the area for the shape type.
+            if ( pShape->GetType() == b2Shape::e_circle )
+            {
+                shapeArea = ComputeCircleSubmergedArea( bodyTransform, dynamic_cast<const b2CircleShape*>(pShape), shapeCenter );
+            }
+            else if ( pShape->GetType() == b2Shape::e_polygon)
+            {
+                shapeArea = ComputePolygonSubmergedArea( bodyTransform, dynamic_cast<const b2PolygonShape*>(pShape), shapeCenter );
+            }
+            else if ( pShape->GetType() == b2Shape::e_edge || pShape->GetType() == b2Shape::e_chain )
+            {
+                shapeArea = 0.0f;
+            }
+            else
+            {
+                // Skip if unknown shape type.
+                continue;
+            }
+
+            // Calculate area.
+			area += shapeArea;
+			areaCenter.x += shapeArea * shapeCenter.x;
+			areaCenter.y += shapeArea * shapeCenter.y;
+
+            // Calculate mass.
+			const F32 shapeDensity = mUseShapeDensity ? fixtureDef.density : 1.0f;
+			mass += shapeArea*shapeDensity;
+			massCenter.x += shapeArea * shapeCenter.x * shapeDensity;
+			massCenter.y += shapeArea * shapeCenter.y * shapeDensity;
+        }
+
+        // Skip not in water.
+		if( area < b2_epsilon )
+			continue;
+
+        // Calculate area/mass centers.
+		areaCenter.x /= area;
+		areaCenter.y /= area;
+		massCenter.x /= mass;
+		massCenter.y /= mass;
+
+		// Buoyancy
+		const Vector2 buoyancyForce = -mFluidDensity * area * mFluidGravity;
+        pSceneObject->applyForce(buoyancyForce, massCenter);
+
+		// Linear drag
+        const Vector2 dragForce = (pSceneObject->getLinearVelocityFromWorldPoint(areaCenter) - mFlowVelocity) * (-mLinearDrag * area);
+		pSceneObject->applyForce(dragForce, areaCenter );
+
+		// Angular drag
+        pSceneObject->applyTorque( -pSceneObject->getInertia() / pSceneObject->getMass() * area * pSceneObject->getAngularVelocity()*mAngularDrag );
+    }
+}
+
+//------------------------------------------------------------------------------
+
+F32 BuoyancyController::ComputeCircleSubmergedArea( const b2Transform& bodyTransform, const b2CircleShape* pShape, Vector2& center )
+{
+    // Sanity!
+    AssertFatal( pShape != NULL, "BuoyancyController::ComputeCircleSubmergedArea() - Invalid shape." );
+
+    // Calculate the world shape center.
+    const b2Vec2 worldShapeCenter = b2Mul( bodyTransform, pShape->m_p );
+
+    const F32 l = -(b2Dot( mSurfaceNormal, worldShapeCenter ) - mFluidArea.upperBound.y);
+
+    // Fetch the circle radius.
+    const F32 radius = pShape->m_radius;
+
+    // Submerged?
+    if (l < - radius + FLT_MIN)
+	{
+        // No, so return zero area submerged.
+		return 0.0f;
+	}
+
+	// Completely wet?
+	if (l > pShape->m_radius)
+	{
+        // Yes!
+		center = worldShapeCenter;
+		return  b2_pi * radius * radius;
+	}
+		
+	// Partial submersion.
+    const F32 r2 = radius * radius;
+    const F32 l2 = l * l;
+    const F32 area = r2 *( mAsin(l / radius) + b2_pi / 2.0f) + l * mSqrt( r2 - l2 );
+    const F32 com = -2.0f / 3.0f * mPow(r2 - l2, 1.5f) / area;
+	
+    // Calculate center.
+	center.x = worldShapeCenter.x + mSurfaceNormal.x * com;
+	center.y = worldShapeCenter.y + mSurfaceNormal.y * com;
+		
+	return area;
+}
+
+//------------------------------------------------------------------------------
+
+F32 BuoyancyController::ComputePolygonSubmergedArea( const b2Transform& bodyTransform, const b2PolygonShape* pShape, Vector2& center )
+{
+    // Sanity!
+    AssertFatal( pShape != NULL, "BuoyancyController::ComputePolygonSubmergedArea() - Invalid shape." );
+
+    // Transform plane into shape co-ordinates
+    b2Vec2 normalL = b2MulT( bodyTransform.q, mSurfaceNormal);
+    F32 offsetL = mFluidArea.upperBound.y - b2Dot(mSurfaceNormal, bodyTransform.p);
+        
+    F32 depths[b2_maxPolygonVertices];
+    S32 diveCount = 0;
+    S32 intoIndex = -1;
+    S32 outoIndex = -1;
+    
+    const S32 vertexCount = pShape->GetVertexCount();
+    const b2Vec2* pVertices = pShape->m_vertices;
+
+    bool lastSubmerged = false;
+    for ( S32 i = 0; i < vertexCount; ++i )
+    {
+        depths[i] = b2Dot(normalL, pVertices[i]) - offsetL;
+        const bool isSubmerged = depths[i]<-FLT_EPSILON;
+
+        if (i > 0)
+        {
+            if (isSubmerged)
+            {
+                if (!lastSubmerged)
+                {
+                    intoIndex = i-1;
+                    diveCount++;
+                }
+            }
+            else
+            {
+                if (lastSubmerged)
+                {
+                    outoIndex = i-1;
+                    diveCount++;
+                }
+            }
+        }
+        lastSubmerged = isSubmerged;
+    }
+
+    switch(diveCount)
+    {
+        case 0:
+            if (lastSubmerged)
+            {
+                // Completely submerged
+                b2MassData md;
+                pShape->ComputeMass(&md, 1.0f);
+                center = b2Mul(bodyTransform, md.center);
+                return md.mass;
+            }
+            else
+            {
+                // Completely dry
+                return 0.0;
+            }
+            break;
+
+        case 1:
+            if( intoIndex==-1 )
+            {
+                intoIndex = vertexCount-1;
+            }
+            else
+            {
+                outoIndex = vertexCount-1;
+            }
+            break;
+    }
+
+    const S32 intoIndex2 = (intoIndex+1) % vertexCount;
+    const S32 outoIndex2 = (outoIndex+1) % vertexCount;
+        
+    const F32 intoLambda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]);
+    const F32 outoLambda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]);
+        
+    const b2Vec2 intoVec( pVertices[intoIndex].x*(1-intoLambda)+pVertices[intoIndex2].x*intoLambda,
+                                    pVertices[intoIndex].y*(1-intoLambda)+pVertices[intoIndex2].y*intoLambda);
+    const b2Vec2 outoVec( pVertices[outoIndex].x*(1-outoLambda)+pVertices[outoIndex2].x*outoLambda,
+                                    pVertices[outoIndex].y*(1-outoLambda)+pVertices[outoIndex2].y*outoLambda);
+        
+    // Initialize accumulator
+    F32 area = 0.0f;
+    center.SetZero();
+    b2Vec2 p2 = pVertices[intoIndex2];
+    b2Vec2 p3;
+        
+    const F32 k_inv3 = 1.0f / 3.0f;
+        
+    // An awkward loop from intoIndex2+1 to outIndex2
+    S32 i = intoIndex2;
+    while (i != outoIndex2)
+    {
+        i = (i+1) % vertexCount;
+        if (i == outoIndex2)
+            p3 = outoVec;
+        else
+            p3 = pVertices[i];
+                
+        // Add the triangle formed by intoVec,p2,p3
+        const b2Vec2 e1 = p2 - intoVec;
+        const b2Vec2 e2 = p3 - intoVec;                        
+        const F32 D = b2Cross(e1, e2);                        
+        const F32 triangleArea = 0.5f * D;
+        area += triangleArea;
+                        
+        // Area weighted centroid
+        center += triangleArea * k_inv3 * (intoVec + p2 + p3);
+
+        p2 = p3;
+    }
+        
+    // Normalize and transform centroid
+    center *= 1.0f / area;        
+    center = b2Mul(bodyTransform, center);
+        
+    return area;
+}
+
+
+//------------------------------------------------------------------------------
+
+void BuoyancyController::renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer )
+{
+    // Call parent.
+    Parent::renderOverlay( pScene, pSceneRenderState, pBatchRenderer );
+
+    // Draw fluid area.
+    pScene->mDebugDraw.DrawAABB( mFluidArea, ColorF(0.7f, 0.7f, 0.9f) );
+}

+ 86 - 0
engine/source/2d/controllers/BuoyancyController.h

@@ -0,0 +1,86 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _BUOYANCY_CONTROLLER_H_
+#define _BUOYANCY_CONTROLLER_H_
+
+#ifndef _PICKING_SCENE_CONTROLLER_H_
+#include "2d/controllers/core/pickingSceneController.h"
+#endif
+
+#ifndef _VECTOR2_H_
+#include "2d/core/vector2.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class BuoyancyController : public PickingSceneController
+{
+private:
+    typedef PickingSceneController Parent;
+
+    /// The fluid area.
+    b2AABB mFluidArea;
+
+	/// The fluid density.
+	F32 mFluidDensity;
+
+	/// Fluid flow velocity for drag calculations.
+	Vector2 mFlowVelocity;
+
+	/// Linear drag co-efficient.
+	F32 mLinearDrag;
+
+	/// Linear drag co-efficient.
+	F32 mAngularDrag;
+
+	/// Gravity to use inside the fluid.
+	Vector2 mFluidGravity;
+
+	/// Whether to use the collision shape densities or assume a uniform density.
+	bool mUseShapeDensity;
+
+	/// The outer fluid surface normal.
+	Vector2 mSurfaceNormal;
+
+protected:
+    F32 ComputeCircleSubmergedArea( const b2Transform& bodyTransform, const b2CircleShape* pShape, Vector2& center );
+    F32 ComputePolygonSubmergedArea( const b2Transform& bodyTransform, const b2PolygonShape* pShape, Vector2& center );
+
+public:
+    BuoyancyController();
+    virtual ~BuoyancyController();
+
+    static void initPersistFields();
+    virtual void copyTo(SimObject* object);
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats );
+
+    // Scene render.
+    virtual void renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer );
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( BuoyancyController );
+};
+
+#endif // _BUOYANCY_CONTROLLER_H_

+ 23 - 0
engine/source/2d/controllers/BuoyancyController_ScriptBinding.h

@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+

+ 137 - 0
engine/source/2d/controllers/PointForceController.cc

@@ -0,0 +1,137 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _ATTRACTOR_CONTROLLER_H_
+#include "2d/controllers/PointForceController.h"
+#endif
+
+// Script bindings.
+#include "PointForceController_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(PointForceController);
+
+//------------------------------------------------------------------------------
+
+PointForceController::PointForceController()        
+{
+    // Reset he controller.
+    mPosition.SetZero();
+    mRadius = 1.0f;
+    mForce = 0.0f;
+}
+
+//------------------------------------------------------------------------------
+
+PointForceController::~PointForceController()
+{
+}
+
+
+//------------------------------------------------------------------------------
+
+void PointForceController::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    // Force.
+    addProtectedField("Position", TypeVector2, Offset( mPosition, PointForceController), &defaultProtectedSetFn, &defaultProtectedGetFn, "The position of the attractor controller.");
+    addProtectedField("Radius", TypeF32, Offset( mRadius, PointForceController), &defaultProtectedSetFn, &defaultProtectedGetFn, "The radius of the attractor circle centered on the attractors position.");
+    addProtectedField("Force", TypeF32, Offset( mForce, PointForceController), &defaultProtectedSetFn, &defaultProtectedGetFn, "The force to apply to attact to the controller position.");
+}
+
+//------------------------------------------------------------------------------
+
+void PointForceController::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to controller.
+    PointForceController* pController = static_cast<PointForceController*>(object);
+
+    // Sanity!
+    AssertFatal(pController != NULL, "PointForceController::copyTo() - Object is not the correct type.");
+
+    // Copy state.
+    pController->setForce( getForce() );
+}
+
+//------------------------------------------------------------------------------
+
+void PointForceController::integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
+{
+    // Finish if the attractor would have no effect.
+    if ( mIsZero( mForce ) || mIsZero( mRadius ) )
+        return;
+
+    // Prepare query filter.
+    WorldQuery* pWorldQuery = prepareQueryFilter( pScene );
+
+    // Calculate the AABB of the attractor.
+    b2AABB aabb;
+    aabb.lowerBound.Set( mPosition.x - mRadius, mPosition.y - mRadius );
+    aabb.upperBound.Set( mPosition.x + mRadius, mPosition.y + mRadius );
+
+    // Query for candidate objects.
+    pWorldQuery->anyQueryArea( aabb ); 
+
+    // Fetch results.
+    typeWorldQueryResultVector& queryResults = pWorldQuery->getQueryResults();
+
+    // Iterate the results.
+    for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
+    {
+        // Fetch the scene object.
+        SceneObject* pSceneObject = queryResults[n].mpSceneObject;
+
+        // Ignore if it's a static body.
+        if ( pSceneObject->getBodyType() == b2_staticBody )
+            continue;
+
+        // Calculate the force direction to the controllers position.
+        Vector2 forceDirection = mPosition - pSceneObject->getPosition();
+
+        // Skip if the position is outside the radius.
+        if ( forceDirection.Length() > mRadius )
+            continue;
+
+        // Normalize to the specified force.
+        forceDirection.Normalize( mForce );
+
+        // Apply the force.
+        pSceneObject->applyForce( forceDirection, true );
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void PointForceController::renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer )
+{
+    // Call parent.
+    Parent::renderOverlay( pScene, pSceneRenderState, pBatchRenderer );
+
+    // Draw force radius.
+    pScene->mDebugDraw.DrawCircle( mPosition, mRadius, ColorF(1.0f, 1.0f, 0.0f ) );
+}

+ 69 - 0
engine/source/2d/controllers/PointForceController.h

@@ -0,0 +1,69 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _ATTRACTOR_CONTROLLER_H_
+#define _ATTRACTOR_CONTROLLER_H_
+
+#ifndef _PICKING_SCENE_CONTROLLER_H_
+#include "2d/controllers/core/pickingSceneController.h"
+#endif
+
+#ifndef _VECTOR2_H_
+#include "2d/core/vector2.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class PointForceController : public PickingSceneController
+{
+private:
+    typedef PickingSceneController Parent;
+
+    Vector2 mPosition;
+    F32 mRadius;
+    F32 mForce;
+
+public:
+    PointForceController();
+    virtual ~PointForceController();
+
+    static void initPersistFields();
+    virtual void copyTo(SimObject* object);
+
+    inline void setPosition( const Vector2& position ) { mPosition = position; }
+    inline const Vector2& getPosition( void ) const { return mPosition; }
+    inline void setRadius( const F32 radius ) { mRadius = getMax( radius, FLT_MIN ); }
+    inline F32 getRadius( void ) const { return mRadius; }
+    inline void setForce( const F32 force ) { mForce = force; }
+    inline F32 getForce( void ) const { return mForce; }
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats );
+
+    // Scene render.
+    virtual void renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer );
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( PointForceController );
+};
+
+#endif // _ATTRACTOR_CONTROLLER_H_

+ 94 - 0
engine/source/2d/controllers/PointForceController_ScriptBinding.h

@@ -0,0 +1,94 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, setPosition, void, 3, 4,    "(float x, float y) - Sets the position of the force center.\n"
+                                                                "@param x The position along the horizontal axis.\n"
+                                                                "@param y The position along the vertical axis.\n"
+                                                                "@return No return value.")
+{
+    // The new position.
+    b2Vec2 position;
+
+    // Elements in the first argument.
+    U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // ("x y")
+    if ((elementCount == 2) && (argc == 3))
+        position = Utility::mGetStringElementVector(argv[2]);
+
+    // (x, y)
+    else if ((elementCount == 1) && (argc == 4))
+        position.Set(dAtof(argv[2]), dAtof(argv[3]));
+
+    // Invalid
+    else
+    {
+        Con::warnf("PointForceController::setPosition() - Invalid number of parameters!");
+        return;
+    }
+
+    // Set Position.
+    object->setPosition(position);
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, PointForceController, const char*, 2, 2,    "() Gets the position of the force center.\n"
+                                                                                "@return (float x/float y) The x and y (horizontal and vertical) position of the force center.")
+{
+    // Get position.
+    return object->getPosition().scriptThis();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, setRadius, void, 3, 3,       "(radius) - Sets the radius of the point force to use.\n"
+                                                                "@param radius The radius of the point force to use.\n"
+                                                                "@return No return value.")
+{
+    object->setRadius( dAtof(argv[2]) );
+} 
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, getRadius, F32, 2, 2,        "() Gets the radius of the point force being used.\n"
+                                                                "@return The radius of the point force being used.")
+{
+    return object->getRadius();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, setForce, void, 3, 3,       "(force) - Sets the point force to use.\n"
+                                                                "@param force The point force to use.\n"
+                                                                "@return No return value.")
+{
+    object->setForce( dAtof(argv[2]) );
+} 
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PointForceController, getForce, F32, 2, 2,        "() Gets the point force being used.\n"
+                                                                "@return The point force being used.")
+{
+    return object->getForce();
+}

+ 51 - 0
engine/source/2d/controllers/core/GroupedSceneController.cc

@@ -0,0 +1,51 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _GROUPED_SCENE_CONTROLLER_H_
+#include "2d/controllers/core/GroupedSceneController.h"
+#endif
+
+// Script bindings.
+#include "2d/controllers/core/GroupedSceneController_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(GroupedSceneController);
+
+//------------------------------------------------------------------------------
+
+void GroupedSceneController::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to controller.
+    GroupedSceneController* pController = static_cast<GroupedSceneController*>(object);
+
+    // Sanity!
+    AssertFatal(pController != NULL, "GroupedSceneController::copyTo() - Object is not the correct type.");
+
+    // Add objects to the controller.
+    for( SceneObjectSet::iterator objectItr = begin(); objectItr != end(); ++objectItr )
+        pController->addObject( *objectItr );
+}
+

+ 64 - 0
engine/source/2d/controllers/core/GroupedSceneController.h

@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _GROUPED_SCENE_CONTROLLER_H_
+#define _GROUPED_SCENE_CONTROLLER_H_
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+#ifndef _SCENE_OBJECT_SET_H_
+#include "2d/sceneObject/sceneObjectSet.h"
+#endif
+
+#ifndef _SCENE_OBJECT_H_
+#include "2d/sceneObject/sceneObject.h"
+#endif
+
+#ifndef _SCENE_CONTROLLER_H_
+#include "2d/controllers/core/sceneController.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class GroupedSceneController : public SceneObjectSet, public SceneController
+{
+    typedef SceneObjectSet Parent;
+
+public:
+    GroupedSceneController() {}
+    virtual ~GroupedSceneController() {}
+
+    virtual void copyTo(SimObject* object);
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats ) {}
+
+    // Scene render.
+    virtual void renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer ) {}
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( GroupedSceneController );
+};
+
+#endif // _GROUPED_SCENE_CONTROLLER_H_

+ 23 - 0
engine/source/2d/controllers/core/GroupedSceneController_ScriptBinding.h

@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+

+ 79 - 0
engine/source/2d/controllers/core/PickingSceneController.cc

@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef PickingSceneController
+#include "2d/controllers/core/PickingSceneController.h"
+#endif
+
+// Script bindings.
+#include "2d/controllers/core/PickingSceneController_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(PickingSceneController);
+
+//------------------------------------------------------------------------------
+
+PickingSceneController::PickingSceneController() :
+        mControlGroupMask( MASK_ALL ),
+        mControlLayerMask( MASK_ALL )
+{
+}
+
+//------------------------------------------------------------------------------
+
+PickingSceneController::~PickingSceneController()
+{
+}
+
+//------------------------------------------------------------------------------
+
+void PickingSceneController::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to controller.
+    PickingSceneController* pController = static_cast<PickingSceneController*>(object);
+
+    // Sanity!
+    AssertFatal(pController != NULL, "PickingSceneController::copyTo() - Object is not the correct type.");
+
+    // Set masks.
+    pController->setControlGroupMask( getControlGroupMask() );
+    pController->setControlLayerMask( getControlLayerMask() );
+}
+
+//------------------------------------------------------------------------------
+
+WorldQuery* PickingSceneController::prepareQueryFilter( Scene* pScene, const bool clearQuery )
+{
+    // Fetch world query and clear results.
+    WorldQuery* pWorldQuery = pScene->getWorldQuery( clearQuery );
+
+    // Set filter.
+    WorldQueryFilter queryFilter( mControlLayerMask, mControlGroupMask, true, false, true, true );
+    pWorldQuery->setQueryFilter( queryFilter );
+
+    return pWorldQuery;
+}
+

+ 76 - 0
engine/source/2d/controllers/core/PickingSceneController.h

@@ -0,0 +1,76 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _PICKING_SCENE_CONTROLLER_H_
+#define _PICKING_SCENE_CONTROLLER_H_
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+#ifndef _SCENE_H_
+#include "2d/scene/scene.h"
+#endif
+
+#ifndef _SCENE_OBJECT_H_
+#include "2d/sceneObject/sceneObject.h"
+#endif
+
+#ifndef _SCENE_CONTROLLER_H_
+#include "2d/controllers/core/sceneController.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class PickingSceneController : public SimObject, public SceneController
+{
+    typedef SimObject Parent;
+
+private:
+    U32 mControlGroupMask;
+    U32 mControlLayerMask;
+
+public:
+    PickingSceneController();
+    virtual ~PickingSceneController();
+
+    virtual void copyTo(SimObject* object);
+
+    inline void setControlGroupMask( const U32 groupMask ) { mControlGroupMask = groupMask; }
+    inline U32 getControlGroupMask( void ) const { return mControlGroupMask; }
+    inline void setControlLayerMask( const U32 layerMask ) { mControlLayerMask = layerMask; }
+    inline U32 getControlLayerMask( void ) const { return mControlLayerMask; }
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats ) {}
+
+    // Scene render.
+    virtual void renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer ) {}
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( PickingSceneController );
+
+protected:
+    WorldQuery* prepareQueryFilter( Scene* pScene, const bool clearQuery = true );
+};
+
+#endif // _PICKING_SCENE_CONTROLLER_H_

+ 187 - 0
engine/source/2d/controllers/core/PickingSceneController_ScriptBinding.h

@@ -0,0 +1,187 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PickingSceneController, setControlGroups, void, 3, 2 + MASK_BITCOUNT, "(groups$) - Sets the control group(s).\n"
+                                                                                    "@param groups A list of groups that are affected by the controller.\n"
+                                                                                    "@return No return value.")
+{
+    // The mask.
+    U32 mask = 0;
+
+    // Grab the element count of the first parameter.
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // Make sure we get at least one number.
+    if (elementCount < 1)
+    {
+        object->setControlGroupMask(MASK_ALL);
+        return;
+    }
+
+    // Space separated list.
+    if (argc == 3)
+    {
+        // Convert the string to a mask.
+        for (U32 i = 0; i < elementCount; i++)
+        {
+            S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
+         
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
+            Con::warnf("PickingSceneController::setControlGroups() - Invalid group specified (%d); skipped!", bit);
+            continue;
+            }
+         
+            mask |= (1 << bit);
+        }
+    }
+
+    // Comma separated list.
+    else
+    {
+        // Convert the list to a mask.
+        for (U32 i = 2; i < (U32)argc; i++)
+        {
+            S32 bit = dAtoi(argv[i]);
+         
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
+            Con::warnf("PickingSceneController::setControlGroups() - Invalid group specified (%d); skipped!", bit);
+            continue;
+            }
+
+            mask |= (1 << bit);
+        }
+    }
+    // Set control groups.
+    object->setControlGroupMask(mask);
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PickingSceneController, getControlGroups, const char*, 2, 2,  "() - Gets the control groups.\n"
+                                                                            "@return (controlGroups) A list of groups that are affected by the controller.")
+{
+    U32 mask = object->getControlGroupMask();
+
+    bool first = true;
+    char* bits = Con::getReturnBuffer(128);
+    bits[0] = '\0';
+    for (S32 i = 0; i < MASK_BITCOUNT; i++)
+    {
+        if (mask & BIT(i))
+        {
+            char bit[4];
+            dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
+            first = false;
+            dStrcat(bits, bit);
+        }
+    }
+
+    return bits;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PickingSceneController, setControlLayers, void, 3, 2 + MASK_BITCOUNT, "(layers$) - Sets the control layers(s).\n"
+                                                                                    "@param layers A list of layers that are affected by the controller.\n"
+                                                                                    "@return No return value.")
+{
+    // The mask.
+    U32 mask = 0;
+
+    // Grab the element count of the first parameter.
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // Make sure we get at least one number.
+    if (elementCount < 1)
+    {
+        object->setControlLayerMask(MASK_ALL);
+        return;
+    }
+
+    // Space separated list.
+    if (argc == 3)
+    {
+        // Convert the string to a mask.
+        for (U32 i = 0; i < elementCount; i++)
+        {
+            S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
+         
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
+            Con::warnf("PickingSceneController::setControlLayers() - Invalid layer specified (%d); skipped!", bit);
+            continue;
+            }
+         
+            mask |= (1 << bit);
+        }
+    }
+
+    // Comma separated list.
+    else
+    {
+        // Convert the list to a mask.
+        for (U32 i = 2; i < (U32)argc; i++)
+        {
+            S32 bit = dAtoi(argv[i]);
+         
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
+            Con::warnf("PickingSceneController::setControlLayers() - Invalid layer specified (%d); skipped!", bit);
+            continue;
+            }
+
+            mask |= (1 << bit);
+        }
+    }
+    // Set control Layers
+    object->setControlLayerMask(mask);
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(PickingSceneController, getControlLayers, const char*, 2, 2,  "() - Gets the control layers.\n"
+                                                                            "@return (controlLayers) A list of layers that are affected by the controller.")
+{
+    U32 mask = object->getControlLayerMask();
+
+    bool first = true;
+    char* bits = Con::getReturnBuffer(128);
+    bits[0] = '\0';
+    for (S32 i = 0; i < MASK_BITCOUNT; i++)
+    {
+        if (mask & BIT(i))
+        {
+            char bit[4];
+            dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
+            first = false;
+            dStrcat(bits, bit);
+        }
+    }
+
+    return bits;
+}

+ 48 - 0
engine/source/2d/controllers/core/SceneController.h

@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SCENE_CONTROLLER_H_
+#define _SCENE_CONTROLLER_H_
+
+//------------------------------------------------------------------------------
+
+class Scene;
+struct SceneRenderState;
+class BatchRender;
+class DebugStats;
+
+//------------------------------------------------------------------------------
+
+class SceneController
+{
+public:
+    SceneController() {}
+    virtual ~SceneController() {}
+
+    /// Integration.
+    virtual void integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats ) = 0;
+
+    // Scene render.
+    virtual void renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer ) = 0;
+};
+
+#endif // _SCENE_CONTROLLER_H_

+ 129 - 75
engine/source/2d/core/SpriteBatch.cc

@@ -24,6 +24,10 @@
 #include "SpriteBatch.h"
 #endif
 
+#ifndef _SPRITE_BATCH_QUERY_H_
+#include "2d/core/spriteBatchQuery.h"
+#endif
+
 #ifndef _SCENE_RENDER_OBJECT_H_
 #include "2d/scene/SceneRenderObject.h"
 #endif
@@ -37,7 +41,7 @@ SpriteBatch::SpriteBatch() :
     mDefaultSpriteStride( 1.0f, 1.0f),
     mDefaultSpriteSize( 1.0f, 1.0f ),
     mDefaultSpriteAngle( 0.0f ),
-    mpSpriteBatchTree( NULL ),
+    mpSpriteBatchQuery( NULL ),
     mBatchCulling( true )
 {
     // Reset batch transform.
@@ -49,8 +53,8 @@ SpriteBatch::SpriteBatch() :
     mLocalExtents.SetZero();
     mLocalExtentsDirty = true;
 
-    // Create the sprite batch tree if sprite clipping is on.
-    createSpriteBatchTree();
+    // Create the sprite batch query if sprite clipping is on.
+    createSpriteBatchQuery();
 }
 
 //------------------------------------------------------------------------------
@@ -60,8 +64,8 @@ SpriteBatch::~SpriteBatch()
     // Clear the sprites.
     clearSprites();
 
-    // Delete the sprite batch tree.
-    destroySpriteBatchTree();
+    // Delete the sprite batch query.
+    destroySpriteBatchQuery();
 }
 
 //-----------------------------------------------------------------------------
@@ -77,27 +81,36 @@ void SpriteBatch::prepareRender( SceneRenderObject* pSceneRenderObject, const Sc
     // Calculate local AABB.
     const b2AABB localAABB = calculateLocalAABB( pSceneRenderState->mRenderAABB );
 
-    // Do we have a sprite batch tree?
-    if ( mpSpriteBatchTree != NULL )
+    // Do we have a sprite batch query?
+    if ( mpSpriteBatchQuery != NULL )
     {
-        // Debug Profiling.
+        // Yes, so debug Profiling.
         PROFILE_START(SpriteBatch_PrepareRenderQuery);
 
-        // Yes, so fetch sprite batch query.
-        SpriteBatchTree::typeSpriteItemVector& batchQuery = mpSpriteBatchTree->mBatchQuery;
-        batchQuery.clear();
+        // Yes, so fetch sprite batch query and clear results.
+        SpriteBatchQuery* pSpriteBatchQuery = getSpriteBatchQuery( true );
 
-        // Perform sprite batch query.
-        mpSpriteBatchTree->Query( mpSpriteBatchTree, localAABB );
+        // Perform query.
+        pSpriteBatchQuery->renderQueryArea( localAABB );
 
         // Debug Profiling.
         PROFILE_END(); // SpriteBatch_PrepareRenderQuery
 
-        // Iterate the sprite batch query results.
-        for( SpriteBatchTree::typeSpriteItemVector::iterator spriteItr = batchQuery.begin(); spriteItr != batchQuery.end(); ++spriteItr )
+        // Fetch result count.
+        const U32 resultCount = pSpriteBatchQuery->getQueryResultsCount();
+
+        // Finish if no results.
+        if ( resultCount == 0 )
+            return;
+
+        // Fetch results.
+        typeSpriteBatchQueryResultVector& queryResults = pSpriteBatchQuery->getQueryResults();
+
+        // Add picked sprites.
+        for ( U32 n = 0; n < resultCount; n++ )
         {
             // Fetch sprite batch Item.
-            SpriteBatchItem* pSpriteBatchItem = *spriteItr;
+            SpriteBatchItem* pSpriteBatchItem = queryResults[n].mpSpriteBatchItem;
 
             // Skip if not visible.
             if ( !pSpriteBatchItem->getVisible() )
@@ -117,7 +130,7 @@ void SpriteBatch::prepareRender( SceneRenderObject* pSceneRenderObject, const Sc
         }
 
         // Clear sprite batch query.
-        batchQuery.clear();
+        pSpriteBatchQuery->clearQuery();
     }
     else
     {
@@ -159,56 +172,73 @@ void SpriteBatch::render( const SceneRenderState* pSceneRenderState, const Scene
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::createTreeProxy( const b2AABB& localAABB, SpriteBatchItem* spriteBatchItem )
+void SpriteBatch::createQueryProxy( SpriteBatchItem* pSpriteBatchItem )
 {
+    // Sanity!
+    AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:createQueryProxy() - Cannot create query proxy on NULL sprite batch item." );
+
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_CreateTreeProxy);
 
-    // Finish if the batch tree is not available.
-    if ( mpSpriteBatchTree == NULL )
+    // Finish if the batch query is not available.
+    if ( mpSpriteBatchQuery == NULL )
         return;
 
     // Create proxy.
-    spriteBatchItem->mProxyId = mpSpriteBatchTree->CreateProxy( localAABB, spriteBatchItem );    
+    pSpriteBatchItem->mProxyId = mpSpriteBatchQuery->add( pSpriteBatchItem );    
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::destroyTreeProxy( SpriteBatchItem* spriteBatchItem )
+void SpriteBatch::destroyQueryProxy( SpriteBatchItem* pSpriteBatchItem )
 {
+    // Sanity!
+    AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:destroyQueryProxy() - Cannot destroy query proxy on NULL sprite batch item." );
+
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_DestroyTreeProxy);
 
-    // Finish if the batch tree is not available.
-    if ( mpSpriteBatchTree == NULL )
+    // Finish if the batch query is not available.
+    if ( mpSpriteBatchQuery == NULL )
         return;
 
-    // Fetch sprite proxy Id.
-    const S32 proxyId = spriteBatchItem->getProxyId();
-
     // Destroy proxy.
-    mpSpriteBatchTree->DestroyProxy( proxyId );
+    mpSpriteBatchQuery->remove( pSpriteBatchItem );
 
     // Remove proxy reference.
-    spriteBatchItem->mProxyId = INVALID_SPRITE_PROXY;
+    pSpriteBatchItem->mProxyId = INVALID_SPRITE_PROXY;
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::moveTreeProxy( SpriteBatchItem* spriteBatchItem, const b2AABB& localAABB )
+void SpriteBatch::moveQueryProxy( SpriteBatchItem* pSpriteBatchItem, const b2AABB& localAABB )
 {
+    // Sanity!
+    AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:moveQueryProxy() - Cannot move query proxy on NULL sprite batch item." );
+
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_MoveTreeProxy);
 
-    // Finish if the batch tree is not available.
-    if ( mpSpriteBatchTree == NULL )
+    // Finish if the batch query is not available.
+    if ( mpSpriteBatchQuery == NULL || pSpriteBatchItem->getProxyId() == INVALID_SPRITE_PROXY )
         return;
 
-    // Fetch sprite proxy Id.
-    const S32 proxyId = spriteBatchItem->getProxyId();
-
     // Move proxy.
-    mpSpriteBatchTree->MoveProxy( proxyId, localAABB, b2Vec2(0.0f, 0.0f) );
+    mpSpriteBatchQuery->update( pSpriteBatchItem, localAABB, b2Vec2(0.0f, 0.0f) );
+}
+
+//------------------------------------------------------------------------------
+
+SpriteBatchQuery* SpriteBatch::getSpriteBatchQuery( const bool clearQuery )
+{
+    if ( mpSpriteBatchQuery == NULL )
+        return NULL;
+
+    // Clear the query if specified.
+    if ( clearQuery )
+        mpSpriteBatchQuery->clearQuery();
+    
+    return mpSpriteBatchQuery;
 }
 
 //------------------------------------------------------------------------------
@@ -246,8 +276,6 @@ void SpriteBatch::copyTo( SpriteBatch* pSpriteBatch ) const
 
         // Push a copy to it.
         pSpriteBatchItem->copyTo( pNewSpriteBatchItem );
-
-
     }
 }
 
@@ -344,11 +372,11 @@ void SpriteBatch::setBatchCulling( const bool batchCulling )
     if ( mBatchCulling == batchCulling )
         return;
 
-    // Create/destroy sprite batch tree appropriately.
+    // Create/destroy sprite batch query appropriately.
     if ( mBatchCulling )
-        createSpriteBatchTree();
+        createSpriteBatchQuery();
     else
-        destroySpriteBatchTree();
+        destroySpriteBatchQuery();
 }
 
 //------------------------------------------------------------------------------
@@ -907,6 +935,9 @@ void SpriteBatch::setSpriteName( const char* pName )
     if ( findSpriteName( pName ) )
         return;
 
+    // Insert sprite name.
+    mSpriteNames.insert( StringTable->insert( pName ), mSelectedSprite );
+
     // Set name.
     mSelectedSprite->setName( pName );
 }
@@ -1105,17 +1136,17 @@ void SpriteBatch::updateLocalExtents( void )
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::createSpriteBatchTree( void )
+void SpriteBatch::createSpriteBatchQuery( void )
 {
     // Debug Profiling.
-    PROFILE_SCOPE(SpriteBatch_CreateSpriteBatchTree);
+    PROFILE_SCOPE(SpriteBatch_CreateSpriteBatchQuery);
 
-    // Finish if batch culling is off or there is already a sprite batch tree.
-    if ( !mBatchCulling || mpSpriteBatchTree != NULL )
+    // Finish if batch culling is off or there is already a sprite batch query.
+    if ( !mBatchCulling || mpSpriteBatchQuery != NULL )
         return;
 
-    // Set the sprite batch tree appropriately.
-    mpSpriteBatchTree = new SpriteBatchTree();
+    // Set the sprite batch query appropriately.
+    mpSpriteBatchQuery = new SpriteBatchQuery( this );
 
     // Finish if there are no sprites.
     if ( mSprites.size() == 0 )
@@ -1127,20 +1158,20 @@ void SpriteBatch::createSpriteBatchTree( void )
         // Fetch sprite batch item.
         SpriteBatchItem* pSpriteBatchItem = spriteItr->value;
 
-        // Create tree proxy for sprite.
-        createTreeProxy( pSpriteBatchItem->getLocalAABB(), pSpriteBatchItem );
+        // Create query proxy for sprite.
+        createQueryProxy( pSpriteBatchItem );
     }
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::destroySpriteBatchTree( void )
+void SpriteBatch::destroySpriteBatchQuery( void )
 {
     // Debug Profiling.
-    PROFILE_SCOPE(SpriteBatch_DestroySpriteBatchTree);
+    PROFILE_SCOPE(SpriteBatch_DestroySpriteBatchQuery);
 
-    // Finish if there is no sprite batch tree.
-    if ( mpSpriteBatchTree == NULL )
+    // Finish if there is no sprite batch query.
+    if ( mpSpriteBatchQuery == NULL )
         return;
 
     // Are there any sprites?
@@ -1149,18 +1180,18 @@ void SpriteBatch::destroySpriteBatchTree( void )
         // Yes, so destroy proxies of all the sprites.
         for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr )
         {
-            // Destroy tree proxy for sprite.
-            destroyTreeProxy( spriteItr->value );
+            // Destroy query proxy for sprite.
+            destroyQueryProxy( spriteItr->value );
         }
     }
 
     // Finish if sprite clipping 
-    delete mpSpriteBatchTree;
+    delete mpSpriteBatchQuery;
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::onTamlCustomWrite( TamlCustomProperty* pSpritesProperty )
+void SpriteBatch::onTamlCustomWrite( TamlCustomNode* pSpritesNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_TamlCustomWrite);
@@ -1171,38 +1202,41 @@ void SpriteBatch::onTamlCustomWrite( TamlCustomProperty* pSpritesProperty )
     // Write all sprites.
     for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pSpriteAlias = pSpritesProperty->addAlias( spriteItemTypeName );
+        // Add sprite node.
+        TamlCustomNode* pNode = pSpritesNode->addNode( spriteItemTypeName );
         
         // Write type with sprite item.
-        spriteItr->value->onTamlCustomWrite( pSpriteAlias );
+        spriteItr->value->onTamlCustomWrite( pNode );
     }
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::onTamlCustomRead( const TamlCustomProperty* pSpritesProperty )
+void SpriteBatch::onTamlCustomRead( const TamlCustomNode* pSpritesNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_TamlCustomRead);
 
-    // Fetch property names.
-    StringTableEntry spriteItemTypeName = StringTable->insert( "Sprite" );
+    // Fetch node name.
+    StringTableEntry spriteItemNodeName = StringTable->insert( "Sprite" );
+
+    // Fetch children nodes.
+    const TamlCustomNodeVector& spriteNodes = pSpritesNode->getChildren();
 
     // Iterate sprite item types.
-    for( TamlCustomProperty::const_iterator spriteAliasItr = pSpritesProperty->begin(); spriteAliasItr != pSpritesProperty->end(); ++spriteAliasItr )
+    for( TamlCustomNodeVector::const_iterator spriteItr = spriteNodes.begin(); spriteItr != spriteNodes.end(); ++spriteItr )
     {
-        // Fetch sprite alias.
-        TamlPropertyAlias* pSpriteAlias = *spriteAliasItr;
+        // Fetch sprite node.
+        TamlCustomNode* pNode = *spriteItr;
 
         // Fetch alias name.
-        StringTableEntry aliasName = pSpriteAlias->mAliasName;
+        StringTableEntry nodeName = pNode->getNodeName();
 
-        // Is this a known alias?
-        if ( aliasName != spriteItemTypeName )
+        // Is this a known node name?
+        if ( nodeName != spriteItemNodeName )
         {
             // No, so warn.
-            Con::warnf( "SpriteBatch - Unknown custom type '%s'.", aliasName );
+            Con::warnf( "SpriteBatch - Unknown custom type '%s'.", nodeName );
             continue;
         }
 
@@ -1210,7 +1244,7 @@ void SpriteBatch::onTamlCustomRead( const TamlCustomProperty* pSpritesProperty )
         SpriteBatchItem* pSpriteBatchItem = createSprite();
 
         // Read type with sprite item.
-        pSpriteBatchItem->onTamlCustomRead( pSpriteAlias );
+        pSpriteBatchItem->onTamlCustomRead( pNode );
 
         // Fetch logical position.
         const SpriteBatchItem::LogicalPosition& logicalPosition = pSpriteBatchItem->getLogicalPosition();
@@ -1221,6 +1255,17 @@ void SpriteBatch::onTamlCustomRead( const TamlCustomProperty* pSpritesProperty )
             // Yes, so insert into sprite positions.
             mSpritePositions.insert( logicalPosition, pSpriteBatchItem );
         }
+
+        // Fetch sprite name.
+        StringTableEntry spriteName = pSpriteBatchItem->getName();
+
+        // Did we get a sprite name?
+        if ( spriteName != StringTable->EmptyString )
+        {
+            // Yes, so insert into sprite names if it doesn't already exist.
+            if ( mSpriteNames.find( spriteName ) != mSpriteNames.end() ) 
+                mSpriteNames.insert( spriteName, mSelectedSprite );
+        }
     }
 }
 
@@ -1231,16 +1276,25 @@ bool SpriteBatch::destroySprite( const U32 batchId )
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_DestroySprite);
 
-    // Find sprite.    
-    SpriteBatchItem* pSpriteBatchItem = findSpriteId( batchId );
+    // Find sprite.
+    typeSpriteBatchHash::iterator spriteItr = mSprites.find( batchId );
 
-    // Finish if not found.
-    if ( pSpriteBatchItem == NULL )
+    // Finish if sprite not found.
+    if ( spriteItr == mSprites.end() )
         return false;
 
+    // Find sprite.    
+    SpriteBatchItem* pSpriteBatchItem = spriteItr->value;
+
+    // Sanity!
+    AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch::destroySprite() - Found sprite but it was NULL." );
+
     // Cache sprite.
     SpriteBatchItemFactory.cacheObject( pSpriteBatchItem );
 
+    // Remove from sprites.
+    mSprites.erase( batchId );
+
     return true;
 }
 

+ 14 - 35
engine/source/2d/core/SpriteBatch.h

@@ -33,35 +33,12 @@
 
 //------------------------------------------------------------------------------  
 
-class SpriteBatch
-{
-protected:
-    /// Sprite batch tree.
-    class SpriteBatchTree : public b2DynamicTree
-    {
-        friend class b2DynamicTree;
-
-    public:
-        // Render query.
-        inline bool QueryCallback( S32 proxyId )
-        {    
-            // Fetch sprite batch item.    
-            SpriteBatchItem* pSpriteBatchItem = static_cast<SpriteBatchItem*>( GetUserData( proxyId ) );
-
-            // Ignore if not visible.
-            if ( !pSpriteBatchItem->getVisible() )
-                return true;
-
-            // Use sprite batch item.
-            mBatchQuery.push_back( pSpriteBatchItem );
+class SpriteBatchQuery;
 
-            return true;
-        }
-
-        typedef Vector< SpriteBatchItem* > typeSpriteItemVector;
-        typeSpriteItemVector mBatchQuery;
-    };
+//------------------------------------------------------------------------------  
 
+class SpriteBatch
+{
 public:
     static const S32                INVALID_SPRITE_PROXY = -1;  
 
@@ -81,7 +58,7 @@ protected:
     F32                             mDefaultSpriteAngle;
 
 private:
-    SpriteBatchTree*                mpSpriteBatchTree;
+    SpriteBatchQuery*               mpSpriteBatchQuery;
     U32                             mMasterBatchId;
 
     b2Transform                     mBatchTransform;
@@ -107,9 +84,11 @@ public:
     inline bool getLocalExtentsDirty( void ) const { return mLocalExtentsDirty; }
     inline const Vector2& getLocalExtents( void ) { if ( getLocalExtentsDirty() ) updateLocalExtents(); return mLocalExtents; }
 
-    void createTreeProxy( const b2AABB& localAABB, SpriteBatchItem* spriteBatchItem );
-    void destroyTreeProxy( SpriteBatchItem* spriteBatchItem );
-    void moveTreeProxy( SpriteBatchItem* spriteBatchItem, const b2AABB& localAABB );         
+    void createQueryProxy( SpriteBatchItem* pSpriteBatchItem );
+    void destroyQueryProxy( SpriteBatchItem* pSpriteBatchItem );
+    void moveQueryProxy( SpriteBatchItem* pSpriteBatchItem, const b2AABB& localAABB );    
+    SpriteBatchQuery* getSpriteBatchQuery( const bool clearQuery = false );
+
 
     virtual void copyTo( SpriteBatch* pSpriteBatch ) const;
 
@@ -206,11 +185,11 @@ protected:
     void setBatchTransform( const b2Transform& batchTransform );
     void updateLocalExtents( void );
 
-    void createSpriteBatchTree( void );
-    void destroySpriteBatchTree( void );
+    void createSpriteBatchQuery( void );
+    void destroySpriteBatchQuery( void );
 
-    void onTamlCustomWrite( TamlCustomProperty* pSpritesProperty );
-    void onTamlCustomRead( const TamlCustomProperty* pSpritesProperty );
+    void onTamlCustomWrite( TamlCustomNode* pSpritesNode );
+    void onTamlCustomRead( const TamlCustomNode* pSpritesNode );
 
 private:
     bool destroySprite( const U32 batchId );

+ 40 - 32
engine/source/2d/core/SpriteBatchItem.cc

@@ -119,7 +119,7 @@ void SpriteBatchItem::resetState( void )
         AssertFatal( mSpriteBatch != NULL, "Cannot remove proxy with NULL sprite batch." );
 
         // Destroy proxy.
-        mSpriteBatch->destroyTreeProxy( this );
+        mSpriteBatch->destroyQueryProxy( this );
     }
 
     mSpriteBatch = NULL;
@@ -157,6 +157,8 @@ void SpriteBatchItem::resetState( void )
     mRenderPosition.setZero();
     mLastBatchTransformId = 0;
 
+    mSpriteBatchQueryKey = 0;
+
     // Require self ticking.
     mSelfTick = true;
 }
@@ -175,7 +177,7 @@ void SpriteBatchItem::setBatchParent( SpriteBatch* pSpriteBatch, const U32 batch
     mBatchId = batchId;
 
     // Create proxy.
-    mSpriteBatch->createTreeProxy( mLocalAABB, this );
+    mSpriteBatch->createQueryProxy( this );
 }
 
 //------------------------------------------------------------------------------
@@ -289,8 +291,8 @@ void SpriteBatchItem::updateLocalTransform( void )
     // Calculate local AABB.
     CoreMath::mOOBBtoAABB( mLocalOOBB, mLocalAABB );
 
-    // Move tree proxy.
-    mSpriteBatch->moveTreeProxy( this, mLocalAABB );
+    // Move query proxy.
+    mSpriteBatch->moveQueryProxy( this, mLocalAABB );
 
     // Flag local transform as NOT dirty.
     mLocalTransformDirty = false;
@@ -332,11 +334,11 @@ void SpriteBatchItem::updateWorldTransform( const U32 batchTransformId )
 
 //------------------------------------------------------------------------------
 
-void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
+void SpriteBatchItem::onTamlCustomWrite( TamlCustomNode* pSpriteNode )
 {
     // Write name.
     if ( getName() != StringTable->EmptyString )
-        pSpriteAlias->addField( spriteNameName, getName() );
+        pSpriteNode->addField( spriteNameName, getName() );
 
     // Write asset.
     if ( isStaticMode() )
@@ -348,10 +350,10 @@ void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
         if ( assetId != StringTable->EmptyString )
         {
             // Yes, so write image asset Id.
-            pSpriteAlias->addField( spriteImageName, assetId );
+            pSpriteNode->addField( spriteImageName, assetId );
 
             // Write image frame.
-            pSpriteAlias->addField( spriteImageFrameName, getImageFrame() );
+            pSpriteNode->addField( spriteImageFrameName, getImageFrame() );
         }
     }
     else
@@ -363,86 +365,89 @@ void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
         if ( assetId != StringTable->EmptyString )
         {
             // Yes, so write animation asset Id.
-            pSpriteAlias->addField( spriteAnimationName, assetId );
+            pSpriteNode->addField( spriteAnimationName, assetId );
 
         }
     }
 
     // Write visible.
     if ( !mVisible )
-        pSpriteAlias->addField( spriteVisibleName, mVisible );
+        pSpriteNode->addField( spriteVisibleName, mVisible );
 
     // Write local position.
-    pSpriteAlias->addField( spriteLocalPositionName, mLocalPosition );
+    pSpriteNode->addField( spriteLocalPositionName, mLocalPosition );
 
     // Write local angle.
     if ( mNotZero(mLocalAngle) )
-        pSpriteAlias->addField( spriteLocalAngleName, mRadToDeg(mLocalAngle) );
+        pSpriteNode->addField( spriteLocalAngleName, mRadToDeg(mLocalAngle) );
 
     // Write size.
-    pSpriteAlias->addField( spriteSizeName, mSize );
+    pSpriteNode->addField( spriteSizeName, mSize );
 
     // Write depth.
     if ( mNotZero(mDepth) )
-        pSpriteAlias->addField( spriteDepthName, mDepth );
+        pSpriteNode->addField( spriteDepthName, mDepth );
 
     // Write flipX
     if ( mFlipX )
-        pSpriteAlias->addField( spriteFlipXName, mFlipX );
+        pSpriteNode->addField( spriteFlipXName, mFlipX );
 
     // Write flipY
     if ( mFlipY )
-        pSpriteAlias->addField( spriteFlipYName, mFlipY );
+        pSpriteNode->addField( spriteFlipYName, mFlipY );
 
     // Write sort point.
     if ( mSortPoint.notZero() )
-        pSpriteAlias->addField( spriteSortPointName, mSortPoint );
+        pSpriteNode->addField( spriteSortPointName, mSortPoint );
 
     // Write render group.
     if ( mRenderGroup != StringTable->EmptyString )
-        pSpriteAlias->addField( spriteRenderGroupName, mRenderGroup );
+        pSpriteNode->addField( spriteRenderGroupName, mRenderGroup );
 
     // Write blend mode.
     if ( !mBlendMode )
-        pSpriteAlias->addField( spriteBlendModeName, mBlendMode );
+        pSpriteNode->addField( spriteBlendModeName, mBlendMode );
 
     // Write source blend factor.
     if ( mBlendMode && mSrcBlendFactor != GL_SRC_ALPHA )
-        pSpriteAlias->addField( spriteBlendModeName, SceneObject::getSrcBlendFactorDescription(mSrcBlendFactor) );
+        pSpriteNode->addField( spriteBlendModeName, SceneObject::getSrcBlendFactorDescription(mSrcBlendFactor) );
         
     // Write destination blend factor.
     if ( mBlendMode && mDstBlendFactor != GL_ONE_MINUS_SRC_ALPHA )
-        pSpriteAlias->addField( spriteDstBlendFactorName, SceneObject::getDstBlendFactorDescription(mDstBlendFactor) );
+        pSpriteNode->addField( spriteDstBlendFactorName, SceneObject::getDstBlendFactorDescription(mDstBlendFactor) );
 
     // Write blend color.
     if ( mBlendMode && mBlendColor != ColorF(1.0f, 1.0f, 1.0f, 1.0f) )
-        pSpriteAlias->addField( spriteBlendColorName, mBlendColor );
+        pSpriteNode->addField( spriteBlendColorName, mBlendColor );
 
     // Write alpha test.
     if ( mBlendMode && mAlphaTest >= 0.0f )
-        pSpriteAlias->addField( spriteAlphaTestName, mAlphaTest );
+        pSpriteNode->addField( spriteAlphaTestName, mAlphaTest );
 
     // Write logical position.
     if ( getLogicalPosition().isValid() )
-        pSpriteAlias->addField( spriteLogicalPositionName, getLogicalPosition().getString() );
+        pSpriteNode->addField( spriteLogicalPositionName, getLogicalPosition().getString() );
 
     // Write data object.
     if ( getDataObject() != NULL )
-        pSpriteAlias->addField( spriteDataObjectName, getDataObject() );
+        pSpriteNode->addNode( getDataObject() );
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatchItem::onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias )
+void SpriteBatchItem::onTamlCustomRead( const TamlCustomNode* pSpriteNode )
 {
     // Sanity!
     AssertFatal( mSpriteBatch != NULL, "SpriteBatchItem::onTamlCustomRead() - Cannot read sprite batch item with sprite batch." );
 
+    // Fetch sprite fields.
+    const TamlCustomFieldVector& spriteField = pSpriteNode->getFields();
+
     // Iterate property fields.
-    for ( TamlPropertyAlias::const_iterator propertyFieldItr = pSpriteAlias->begin(); propertyFieldItr != pSpriteAlias->end(); ++propertyFieldItr )
+    for ( TamlCustomFieldVector::const_iterator fieldItr = spriteField.begin(); fieldItr != spriteField.end(); ++fieldItr )
     {
         // Fetch sprite field.
-        TamlPropertyField* pSpriteField = *propertyFieldItr;
+        TamlCustomField* pSpriteField = *fieldItr;
 
         // Fetch sprite field name.
         StringTableEntry fieldName = pSpriteField->getFieldName();
@@ -570,9 +575,12 @@ void SpriteBatchItem::onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias )
             // Set logical position.
             setLogicalPosition( LogicalPosition( pLogicalPositionArgs ) );
         }
-        else if ( fieldName == spriteDataObjectName )
-        {            
-            setDataObject( pSpriteField->getFieldObject() );
-        }
     }
+
+    // Fetch sprite children.
+    const TamlCustomNodeVector& spriteChildren = pSpriteNode->getChildren();
+
+    // Set the data object if a single child exists.
+    if ( spriteChildren.size() == 1 )
+        setDataObject( spriteChildren[0]->getProxyObject<SimObject>(true) );
 }

+ 7 - 2
engine/source/2d/core/SpriteBatchItem.h

@@ -223,6 +223,8 @@ protected:
     Vector2             mRenderPosition;
     U32                 mLastBatchTransformId;
 
+    U32                 mSpriteBatchQueryKey;
+
 public:
     SpriteBatchItem();
     virtual ~SpriteBatchItem();
@@ -282,6 +284,9 @@ public:
     inline void setDataObject( SimObject* pDataObject ) { mDataObject = pDataObject; }
     inline SimObject* getDataObject( void ) const { return mDataObject; }
 
+    inline void setSpriteBatchQueryKey( const U32 key ) { mSpriteBatchQueryKey = key; }
+    inline U32  getSpriteBatchQueryKey( void ) const { return mSpriteBatchQueryKey; }
+
     virtual void copyTo( SpriteBatchItem* pSpriteBatchItem ) const;
 
     void prepareRender( SceneRenderRequest* pSceneRenderRequest, const U32 batchTransformId );
@@ -294,8 +299,8 @@ protected:
     void updateLocalTransform( void );
     void updateWorldTransform( const U32 batchTransformId );
 
-    void onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias );
-    void onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias );
+    void onTamlCustomWrite( TamlCustomNode* pSpriteNode );
+    void onTamlCustomRead( const TamlCustomNode* pSpriteNode );
 };
 
 //------------------------------------------------------------------------------  

+ 238 - 0
engine/source/2d/core/SpriteBatchQuery.cc

@@ -0,0 +1,238 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "2d/core/SpriteBatchQuery.h"
+
+#ifndef _SPRITE_BATCH_H_
+#include "2d/core/spriteBatch.h"
+#endif
+
+#ifndef _SPRITE_BATCH_ITEM_H_
+#include "2d/core/spriteBatchItem.h"
+#endif
+
+// Debug Profiling.
+#include "debug/profiler.h"
+
+//-----------------------------------------------------------------------------
+
+SpriteBatchQuery::SpriteBatchQuery( SpriteBatch* pSpriteBatch ) :
+        mpSpriteBatch(pSpriteBatch),
+        mIsRaycastQueryResult(false),
+        mMasterQueryKey(0),
+        mCheckFixturePoint(false),
+        mFixturePoint(0.0f, 0.0f)
+{
+    // Set debug associations.
+    VECTOR_SET_ASSOCIATION( mQueryResults );
+
+    // Clear the query.
+    clearQuery();
+}
+
+//-----------------------------------------------------------------------------
+
+S32 SpriteBatchQuery::add( SpriteBatchItem* pSpriteBatchItem )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_Add);
+
+    return CreateProxy( pSpriteBatchItem->getLocalAABB(), pSpriteBatchItem );
+}
+
+//-----------------------------------------------------------------------------
+
+void SpriteBatchQuery::remove( SpriteBatchItem* pSpriteBatchItem )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_Remove);
+
+    DestroyProxy( pSpriteBatchItem->getProxyId() );
+}
+
+//-----------------------------------------------------------------------------
+
+bool SpriteBatchQuery::update( SpriteBatchItem* pSpriteBatchItem, const b2AABB& aabb, const b2Vec2& displacement )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_Update);
+
+    return MoveProxy( pSpriteBatchItem->getProxyId(), aabb, displacement );
+}
+
+//-----------------------------------------------------------------------------
+
+U32 SpriteBatchQuery::renderQueryArea( const b2AABB& aabb )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_RenderQueryArea);
+
+    mMasterQueryKey++;
+
+    // Flag as not a ray-cast query result.
+    mIsRaycastQueryResult = false;
+
+    // Query.
+    Query( this, aabb );
+
+    return getQueryResultsCount();
+}
+
+//-----------------------------------------------------------------------------
+
+U32 SpriteBatchQuery::renderQueryRay( const Vector2& point1, const Vector2& point2 )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_RenderQueryRay);
+
+    mMasterQueryKey++;
+
+    // Flag as a ray-cast query result.
+    mIsRaycastQueryResult = true;
+
+    // Query.
+    b2RayCastInput rayInput;
+    rayInput.p1 = point1;
+    rayInput.p2 = point2;
+
+    rayInput.maxFraction = 1.0f;
+
+    RayCast( this, rayInput );
+
+    return getQueryResultsCount();
+}
+
+//-----------------------------------------------------------------------------
+
+U32 SpriteBatchQuery::renderQueryPoint( const Vector2& point )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_RenderQueryPoint);
+
+    mMasterQueryKey++;
+
+    // Flag as not a ray-cast query result.
+    mIsRaycastQueryResult = false;
+
+    // Query.
+    b2RayCastInput rayInput;
+    rayInput.p1 = point;
+    rayInput.p2 = b2Vec2( point.x + b2_linearSlop, point.y + b2_linearSlop );
+    rayInput.maxFraction = 1.0f;
+
+    RayCast( this, rayInput );
+
+    return getQueryResultsCount();
+}
+
+//-----------------------------------------------------------------------------
+
+void SpriteBatchQuery::clearQuery( void )
+{
+    mQueryResults.clear();
+}
+
+//-----------------------------------------------------------------------------
+
+void SpriteBatchQuery::sortRaycastQueryResult( void )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_SortRayCastQueryResult);
+
+    // Ignore if not a ray-cast query result or there are not results to sort.
+    if ( !getIsRaycastQueryResult() || getQueryResultsCount() == 0 )
+        return;
+
+    // Sort query results.
+    dQsort( mQueryResults.address(), mQueryResults.size(), sizeof(SpriteBatchQueryResult), rayCastFractionSort );
+}
+
+//-----------------------------------------------------------------------------
+
+bool SpriteBatchQuery::QueryCallback( S32 proxyId )
+{    
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_QueryCallback);
+
+    // Fetch sprite batch item.
+    SpriteBatchItem* pSpriteBatchItem = static_cast<SpriteBatchItem*>(GetUserData( proxyId ));
+
+    // Ignore if already tagged with the sprite batch query key.
+    if ( pSpriteBatchItem->getSpriteBatchQueryKey() == mMasterQueryKey )
+        return true;
+
+    // Tag with world query key.
+    pSpriteBatchItem->setSpriteBatchQueryKey( mMasterQueryKey );
+
+    SpriteBatchQueryResult queryResult( pSpriteBatchItem );
+    mQueryResults.push_back( queryResult );
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+F32 SpriteBatchQuery::RayCastCallback( const b2RayCastInput& input, S32 proxyId )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_RayCastCallback);
+
+    // Fetch sprite batch item.
+    SpriteBatchItem* pSpriteBatchItem = static_cast<SpriteBatchItem*>(GetUserData( proxyId ));
+
+    // Ignore if already tagged with the sprite batch query key.
+    if ( pSpriteBatchItem->getSpriteBatchQueryKey() == mMasterQueryKey )
+        return 1.0f;
+
+    // Tag with world query key.
+    pSpriteBatchItem->setSpriteBatchQueryKey( mMasterQueryKey );
+
+    SpriteBatchQueryResult queryResult( pSpriteBatchItem );
+    mQueryResults.push_back( queryResult );
+
+    return 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+
+S32 QSORT_CALLBACK SpriteBatchQuery::rayCastFractionSort(const void* a, const void* b)
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatchQuery_RayCastFractionSort);
+
+    // Fetch scene objects.
+    SpriteBatchQueryResult* pQueryResultA  = (SpriteBatchQueryResult*)a;
+    SpriteBatchQueryResult* pQueryResultB  = (SpriteBatchQueryResult*)b;
+
+    // Fetch fractions.
+    const F32 queryFractionA = pQueryResultA->mFraction;
+    const F32 queryFractionB = pQueryResultB->mFraction;
+
+    if ( queryFractionA < queryFractionB )
+        return -1;
+
+    if ( queryFractionA > queryFractionB )
+        return 1;
+
+    return 0;
+}
+

+ 85 - 0
engine/source/2d/core/SpriteBatchQuery.h

@@ -0,0 +1,85 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SPRITE_BATCH_QUERY_H_
+#define _SPRITE_BATCH_QUERY_H_
+
+#ifndef _SPRITE_BATCH_QUERY_RESULT_H_
+#include "2d/core/SpriteBatchQueryResult.h"
+#endif
+
+///-----------------------------------------------------------------------------
+
+class SpriteBatch;
+class SpriteBatchItem;
+
+///-----------------------------------------------------------------------------
+
+class SpriteBatchQuery :
+    protected b2DynamicTree,
+    public b2QueryCallback,
+    public b2RayCastCallback
+{
+public:
+    SpriteBatchQuery( SpriteBatch* pSpriteBatch );
+    virtual         ~SpriteBatchQuery() {}
+
+    /// Standard scope.
+    S32             add( SpriteBatchItem* pSpriteBatchItem );
+    void            remove( SpriteBatchItem* pSpriteBatchItem );
+    bool            update( SpriteBatchItem* pSpriteBatchItem, const b2AABB& aabb, const b2Vec2& displacement );
+
+    //// Render queries.
+    U32             renderQueryArea( const b2AABB& aabb );
+    U32             renderQueryRay( const Vector2& point1, const Vector2& point2 );
+    U32             renderQueryPoint( const Vector2& point );
+ 
+    /// Results.
+    void            clearQuery( void );
+    typeSpriteBatchQueryResultVector& getQueryResults( void ) { return mQueryResults; }
+    inline U32      getQueryResultsCount( void ) const { return mQueryResults.size(); }
+    inline bool     getIsRaycastQueryResult( void ) const { return mIsRaycastQueryResult; }
+    void            sortRaycastQueryResult( void );
+
+    /// Unused result callbacks.
+    virtual bool    ReportFixture( b2Fixture* fixture ) { return true; }
+    virtual F32     ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, F32 fraction ) { return 1.0; }
+
+
+    /// Callbacks.
+    bool            QueryCallback( S32 proxyId );
+    F32             RayCastCallback( const b2RayCastInput& input, S32 proxyId );
+
+private:
+    static S32      QSORT_CALLBACK rayCastFractionSort(const void* a, const void* b);
+
+private:
+    SpriteBatch*                mpSpriteBatch;
+    bool                        mCheckFixturePoint;
+    b2Vec2                      mFixturePoint;
+    typeSpriteBatchQueryResultVector mQueryResults;
+    bool                        mIsRaycastQueryResult;
+    typeSceneObjectVector       mAlwaysInScopeSet;
+    U32                         mMasterQueryKey;
+};
+
+#endif // _SPRITE_BATCH_QUERY_H_

+ 78 - 0
engine/source/2d/core/SpriteBatchQueryResult.h

@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SPRITE_BATCH_QUERY_RESULT_H_
+#define _SPRITE_BATCH_QUERY_RESULT_H_
+
+#ifndef _VECTOR2_H_
+#include "2d/core/Vector2.h"
+#endif
+
+///-----------------------------------------------------------------------------
+
+class SpriteBatchItem;
+
+///-----------------------------------------------------------------------------
+
+struct SpriteBatchQueryResult
+{
+    SpriteBatchQueryResult() :
+        mpSpriteBatchItem( NULL ),
+        mShapeIndex( 0 ),
+        mPoint( 0.0f, 0.0f ),
+        mNormal( 0.0f, 0.0f ),
+        mFraction( 0.0f )
+    {
+    }
+
+    /// Initialize a non-ray-cast result.
+    SpriteBatchQueryResult( SpriteBatchItem* mpSpriteBatchItem ) :
+        mpSpriteBatchItem( mpSpriteBatchItem ),
+        mShapeIndex( 0 ),
+        mPoint( 0.0f, 0.0f ),
+        mNormal( 0.0f, 0.0f ),
+        mFraction( 0.0f )
+    {
+    }
+
+    /// Initialize a ray-cast result.    
+    SpriteBatchQueryResult( SpriteBatchItem* mpSpriteBatchItem, const b2Vec2& point, const b2Vec2& normal, const F32 fraction, const U32 shapeIndex ) :
+        mpSpriteBatchItem( mpSpriteBatchItem ),
+        mShapeIndex( shapeIndex ),
+        mPoint( point ),
+        mNormal( normal ),
+        mFraction( fraction )
+    {
+    }
+
+    b2Vec2          mPoint;
+    b2Vec2          mNormal;
+    F32             mFraction;
+    SpriteBatchItem* mpSpriteBatchItem;
+    U32             mShapeIndex;
+};
+
+///-----------------------------------------------------------------------------
+
+typedef Vector<SpriteBatchQueryResult> typeSpriteBatchQueryResultVector;
+
+#endif // _SPRITE_BATCH_QUERY_RESULT_H_

+ 43 - 0
engine/source/2d/core/Utility.cc

@@ -30,6 +30,49 @@
 
 //-----------------------------------------------------------------------------
 
+ConsoleType( b2AABB, Typeb2AABB, sizeof(b2AABB), "" )
+
+ConsoleGetType( Typeb2AABB )
+{
+    // Fetch AABB.
+    const b2AABB* pAABB = (b2AABB*)dptr;
+
+    // Format AABB.
+    char* pBuffer = Con::getReturnBuffer(64);
+    dSprintf(pBuffer, 64, "%.5g %.5g", pAABB->lowerBound.x, pAABB->lowerBound.y, pAABB->upperBound.x, pAABB->upperBound.y );
+    return pBuffer;
+}
+
+ConsoleSetType( Typeb2AABB )
+{
+    // Fetch AABB.
+    b2AABB* pAABB = (b2AABB*)dptr;
+
+    // "lowerX lowerY upperX upperY".
+    if( argc == 1 )
+    {
+        if ( dSscanf(argv[0], "%g %g %g %g", &(pAABB->lowerBound.x), &(pAABB->lowerBound.y), &(pAABB->upperBound.x), &(pAABB->upperBound.y) ) == 4 )
+            return;
+    }
+    
+    // "lowerX,lowerY,upperX,upperY".
+    else if( argc == 4 )
+    {
+        pAABB->lowerBound.Set( dAtof(argv[0]), dAtof(argv[1] ) );
+        pAABB->upperBound.Set( dAtof(argv[2]), dAtof(argv[3] ) );
+        return;
+    }
+
+    // Reset the AABB.
+    pAABB->lowerBound.SetZero();
+    pAABB->upperBound.SetZero();
+
+    // Warn.
+    Con::printf("Typeb2AABB must be set as { lowerX, lowerY, upperX, upperY } or \"lowerX lowerY upperX upperY\"");
+}
+
+//-----------------------------------------------------------------------------
+
 namespace Utility
 {
 

+ 6 - 8
engine/source/2d/core/Utility.h

@@ -39,7 +39,6 @@
 #include "math/mMath.h"
 #endif
 
-
 //-----------------------------------------------------------------------------
 // Miscellaneous Defines.
 //-----------------------------------------------------------------------------
@@ -47,25 +46,24 @@
 #define MASK_ALL                        (U32_MAX)
 #define MASK_BITCOUNT                   (32)
 #define DEBUG_MODE_COUNT                (8)
-
 #define MAX_LAYERS_SUPPORTED            (32)
-
-#define MAX_CONTACTPOINTS_SUPPORTED     (1024)
-
 #define CANNOT_RENDER_PROXY_NAME        "CannotRenderProxy"
 #define b2_pi2                          (b2_pi * 2.0f)
 
 //-----------------------------------------------------------------------------
 
-class Scene; // Yuk!
 class SceneObject;
 struct Vector2;
 
-//-----------------------------------------------------------------------------
-
 typedef Vector<SceneObject*> typeSceneObjectVector;
 typedef const Vector<SceneObject*>& typeSceneObjectVectorConstRef;
 
+///-----------------------------------------------------------------------------
+
+DefineConsoleType( Typeb2AABB )
+
+//-----------------------------------------------------------------------------
+
 namespace Utility
 {
 

+ 3 - 5
engine/source/2d/core/Vector2.cc

@@ -38,20 +38,18 @@ ConsoleSetType( TypeVector2 )
 {
     // Fetch vector.
     Vector2* pVector = (Vector2*)dptr;
+
     // "x y".
     if( argc == 1 )
     {
-        if ( dSscanf(argv[0], "%g %g", &(pVector->x), &(pVector->y)) == 2 )
-            return;
-
-        pVector->y = pVector->x;
+        pVector->setString( argv[0] );
         return;
     }
 
     // "x,y".
     if( argc == 2 )
     {
-        *((Vector2*)dptr) = Vector2(dAtof(argv[0]), dAtof(argv[1]));
+        pVector->Set(dAtof(argv[0]), dAtof(argv[1]));
         return;
     }
 

+ 21 - 20
engine/source/2d/core/Vector2.h

@@ -59,25 +59,7 @@ struct Vector2 : b2Vec2
     inline Vector2( const Point2I& point ) : b2Vec2( F32(point.x), F32(point.y) ) {}
     inline Vector2( const Point2F& point ) : b2Vec2( point.x, point.y ) {}
     inline Vector2( const Point2D& point ) : b2Vec2( F32(point.x), F32(point.y) ) {}
-    inline Vector2( const char* pString )
-    {
-        const U32 elementCount = Utility::mGetStringElementCount(pString);
-
-        if ( elementCount == 0 )
-        {
-            SetZero();
-            return;
-        }
-
-        if ( elementCount == 1 )
-        {
-            x = y = dAtof(Utility::mGetStringElement(pString,0));
-            return;
-        }
-
-        x = dAtof(Utility::mGetStringElement(pString,0));
-        y = dAtof(Utility::mGetStringElement(pString,1));
-    }
+    inline Vector2( const char* pString ) { setString( pString ); }
 
     /// Operators.
     inline Vector2& operator /= (const F32 s)                           { x /= s; y /= s; return *this; }
@@ -105,7 +87,26 @@ struct Vector2 : b2Vec2
 
     /// Utility.
     inline void setAngle(const F32 radians)                             { x = mSin(radians); y = mCos(radians); }
-    inline void setPolar(const F32 radians,F32 length)                  { x = mSin(radians)*length; y = mCos(radians*length); }
+    inline void setPolar(const F32 radians,F32 length)                  { x = mSin(radians)*length; y = mCos(radians)*length; }
+    inline void setString(const char* pString )
+    {
+        const U32 elementCount = Utility::mGetStringElementCount(pString);
+
+        if ( elementCount == 0 )
+        {
+            SetZero();
+            return;
+        }
+
+        if ( elementCount == 1 )
+        {
+            x = y = dAtof(Utility::mGetStringElement(pString,0));
+            return;
+        }
+
+        x = dAtof(Utility::mGetStringElement(pString,0));
+        y = dAtof(Utility::mGetStringElement(pString,1));
+    }
     inline const Vector2& setZero()                                     { (*this) = getZero(); return *this; }
     inline const Vector2& setOne()                                      { (*this) = getOne(); return *this; }
     inline static const Vector2& getZero()                              { static const Vector2 v(0.0f, 0.0f); return v; }

+ 23 - 3
engine/source/2d/core/Vector2_ScriptBinding.h

@@ -203,7 +203,7 @@ ConsoleFunction( Vector2Distance, F32, 3, 3, "(Vector2 p1, Vector2 p2) - Returns
 //-----------------------------------------------------------------------------
 // Angle between two 2D Vectors.
 //-----------------------------------------------------------------------------
-ConsoleFunction( t2dAngleBetween, F32, 3, 3, "(Vector2 v1, Vector2 v2) - Returns the angle between v1 and v2.")
+ConsoleFunction( Vector2AngleBetween, F32, 3, 3, "(Vector2 v1, Vector2 v2) - Returns the angle between v1 and v2.")
 {
     // Check Parameters.
     if (Utility::mGetStringElementCount(argv[1]) < 2 ||Utility::mGetStringElementCount(argv[2]) < 2 )
@@ -224,7 +224,7 @@ ConsoleFunction( t2dAngleBetween, F32, 3, 3, "(Vector2 v1, Vector2 v2) - Returns
 //-----------------------------------------------------------------------------
 // Angle from one point to another.
 //-----------------------------------------------------------------------------
-ConsoleFunction( t2dAngleToPoint, F32, 3, 3, "(Vector2 p1, Vector2 p1) - Returns the angle from p1 to p2.")
+ConsoleFunction( Vector2AngleToPoint, F32, 3, 3, "(Vector2 p1, Vector2 p1) - Returns the angle from p1 to p2.")
 {
     // Check Parameters.
     if (Utility::mGetStringElementCount(argv[1]) < 2 ||Utility::mGetStringElementCount(argv[2]) < 2 )
@@ -240,6 +240,26 @@ ConsoleFunction( t2dAngleToPoint, F32, 3, 3, "(Vector2 p1, Vector2 p1) - Returns
     return mRadToDeg( mAtan((p2.x - p1.x), (p1.y - p2.y)) );
 }
 
+//-----------------------------------------------------------------------------
+// Vector from angle and magnitude.
+//-----------------------------------------------------------------------------
+ConsoleFunction( Vector2Direction, const char*, 3, 3,   "(F32 angle, F32 magnitude) - Calculates a direction from an angle and magnitude.\n"
+                                                        "@param angle The angle of the direction.\n"
+                                                        "@param magnitude The magnitude of the direction.\n"
+                                                        "@return No return value.")
+{
+    // Fetch angle.
+    const F32 angle = mDegToRad(dAtof(argv[1]));
+
+    // Fetch magnitude.
+    const F32 magnitude = dAtof(argv[2]);
+
+    // Do Operation.
+    Vector2 direction;
+    direction.setPolar( angle, magnitude );
+    return direction.scriptThis();
+}
+
 //-----------------------------------------------------------------------------
 // Length of a 2D Vector.
 //-----------------------------------------------------------------------------
@@ -313,7 +333,7 @@ ConsoleFunction( Vector2InverseY, const char*, 2, 2, "(Vector2 v1) - Returns the
 //-----------------------------------------------------------------------------
 // Normalize Rectangle (two 2D Vectors) with relation to each other.
 //-----------------------------------------------------------------------------
-ConsoleFunction( t2dRectNormalize, const char*, 3, 3, "(Vector2 v1, Vector2 v2) - Returns Normalize rectangle of v1 and v2.")
+ConsoleFunction( Vector2AreaNormalize, const char*, 3, 3, "(Vector2 v1, Vector2 v2) - Returns Normalize rectangle of v1 and v2.")
 {
     // Check Parameters.
     if (Utility::mGetStringElementCount(argv[1]) < 2 ||Utility::mGetStringElementCount(argv[2]) < 2 )

+ 32 - 8
engine/source/2d/gui/SceneWindow.cc

@@ -76,6 +76,8 @@ SceneWindow::SceneWindow() :    mpScene(NULL),
                                 mWindowDirty(true),
                                 mRenderLayerMask(MASK_ALL),
                                 mRenderGroupMask(MASK_ALL),
+                                mBackgroundColor( "Black" ),
+                                mUseBackgroundColor(false),   
                                 mCameraInterpolationMode(SIGMOID),
                                 mMaxQueueItems(64),
                                 mCameraTransitionTime(2.0f),
@@ -163,12 +165,16 @@ void SceneWindow::onRemove()
 void SceneWindow::initPersistFields()
 {
     // Call Parent.
-   Parent::initPersistFields();
+    Parent::initPersistFields();
 
-   // Add Fields.
-   addField( "lockMouse",               TypeBool, Offset(mLockMouse, SceneWindow) );
-   addField( "UseWindowInputEvents",    TypeBool, Offset(mUseWindowInputEvents, SceneWindow) );
-   addField( "UseObjectInputEvents",    TypeBool, Offset(mUseObjectInputEvents, SceneWindow) );
+    // Add Fields.
+    addField( "lockMouse",               TypeBool, Offset(mLockMouse, SceneWindow) );
+    addField( "UseWindowInputEvents",    TypeBool, Offset(mUseWindowInputEvents, SceneWindow) );
+    addField( "UseObjectInputEvents",    TypeBool, Offset(mUseObjectInputEvents, SceneWindow) );
+
+    // Background color.
+    addField("UseBackgroundColor", TypeBool, Offset(mUseBackgroundColor, SceneWindow), &writeUseBackgroundColor, "" );
+    addField("BackgroundColor", TypeColorF, Offset(mBackgroundColor, SceneWindow), &writeBackgroundColor, "" );
 }
 
 //-----------------------------------------------------------------------------
@@ -1612,7 +1618,24 @@ void SceneWindow::onRender( Point2I offset, const RectI& updateRect )
         mRenderLayerMask,
         mRenderGroupMask,
         Vector2( mCameraCurrent.mSceneWindowScale ),
-        &debugStats );
+        &debugStats,
+        this );
+
+    // Clear the background color if requested.
+    if ( mUseBackgroundColor )
+    {
+        // Enable the scissor.
+        const RectI& clipRect = dglGetClipRect();
+        glEnable(GL_SCISSOR_TEST );
+        glScissor( clipRect.point.x, Platform::getWindowSize().y - (clipRect.point.y + clipRect.extent.y), clipRect.len_x(), clipRect.len_y() );
+
+        // Clear the background.
+        glClearColor( mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha );
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        // Disable the scissor.
+        glDisable( GL_SCISSOR_TEST );
+    }
 
     // Render View.
     pScene->sceneRender( &sceneRenderState );
@@ -1728,13 +1751,14 @@ void SceneWindow::renderMetricsOverlay( Point2I offset, const RectI& updateRect
 
         // Scene.
         dglDrawText( font, bannerOffset + Point2I(0,(S32)linePositionY), "Scene", NULL );
-        dSprintf( mDebugText, sizeof( mDebugText ), "- Count=%d, Index=%d, Time=%0.1fs, Objects=%d<%d>(Global=%d), Enabled=%d<%d>, Visible=%d<%d>, Awake=%d<%d>",
+        dSprintf( mDebugText, sizeof( mDebugText ), "- Count=%d, Index=%d, Time=%0.1fs, Objects=%d<%d>(Global=%d), Enabled=%d<%d>, Visible=%d<%d>, Awake=%d<%d>, Controllers=%d",
             Scene::getGlobalSceneCount(), pScene->getSceneIndex(),
             pScene->getSceneTime(),
             debugStats.objectsCount, debugStats.maxObjectsCount, SceneObject::getGlobalSceneObjectCount(),
             debugStats.objectsEnabled, debugStats.maxObjectsEnabled,
             debugStats.objectsVisible, debugStats.maxObjectsVisible,
-            debugStats.objectsAwake, debugStats.maxObjectsAwake );        
+            debugStats.objectsAwake, debugStats.maxObjectsAwake,
+            pScene->getControllers() == NULL ? 0 : pScene->getControllers()->size() );        
         dglDrawText( font, bannerOffset + Point2I(metricsOffset,(S32)linePositionY), mDebugText, NULL );
         linePositionY += linePositionOffsetY;
 

+ 12 - 0
engine/source/2d/gui/SceneWindow.h

@@ -94,6 +94,10 @@ private:
     Point2F             mPreTickPosition;
     Point2F             mPostTickPosition;
 
+    /// Background color.
+    ColorF                      mBackgroundColor;
+    bool                        mUseBackgroundColor;
+
     /// Camera Attachment.
     bool                mCameraMounted;
     SceneObject*        mpMountedTo;
@@ -198,6 +202,12 @@ public:
     Vector2 getMousePosition( void );
     void setMousePosition( const Vector2& mousePosition );
 
+    /// Background color.
+    inline void             setBackgroundColor( const ColorF& backgroundColor ) { mBackgroundColor = backgroundColor; }
+    inline const ColorF&    getBackgroundColor( void ) const            { return mBackgroundColor; }
+    inline void             setUseBackgroundColor( const bool useBackgroundColor ) { mUseBackgroundColor = useBackgroundColor; }
+    inline bool             getUseBackgroundColor( void ) const         { return mUseBackgroundColor; }
+
     /// Input.
     void setObjectInputEventFilter( const U32 groupMask, const U32 layerMask, const bool useInvisible = false );
     void setObjectInputEventGroupFilter( const U32 groupMask );
@@ -317,6 +327,8 @@ protected:
     static bool writeLockMouse( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneWindow*>(obj)->mLockMouse == true; }
     static bool writeUseWindowInputEvents( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneWindow*>(obj)->mUseWindowInputEvents == false; }
     static bool writeUseObjectInputEvents( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneWindow*>(obj)->mUseObjectInputEvents == true; }
+    static bool writeBackgroundColor( void* obj, StringTableEntry pFieldName )      { return static_cast<SceneWindow*>(obj)->mUseBackgroundColor == true; }
+    static bool writeUseBackgroundColor( void* obj, StringTableEntry pFieldName )   { return static_cast<SceneWindow*>(obj)->mUseBackgroundColor == true; }
 };
 
 #endif // _SCENE_WINDOW_H_

+ 120 - 1
engine/source/2d/gui/SceneWindow_ScriptBinding.h

@@ -850,7 +850,126 @@ ConsoleMethod(SceneWindow, getRenderGroupMask, S32, 2, 2, "() - Gets the group m
               "@returns The bit mask corresponding to the groups which are to be rendered")
 {
    return object->getRenderGroupMask();
-} 
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneWindow, setBackgroundColor, void, 3, 6,  "(float red, float green, float blue, [float alpha = 1.0]) or ( stockColorName )  - Sets the background color for the scene."
+                                                            "@param red The red value.\n"
+                                                            "@param green The green value.\n"
+                                                            "@param blue The blue value.\n"
+                                                            "@param alpha The alpha value.\n"
+                                                            "@return No return Value.")
+{
+    // The colors.
+    F32 red;
+    F32 green;
+    F32 blue;
+    F32 alpha = 1.0f;
+
+    // Space separated.
+    if (argc == 3)
+    {
+        // Grab the element count.
+        const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+        // Has a single argument been specified?
+        if ( elementCount == 1 )
+        {
+            object->setDataField( StringTable->insert("BackgroundColor"), NULL, argv[2] );
+            return;
+        }
+
+        // ("R G B [A]")
+        if ((elementCount == 3) || (elementCount == 4))
+        {
+            // Extract the color.
+            red   = dAtof(Utility::mGetStringElement(argv[2], 0));
+            green = dAtof(Utility::mGetStringElement(argv[2], 1));
+            blue  = dAtof(Utility::mGetStringElement(argv[2], 2));
+
+            // Grab the alpha if it's there.
+            if (elementCount > 3)
+                alpha = dAtof(Utility::mGetStringElement(argv[2], 3));
+        }
+
+        // Invalid.
+        else
+        {
+            Con::warnf("SceneWindow::setBackgroundColor() - Invalid Number of parameters!");
+            return;
+        }
+    }
+
+    // (R, G, B)
+    else if (argc >= 5)
+    {
+        red   = dAtof(argv[2]);
+        green = dAtof(argv[3]);
+        blue  = dAtof(argv[4]);
+
+        // Grab the alpha if it's there.
+        if (argc > 5)
+            alpha = dAtof(argv[5]);
+    }
+
+    // Invalid.
+    else
+    {
+        Con::warnf("SceneWindow::setBackgroundColor() - Invalid Number of parameters!");
+        return;
+    }
+
+    // Set background color.
+    object->setBackgroundColor(ColorF(red, green, blue, alpha) );
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneWindow, getBackgroundColor, const char*, 2, 2,   "Gets the background color for the scene.\n"
+                                                                    "@return (float red / float green / float blue / float alpha) The background color for the scene.")
+{
+    // Get the background color.
+    const ColorF& color = object->getBackgroundColor();
+
+    // Fetch color name.
+    StringTableEntry colorName = StockColor::name( color );
+
+    // Return the color name if it's valid.
+    if ( colorName != StringTable->EmptyString )
+        return colorName;
+
+    // Create Returnable Buffer.
+    char* pBuffer = Con::getReturnBuffer(64);
+
+    // Format Buffer.
+    dSprintf(pBuffer, 64, "%g %g %g %g", color.red, color.green, color.blue, color.alpha );
+
+    // Return buffer.
+    return pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneWindow, setUseBackgroundColor, void, 3, 3,   "Sets whether to use the scene background color or not.\n"
+                                                                "@param useBackgroundColor Whether to use the scene background color or not.\n"
+                                                                "@return No return value." )
+{
+    // Fetch flag.
+    const bool useBackgroundColor = dAtob(argv[2]);
+
+    // Set the flag.
+    object->setUseBackgroundColor( useBackgroundColor );
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneWindow, getUseBackgroundColor, bool, 2, 2,   "Gets whether the scene background color is in use or not.\n"
+                                                                "@return Whether the scene background color is in use or not." )
+{
+    // Get the flag.
+    return object->getUseBackgroundColor();
+}
 
 //-----------------------------------------------------------------------------
 

+ 2 - 1
engine/source/2d/gui/guiSceneObjectCtrl.cc

@@ -434,7 +434,8 @@ void GuiSceneObjectCtrl::onRender(Point2I offset, const RectI& updateRect)
           MASK_ALL,
           MASK_ALL,
           Vector2::getOne(),
-          &debugStats );
+          &debugStats,
+          this );
 
       SceneRenderRequest guiSceneRenderRequest;
       guiSceneRenderRequest.set(

+ 31 - 31
engine/source/2d/scene/DebugDraw.cc

@@ -28,7 +28,7 @@
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawAABB( const b2AABB& aabb )
+void DebugDraw::DrawAABB( const b2AABB& aabb, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawAABB);
@@ -40,28 +40,28 @@ void DebugDraw::DrawAABB( const b2AABB& aabb )
     aabbVertex[2].Set(aabb.upperBound.x, aabb.upperBound.y);
     aabbVertex[3].Set(aabb.lowerBound.x, aabb.upperBound.y);
 
-    DrawPolygon( aabbVertex, 4, b2Color(0.7f, 0.7f, 0.9f) );
+    DrawPolygon( aabbVertex, 4, color );
 }
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawOOBB( const b2Vec2* pOOBB )
+void DebugDraw::DrawOOBB( const b2Vec2* pOOBB, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawOOBB);
 
-    DrawPolygon( pOOBB, 4, b2Color(0.9f, 0.9f, 1.0f) );
+    DrawPolygon( pOOBB, 4, color );
 }
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawAsleep( const b2Vec2* pOOBB )
+void DebugDraw::DrawAsleep( const b2Vec2* pOOBB, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawAsleep);
 
-    DrawSegment( pOOBB[0], pOOBB[2], b2Color( 0.0f, 1.0f, 0.0f ) );
-    DrawSegment( pOOBB[1], pOOBB[3], b2Color( 0.0f, 1.0f, 0.0f ) );
+    DrawSegment( pOOBB[0], pOOBB[2], color );
+    DrawSegment( pOOBB[1], pOOBB[3], color );
 }
 
 //-----------------------------------------------------------------------------
@@ -77,27 +77,27 @@ void DebugDraw::DrawCollisionShapes( const b2Transform& xf, b2Body* pBody )
         // Inactive fixture.
         if ( pBody->IsActive() == false )
         {
-            DrawShape(pFixture, xf, b2Color(0.5f, 0.5f, 0.5f));
+            DrawShape(pFixture, xf, ColorF(0.5f, 0.5f, 0.5f));
         }
         // Active static fixture.
         else if ( pBody->GetType() == b2_staticBody )
         {
-            DrawShape(pFixture, xf, b2Color(0.5f, 0.9f, 0.5f));
+            DrawShape(pFixture, xf, ColorF(0.5f, 0.9f, 0.5f));
         }
         // Active kinematic fixture.
         else if ( pBody->GetType() == b2_kinematicBody )
         {
-            DrawShape(pFixture, xf, b2Color(0.5f, 0.5f, 0.9f));
+            DrawShape(pFixture, xf, ColorF(0.5f, 0.5f, 0.9f));
         }
         // Active, asleep dynamic fixture.
         else if ( pBody->IsAwake() == false )
         {
-            DrawShape(pFixture, xf, b2Color(0.6f, 0.6f, 0.2f));
+            DrawShape(pFixture, xf, ColorF(0.6f, 0.6f, 0.2f));
         }
         // Active, awake dynamic fixture.
         else
         {
-            DrawShape(pFixture, xf, b2Color(0.9f, 0.9f, 0.2f));
+            DrawShape(pFixture, xf, ColorF(0.9f, 0.9f, 0.2f));
         }
     }
 }
@@ -124,13 +124,13 @@ void DebugDraw::DrawSortPoint( const b2Vec2& worldPosition, const b2Vec2& size,
     worldSortPoint[2].Set( worldPoint.x - markerSize, worldPoint.y + markerSize );
     worldSortPoint[3].Set( worldPoint.x + markerSize, worldPoint.y - markerSize );
 
-    DrawSegment( worldSortPoint[0], worldSortPoint[1], b2Color( 0.0f, 1.0f, 0.8f ) );
-    DrawSegment( worldSortPoint[2], worldSortPoint[3], b2Color( 0.0f, 1.0f, 0.8f ) );
+    DrawSegment( worldSortPoint[0], worldSortPoint[1], ColorF( 0.0f, 1.0f, 0.8f ) );
+    DrawSegment( worldSortPoint[2], worldSortPoint[3], ColorF( 0.0f, 1.0f, 0.8f ) );
 }
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawShape( b2Fixture* fixture, const b2Transform& xf, const b2Color& color )
+void DebugDraw::DrawShape( b2Fixture* fixture, const b2Transform& xf, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawShape);
@@ -216,7 +216,7 @@ void DebugDraw::DrawJoints( b2World* pWorld )
         b2Vec2 p1 = pJoint->GetAnchorA();
         b2Vec2 p2 = pJoint->GetAnchorB();
 
-        b2Color color( 0.5f, 0.8f, 0.8f );
+        ColorF color( 0.5f, 0.8f, 0.8f );
 
         switch ( pJoint->GetType() )
         {
@@ -259,12 +259,12 @@ void DebugDraw::DrawJoints( b2World* pWorld )
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawPolygon( const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
+void DebugDraw::DrawPolygon( const b2Vec2* vertices, int32 vertexCount, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawPolygon);
 
-    glColor3f(color.r, color.g, color.b);
+    glColor3f(color.red, color.green, color.blue);
     glBegin(GL_LINE_LOOP);
     for (int32 i = 0; i < vertexCount; ++i)
     {
@@ -275,14 +275,14 @@ void DebugDraw::DrawPolygon( const b2Vec2* vertices, int32 vertexCount, const b2
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
+void DebugDraw::DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawSolidPolygon);
 
     glEnable(GL_BLEND);
     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glColor4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.15f);
+    glColor4f(0.5f * color.red, 0.5f * color.green, 0.5f * color.blue, 0.15f);
     glBegin(GL_TRIANGLE_FAN);
     for (int32 i = 0; i < vertexCount; ++i)
     {
@@ -291,7 +291,7 @@ void DebugDraw::DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, con
     glEnd();
     glDisable(GL_BLEND);
 
-    glColor4f(color.r, color.g, color.b, 1.0f);
+    glColor4f(color.red, color.green, color.blue, 1.0f);
     glBegin(GL_LINE_LOOP);
     for (int32 i = 0; i < vertexCount; ++i)
     {
@@ -302,7 +302,7 @@ void DebugDraw::DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, con
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawCircle( const b2Vec2& center, float32 radius, const b2Color& color)
+void DebugDraw::DrawCircle( const b2Vec2& center, float32 radius, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawCircle);
@@ -310,7 +310,7 @@ void DebugDraw::DrawCircle( const b2Vec2& center, float32 radius, const b2Color&
     const float32 k_segments = 16.0f;
     const float32 k_increment = 2.0f * b2_pi / k_segments;
     float32 theta = 0.0f;
-    glColor3f(color.r, color.g, color.b);
+    glColor3f(color.red, color.green, color.blue);
     glBegin(GL_LINE_LOOP);
     for (int32 i = 0; i < k_segments; ++i)
     {
@@ -323,7 +323,7 @@ void DebugDraw::DrawCircle( const b2Vec2& center, float32 radius, const b2Color&
     
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawSolidCircle( const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color)
+void DebugDraw::DrawSolidCircle( const b2Vec2& center, float32 radius, const b2Vec2& axis, const ColorF& color )
 {
     // Debug Profiling.
     PROFILE_SCOPE(DebugDraw_DrawSolidCircle);
@@ -333,7 +333,7 @@ void DebugDraw::DrawSolidCircle( const b2Vec2& center, float32 radius, const b2V
     float32 theta = 0.0f;
     glEnable(GL_BLEND);
     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glColor4f(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.15f);
+    glColor4f(0.5f * color.red, 0.5f * color.green, 0.5f * color.blue, 0.15f);
     glBegin(GL_TRIANGLE_FAN);
     for (int32 i = 0; i < k_segments; ++i)
     {
@@ -345,7 +345,7 @@ void DebugDraw::DrawSolidCircle( const b2Vec2& center, float32 radius, const b2V
     glDisable(GL_BLEND);
 
     theta = 0.0f;
-    glColor4f(color.r, color.g, color.b, 1.0f);
+    glColor4f(color.red, color.green, color.blue, 1.0f);
     glBegin(GL_LINE_LOOP);
     for (int32 i = 0; i < k_segments; ++i)
     {
@@ -364,9 +364,9 @@ void DebugDraw::DrawSolidCircle( const b2Vec2& center, float32 radius, const b2V
     
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawSegment( const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
+void DebugDraw::DrawSegment( const b2Vec2& p1, const b2Vec2& p2, const ColorF& color )
 {
-    glColor3f(color.r, color.g, color.b);
+    glColor3f(color.red, color.green, color.blue);
     glBegin(GL_LINES);
     glVertex2f(p1.x, p1.y);
     glVertex2f(p2.x, p2.y);
@@ -375,7 +375,7 @@ void DebugDraw::DrawSegment( const b2Vec2& p1, const b2Vec2& p2, const b2Color&
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawTransform(const b2Transform& xf)
+void DebugDraw::DrawTransform( const b2Transform& xf )
 {
     b2Vec2 p1 = xf.p, p2;
     const float32 k_axisScale = 0.4f;
@@ -396,11 +396,11 @@ void DebugDraw::DrawTransform(const b2Transform& xf)
 
 //-----------------------------------------------------------------------------
 
-void DebugDraw::DrawPoint(const b2Vec2& p, float32 size, const b2Color& color)
+void DebugDraw::DrawPoint( const b2Vec2& p, float32 size, const ColorF& color )
 {
     glPointSize(size);
     glBegin(GL_POINTS);
-    glColor3f(color.r, color.g, color.b);
+    glColor3f(color.red, color.green, color.blue);
     glVertex2f(p.x, p.y);
     glEnd();
     glPointSize(1.0f);

+ 14 - 10
engine/source/2d/scene/DebugDraw.h

@@ -31,6 +31,10 @@
 #include "math/mMath.h"
 #endif
 
+#ifndef _COLOR_H_
+#include "graphics/color.h"
+#endif
+
 //-----------------------------------------------------------------------------
 
 class DebugDraw
@@ -39,22 +43,22 @@ public:
     DebugDraw() {}
     virtual ~DebugDraw() {}
 
-    void DrawAABB( const b2AABB& aabb );
-    void DrawOOBB( const b2Vec2* pOOBB );
-    void DrawAsleep( const b2Vec2* pOOBB );
+    void DrawAABB( const b2AABB& aabb, const ColorF& color );
+    void DrawOOBB( const b2Vec2* pOOBB, const ColorF& color );
+    void DrawAsleep( const b2Vec2* pOOBB, const ColorF& color );
     void DrawCollisionShapes( const b2Transform& xf, b2Body* pBody );
     void DrawSortPoint( const b2Vec2& worldPosition, const b2Vec2& size, const b2Vec2& localSortPoint );
 
     void DrawJoints( b2World* pWorld );
 
-    void DrawShape( b2Fixture* fixture, const b2Transform& xf, const b2Color& color );
-    void DrawPolygon( const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
-    void DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
-    void DrawCircle( const b2Vec2& center, float32 radius, const b2Color& color);
-    void DrawSolidCircle( const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color);
-    void DrawSegment( const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);
+    void DrawShape( b2Fixture* fixture, const b2Transform& xf, const ColorF& color );
+    void DrawPolygon( const b2Vec2* vertices, int32 vertexCount, const ColorF& color);
+    void DrawSolidPolygon( const b2Vec2* vertices, int32 vertexCount, const ColorF& color);
+    void DrawCircle( const b2Vec2& center, float32 radius, const ColorF& color);
+    void DrawSolidCircle( const b2Vec2& center, float32 radius, const b2Vec2& axis, const ColorF& color);
+    void DrawSegment( const b2Vec2& p1, const b2Vec2& p2, const ColorF& color);
     void DrawTransform(const b2Transform& xf);
-    void DrawPoint(const b2Vec2& p, float32 size, const b2Color& color);
+    void DrawPoint(const b2Vec2& p, float32 size, const ColorF& color);
 };
 
 #endif // _DEBUG_DRAW_H_

+ 12 - 0
engine/source/2d/scene/DebugStats.h

@@ -23,10 +23,22 @@
 #ifndef _DEBUG_STATS_H_
 #define _DEBUG_STATS_H_
 
+#ifndef _PROFILER_H_
+#include "debug/profiler.h"
+#endif
+
 #ifndef _TORQUE_TYPES_H_
 #include "platform\types.h"
 #endif
 
+#ifndef _PLATFORM_MEMORY_H_
+#include "platform/platformMemory.h"
+#endif
+
+#ifndef BOX2D_H
+#include "box2d/Box2D.h"
+#endif
+
 //-----------------------------------------------------------------------------
 
 class DebugStats

Datei-Diff unterdrückt, da er zu groß ist
+ 1374 - 1020
engine/source/2d/scene/Scene.cc


+ 23 - 47
engine/source/2d/scene/Scene.h

@@ -23,10 +23,6 @@
 #ifndef _SCENE_H_
 #define _SCENE_H_
 
-#ifndef _VECTOR_H_
-#include "collection/vector.h"
-#endif
-
 #ifndef _MMATH_H_
 #include "math/mMath.h"
 #endif
@@ -35,10 +31,6 @@
 #include "2d/core/Vector2.h"
 #endif
 
-#ifndef _FILESTREAM_H_
-#include "io/fileStream.h"
-#endif
-
 #ifndef _NETOBJECT_H_
 #include "network/netObject.h"
 #endif
@@ -47,10 +39,6 @@
 #include "platform/Tickable.h"
 #endif
 
-#ifndef _DEBUG_STATS_H_
-#include "2d/scene/DebugStats.h"
-#endif
-
 #ifndef _PHYSICS_PROXY_H_
 #include "2d/scene/PhysicsProxy.h"
 #endif
@@ -59,10 +47,6 @@
 #include "2d/scene/WorldQuery.h"
 #endif
 
-#ifndef BOX2D_H
-#include "box2d\Box2D.h"
-#endif
-
 #ifndef _DEBUG_DRAW_H_
 #include "2d/scene/DebugDraw.h"
 #endif
@@ -169,14 +153,6 @@ struct TickContact
 
 ///-----------------------------------------------------------------------------
 
-typedef HashMap<U32, b2Joint*>              typeJointHash;
-typedef HashMap<U32, U32>                   typeReverseJointHash;
-typedef Vector<tDeleteRequest>              typeDeleteVector;
-typedef Vector<TickContact>                 typeContactVector;
-typedef HashMap<b2Contact*, TickContact>    typeContactHash;
-
-///-----------------------------------------------------------------------------
-
 class Scene :
     public BehaviorComponent,
     public TamlChildren,
@@ -186,6 +162,12 @@ class Scene :
     public virtual Tickable
 {
 public:
+    typedef HashMap<U32, b2Joint*>              typeJointHash;
+    typedef HashMap<U32, U32>                   typeReverseJointHash;
+    typedef Vector<tDeleteRequest>              typeDeleteVector;
+    typedef Vector<TickContact>                 typeContactVector;
+    typedef HashMap<b2Contact*, TickContact>    typeContactHash;
+
     /// Scene Debug Options.
     enum DebugOption
     {
@@ -193,15 +175,16 @@ public:
         ///
         SCENE_DEBUG_METRICS            = BIT(0),
         SCENE_DEBUG_FPS_METRICS        = BIT(1),
-        SCENE_DEBUG_JOINTS             = BIT(2),
-        SCENE_DEBUG_WIREFRAME_RENDER   = BIT(3),
+        SCENE_DEBUG_CONTROLLERS        = BIT(2), 
+        SCENE_DEBUG_JOINTS             = BIT(3),
+        SCENE_DEBUG_WIREFRAME_RENDER   = BIT(4),
         ///
-        SCENE_DEBUG_AABB               = BIT(4),
-        SCENE_DEBUG_OOBB               = BIT(5),
-        SCENE_DEBUG_SLEEP              = BIT(6),
-        SCENE_DEBUG_COLLISION_SHAPES   = BIT(7),
-        SCENE_DEBUG_POSITION_AND_COM   = BIT(8),
-        SCENE_DEBUG_SORT_POINTS        = BIT(9),
+        SCENE_DEBUG_AABB               = BIT(16),
+        SCENE_DEBUG_OOBB               = BIT(17),
+        SCENE_DEBUG_SLEEP              = BIT(18),
+        SCENE_DEBUG_COLLISION_SHAPES   = BIT(19),
+        SCENE_DEBUG_POSITION_AND_COM   = BIT(20),
+        SCENE_DEBUG_SORT_POINTS        = BIT(21),
     };
 
     /// Pick mode.
@@ -238,6 +221,9 @@ private:
     typeReverseJointHash        mReverseJoints;
     U32                         mJointMasterId;
 
+	/// Scene controllers.
+	SimObjectPtr<SimSet>	    mControllers;
+
     /// Scene time.
     F32                         mSceneTime;
     bool                        mScenePause;
@@ -256,10 +242,6 @@ private:
     /// Window rendering.
     SceneWindow*                mpCurrentRenderWindow;
 
-    /// Background color.
-    ColorF                      mBackgroundColor;
-    bool                        mUseBackgroundColor;
-
     /// Window attachments.
     SimSet                      mAttachedSceneWindows;
 
@@ -305,9 +287,9 @@ private:
 protected:
     /// Taml callbacks.
     virtual void            onTamlPreRead( void );
-    virtual void            onTamlPostRead( const TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void            onTamlPostRead( const TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     Scene();
@@ -348,12 +330,6 @@ public:
     inline void             setPositionIterations( const S32 iterations ) { mPositionIterations = iterations; }
     inline S32              getPositionIterations( void ) const         { return mPositionIterations; }
 
-    /// Background color.
-    inline void             setBackgroundColor( const ColorF& backgroundColor ) { mBackgroundColor = backgroundColor; }
-    inline const ColorF&    getBackgroundColor( void ) const            { return mBackgroundColor; }
-    inline void             setUseBackgroundColor( const bool useBackgroundColor ) { mUseBackgroundColor = useBackgroundColor; }
-    inline bool             getUseBackgroundColor( void ) const         { return mUseBackgroundColor; }
-
     /// Scene occupancy.
     void                    clearScene( bool deleteObjects = true );
     void                    addToScene( SceneObject* pSceneObject );
@@ -367,6 +343,8 @@ public:
 
     void                    mergeScene( const Scene* pScene );
 
+	inline SimSet*			getControllers( void )						{ return mControllers; }
+
     /// Scene time.
     inline F32              getSceneTime( void ) const                  { return mSceneTime; };
     inline void             setScenePause( bool status )                { mScenePause = status; }
@@ -688,8 +666,6 @@ protected:
     static bool writeGravity( void* obj, StringTableEntry pFieldName )              { return Vector2(static_cast<Scene*>(obj)->getGravity()).notEqual( Vector2::getZero() ); }
     static bool writeVelocityIterations( void* obj, StringTableEntry pFieldName )   { return static_cast<Scene*>(obj)->getVelocityIterations() != 8; }
     static bool writePositionIterations( void* obj, StringTableEntry pFieldName )   { return static_cast<Scene*>(obj)->getPositionIterations() != 3; }
-    static bool writeBackgroundColor( void* obj, StringTableEntry pFieldName )      { return static_cast<Scene*>(obj)->mUseBackgroundColor; }
-    static bool writeUseBackgroundColor( void* obj, StringTableEntry pFieldName )   { return static_cast<Scene*>(obj)->mUseBackgroundColor; }
 
     static bool writeLayerSortMode( void* obj, StringTableEntry pFieldName )
     {

+ 10 - 1
engine/source/2d/scene/SceneRenderState.h

@@ -23,8 +23,13 @@
 #ifndef _SCENE_RENDER_STATE_H_
 #define _SCENE_RENDER_STATE_H_
 
+#ifndef _VECTOR2_H_
+#include "2d/core/vector2.h"
+#endif
+
 //-----------------------------------------------------------------------------
 
+class GuiControl;
 class RectF;
 class DebugStats;
 struct b2AABB;
@@ -40,7 +45,8 @@ struct SceneRenderState
         U32 renderLayerMask,
         U32 renderGroupMask,
         const Vector2& renderScale,
-        DebugStats* pDebugStats )
+        DebugStats* pDebugStats,
+        GuiControl* pGuiControl )
     {
         mRenderArea       = renderArea;
         mRenderAABB       = CoreMath::mRectFtoAABB( renderArea );
@@ -50,6 +56,7 @@ struct SceneRenderState
         mRenderLayerMask  = renderLayerMask;
         mRenderGroupMask  = renderGroupMask;
         mpDebugStats      = pDebugStats;
+        mpGuiControl      = pGuiControl;
     }
 
     RectF           mRenderArea;
@@ -60,6 +67,8 @@ struct SceneRenderState
     U32             mRenderGroupMask;
     Vector2         mRenderScale;
     DebugStats*     mpDebugStats;
+    GuiControl*     mpGuiControl;
+
 
 };
 

+ 31 - 128
engine/source/2d/scene/Scene_ScriptBinding.h

@@ -100,125 +100,6 @@ ConsoleMethod(Scene, getPositionIterations, S32, 2, 2,  "() Gets the number of p
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(Scene, setBackgroundColor, void, 3, 6,   "(float red, float green, float blue, [float alpha = 1.0]) or ( stockColorName )  - Sets the background color for the scene."
-                                                        "@param red The red value.\n"
-                                                        "@param green The green value.\n"
-                                                        "@param blue The blue value.\n"
-                                                        "@param alpha The alpha value.\n"
-                                                        "@return No return Value.")
-{
-    // The colors.
-    F32 red;
-    F32 green;
-    F32 blue;
-    F32 alpha = 1.0f;
-
-    // Space separated.
-    if (argc == 3)
-    {
-        // Grab the element count.
-        const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
-
-        // Has a single argument been specified?
-        if ( elementCount == 1 )
-        {
-            object->setDataField( StringTable->insert("BackgroundColor"), NULL, argv[2] );
-            return;
-        }
-
-        // ("R G B [A]")
-        if ((elementCount == 3) || (elementCount == 4))
-        {
-            // Extract the color.
-            red   = dAtof(Utility::mGetStringElement(argv[2], 0));
-            green = dAtof(Utility::mGetStringElement(argv[2], 1));
-            blue  = dAtof(Utility::mGetStringElement(argv[2], 2));
-
-            // Grab the alpha if it's there.
-            if (elementCount > 3)
-                alpha = dAtof(Utility::mGetStringElement(argv[2], 3));
-        }
-
-        // Invalid.
-        else
-        {
-            Con::warnf("Scene::setBackgroundColor() - Invalid Number of parameters!");
-            return;
-        }
-    }
-
-    // (R, G, B)
-    else if (argc >= 5)
-    {
-        red   = dAtof(argv[2]);
-        green = dAtof(argv[3]);
-        blue  = dAtof(argv[4]);
-
-        // Grab the alpha if it's there.
-        if (argc > 5)
-            alpha = dAtof(argv[5]);
-    }
-
-    // Invalid.
-    else
-    {
-        Con::warnf("Scene::setBackgroundColor() - Invalid Number of parameters!");
-        return;
-    }
-
-    // Set background color.
-    object->setBackgroundColor(ColorF(red, green, blue, alpha) );
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(Scene, getBackgroundColor, const char*, 2, 2, "Gets the background color for the scene.\n"
-                                                                "@return (float red / float green / float blue / float alpha) The background color for the scene.")
-{
-    // Get the background color.
-    const ColorF& color = object->getBackgroundColor();
-
-    // Fetch color name.
-    StringTableEntry colorName = StockColor::name( color );
-
-    // Return the color name if it's valid.
-    if ( colorName != StringTable->EmptyString )
-        return colorName;
-
-    // Create Returnable Buffer.
-    char* pBuffer = Con::getReturnBuffer(64);
-
-    // Format Buffer.
-    dSprintf(pBuffer, 64, "%g %g %g %g", color.red, color.green, color.blue, color.alpha );
-
-    // Return buffer.
-    return pBuffer;
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(Scene, setUseBackgroundColor, void, 3, 3, "Sets whether to use the scene background color or not.\n"
-                                                        "@param useBackgroundColor Whether to use the scene background color or not.\n"
-                                                        "@return No return value." )
-{
-    // Fetch flag.
-    const bool useBackgroundColor = dAtob(argv[2]);
-
-    // Set the flag.
-    object->setUseBackgroundColor( useBackgroundColor );
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(Scene, getUseBackgroundColor, bool, 2, 2, "Gets whether the scene background color is in use or not.\n"
-                                                        "@return Whether the scene background color is in use or not." )
-{
-    // Get the flag.
-    return object->getUseBackgroundColor();
-}
-
-//-----------------------------------------------------------------------------
-
 ConsoleMethod(Scene, add, void, 3, 3,   "(sceneObject) Add the SceneObject to the scene.\n"
                                         "@param sceneObject The SceneObject to add to the scene.\n"
                                         "@return No return value.")
@@ -387,6 +268,17 @@ ConsoleMethod(Scene, mergeScene, void, 3, 3,    "(scene) Merges the specified sc
 
 //-----------------------------------------------------------------------------
 
+ConsoleMethod(Scene, getControllers, const char*, 2, 2,	"() Gets the Scene Controllers.\n"
+														"@return Gets the scene controllers.")
+{
+    // Fetch the scene controllers.
+    SimSet* pControllerSet = object->getControllers();
+
+    return ( pControllerSet == NULL ) ? StringTable->EmptyString : pControllerSet->getIdString();
+}
+
+//-----------------------------------------------------------------------------
+
 ConsoleMethod(Scene, getSceneTime, F32, 2, 2,   "() Gets the Scene Time.\n"
                                                         "@return Returns the time as a floating point number\n")
 {
@@ -2456,8 +2348,11 @@ ConsoleMethod(Scene, pickArea, const char*, 4, 9, "(startx/y, endx/y, [sceneGrou
         AssertFatal( false, "Unsupported pick mode." );
     }
 
+    // Fetch result count.
+    const U32 resultCount = pWorldQuery->getQueryResultsCount();
+
     // Finish if no results.
-    if ( pWorldQuery->getQueryResultsCount() == 0 )
+    if ( resultCount == 0 )
         return NULL;
 
     // Fetch results.
@@ -2473,7 +2368,7 @@ ConsoleMethod(Scene, pickArea, const char*, 4, 9, "(startx/y, endx/y, [sceneGrou
     U32 bufferCount = 0;
 
     // Add picked objects.
-    for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
+    for ( U32 n = 0; n < resultCount; n++ )
     {
         // Output Object ID.
         bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSceneObject->getId() );
@@ -2598,8 +2493,11 @@ ConsoleMethod(Scene, pickRay, const char*, 4, 9, "(startx/y, endx/y, [sceneGroup
     // Sanity!
     AssertFatal( pWorldQuery->getIsRaycastQueryResult(), "Invalid non-ray-cast query result returned." );
 
+    // Fetch result count.
+    const U32 resultCount = pWorldQuery->getQueryResultsCount();
+
     // Finish if no results.
-    if ( pWorldQuery->getQueryResultsCount() == 0 )
+    if ( resultCount == 0 )
         return NULL;
 
     // Sort ray-cast result.
@@ -2618,7 +2516,7 @@ ConsoleMethod(Scene, pickRay, const char*, 4, 9, "(startx/y, endx/y, [sceneGroup
     U32 bufferCount = 0;
 
     // Add Picked Objects to List.
-    for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
+    for ( U32 n = 0; n < resultCount; n++ )
     {
         // Output Object ID.
         bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSceneObject->getId() );
@@ -2716,9 +2614,11 @@ ConsoleMethod(Scene, pickRayCollision, const char*, 4, 8, "(startx/y, endx/y, [s
     // Sanity!
     AssertFatal( pWorldQuery->getIsRaycastQueryResult(), "Invalid non-ray-cast query result returned." );
 
+    // Fetch result count.
+    const U32 resultCount = pWorldQuery->getQueryResultsCount();
+
     // Finish if no results.
-    const U32 queryResultCount = pWorldQuery->getQueryResultsCount();
-    if ( queryResultCount == 0 )
+    if ( resultCount == 0 )
         return NULL;
 
     // Sort ray-cast result.
@@ -2737,7 +2637,7 @@ ConsoleMethod(Scene, pickRayCollision, const char*, 4, 8, "(startx/y, endx/y, [s
     U32 bufferCount = 0;
 
     // Add Picked Objects to List.
-    for ( U32 n = 0; n < queryResultCount; n++ )
+    for ( U32 n = 0; n < resultCount; n++ )
     {
         // Fetch query result.
         const WorldQueryResult& queryResult = queryResults[n];
@@ -2852,8 +2752,11 @@ ConsoleMethod(Scene, pickPoint, const char*, 3, 7, "(x / y, [sceneGroupMask], [s
         AssertFatal( false, "Unsupported pick mode." );
     }
 
+    // Fetch result count.
+    const U32 resultCount = pWorldQuery->getQueryResultsCount();
+
     // Finish if no results.
-    if ( pWorldQuery->getQueryResultsCount() == 0 )
+    if ( resultCount == 0 )
         return NULL;
 
     // Fetch results.
@@ -2869,7 +2772,7 @@ ConsoleMethod(Scene, pickPoint, const char*, 3, 7, "(x / y, [sceneGroupMask], [s
     U32 bufferCount = 0;
 
     // Add Picked Objects to List.
-    for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
+    for ( U32 n = 0; n < resultCount; n++ )
     {
         // Output Object ID.
         bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSceneObject->getId() );

+ 9 - 8
engine/source/2d/scene/WorldQuery.cc

@@ -422,7 +422,7 @@ bool WorldQuery::ReportFixture( b2Fixture* fixture )
         return true;
 
     // Picking allowed filter.
-    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getIsPickingAllowed() )
+    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getPickingAllowed() )
         return true;
 
     // Check fixture point.
@@ -475,7 +475,7 @@ F32 WorldQuery::ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2
         return 1.0f;
 
     // Picking allowed filter.
-    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getIsPickingAllowed() )
+    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getPickingAllowed() )
         return 1.0f;
 
     // Tag with world query key.
@@ -530,7 +530,7 @@ bool WorldQuery::QueryCallback( S32 proxyId )
         return true;
 
     // Picking allowed filter.
-    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getIsPickingAllowed() )
+    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getPickingAllowed() )
         return true;
 
     // Tag with world query key.
@@ -545,7 +545,7 @@ bool WorldQuery::QueryCallback( S32 proxyId )
     {
         WorldQueryResult queryResult( pSceneObject );
         mLayeredQueryResults[pSceneObject->getSceneLayer()].push_back( queryResult );
-        mQueryResults.push_back( pSceneObject );
+        mQueryResults.push_back( queryResult );
     }
 
     return true;
@@ -579,7 +579,7 @@ F32 WorldQuery::RayCastCallback( const b2RayCastInput& input, S32 proxyId )
         return 1.0f;
 
     // Picking allowed filter.
-    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getIsPickingAllowed() )
+    if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getPickingAllowed() )
         return 1.0f;
 
     // Tag with world query key.
@@ -633,7 +633,7 @@ void WorldQuery::injectAlwaysInScope( void )
             continue;
 
         // Picking allowed filter.
-        if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getIsPickingAllowed() )
+        if ( mQueryFilter.mPickingAllowedFilter && !pSceneObject->getPickingAllowed() )
             continue;
 
         // Fetch layer and group masks.
@@ -643,8 +643,9 @@ void WorldQuery::injectAlwaysInScope( void )
         // Compare masks and report.
         if ( (mQueryFilter.mSceneLayerMask & sceneLayerMask) != 0 && (mQueryFilter.mSceneGroupMask & sceneGroupMask) != 0 )
         {
-            mLayeredQueryResults[pSceneObject->getSceneLayer()].push_back( pSceneObject );
-            mQueryResults.push_back( pSceneObject );
+            WorldQueryResult queryResult( pSceneObject );
+            mLayeredQueryResults[pSceneObject->getSceneLayer()].push_back( queryResult );
+            mQueryResults.push_back( queryResult );
         }
     }
 }

+ 16 - 16
engine/source/2d/sceneobject/CompositeSprite.cc

@@ -24,8 +24,8 @@
 #include "2d/sceneobject/CompositeSprite.h"
 #endif
 
-#ifndef _SPRITE_BATCH_ITEM_H_
-#include "2d/core/SpriteBatchItem.h"
+#ifndef _SPRITE_BATCH_QUERY_H_
+#include "2d/core/spriteBatchQuery.h"
 #endif
 
 #ifndef _RENDER_PROXY_H_
@@ -381,10 +381,10 @@ SpriteBatchItem* CompositeSprite::createCustomLayout( const SpriteBatchItem::Log
 
 //-----------------------------------------------------------------------------
 
-void CompositeSprite::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void CompositeSprite::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch sprite count.
     const U32 spriteCount = getSpriteCount();
@@ -393,27 +393,27 @@ void CompositeSprite::onTamlCustomWrite( TamlCustomProperties& customProperties
     if ( spriteCount == 0 )
         return;
 
-    // Add sprites property.
-    TamlCustomProperty* pSpritesProperty = customProperties.addProperty( StringTable->insert("Sprites") );
+    // Add sprites node.
+    TamlCustomNode* pSpritesNode = customNodes.addNode( StringTable->insert("Sprites") );
 
-    // Write property with sprite batch.
-    SpriteBatch::onTamlCustomWrite( pSpritesProperty );
+    // Write node with sprite batch.
+    SpriteBatch::onTamlCustomWrite( pSpritesNode );
 }
 
 //-----------------------------------------------------------------------------
 
-void CompositeSprite::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void CompositeSprite::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find sprites custom property.
-    const TamlCustomProperty* pSpritesProperty = customProperties.findProperty( StringTable->insert("Sprites") );
+    // Find sprites custom node.
+    const TamlCustomNode* pSpritesNode = customNodes.findNode( StringTable->insert("Sprites") );
 
-    // Finish if we don't have the property.
-    if ( pSpritesProperty == NULL )
+    // Finish if we don't have the node.
+    if ( pSpritesNode == NULL )
         return;
 
-    // Read property with sprite batch.
-    SpriteBatch::onTamlCustomRead( pSpritesProperty );
+    // Read node with sprite batch.
+    SpriteBatch::onTamlCustomRead( pSpritesNode );
 }

+ 2 - 2
engine/source/2d/sceneobject/CompositeSprite.h

@@ -85,8 +85,8 @@ protected:
     virtual SpriteBatchItem* createSpriteIsometricLayout( const SpriteBatchItem::LogicalPosition& logicalPosition );
     virtual SpriteBatchItem* createCustomLayout( const SpriteBatchItem::LogicalPosition& logicalPosition );
 
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 protected:
     static bool         setDefaultSpriteAngle(void* obj, const char* data)                  { STATIC_VOID_CAST_TO(CompositeSprite, SpriteBatch, obj)->setDefaultSpriteAngle(mDegToRad(dAtof(data))); return false; }

+ 342 - 0
engine/source/2d/sceneobject/CompositeSprite_ScriptBinding.h

@@ -852,3 +852,345 @@ ConsoleMethod(CompositeSprite, getSpriteName, const char*, 2, 2,    "() - Gets t
     return object->getSpriteName();
 }
 
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(CompositeSprite, pickPoint, const char*, 3, 4,    "(x / y ) Picks sprites intersecting the specified point with optional group/layer masks.\n"
+                                                                "@param x/y The coordinate of the point as either (\"x y\") or (x,y)\n"
+                                                                "@return Returns list of sprite Ids.")
+{
+    // Fetch sprite batch query and clear results.
+    SpriteBatchQuery* pSpriteBatchQuery = object->getSpriteBatchQuery( true );
+
+    // Is the sprite batch query available?
+    if ( pSpriteBatchQuery == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "CompositeSprite::pickPoint() - Cannot pick sprites if clipping mode is off." );
+
+        // Return nothing.
+        return NULL;
+    }
+
+    // The point.
+    Vector2 point;
+
+    // The index of the first optional parameter.
+    U32 firstArg;
+
+    // Grab the number of elements in the first parameter.
+    U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // ("x y")
+    if ((elementCount == 2) && (argc < 8))
+    {
+        point = Utility::mGetStringElementVector(argv[2]);
+        firstArg = 3;
+    }
+   
+    // (x, y)
+    else if ((elementCount == 1) && (argc > 3))
+    {
+        point = Vector2(dAtof(argv[2]), dAtof(argv[3]));
+        firstArg = 4;
+    }
+   
+    // Invalid
+    else
+    {
+        Con::warnf("CompositeSprite::pickPoint() - Invalid number of parameters!");
+        return NULL;
+    }
+
+    // Fetch the render transform.
+    const b2Transform& renderTransform = object->getRenderTransform();
+
+    // Transform into local space.
+    point = b2MulT( renderTransform, point );
+
+    // Perform query.
+    pSpriteBatchQuery->renderQueryPoint( point );
+
+    // Fetch result count.
+    const U32 resultCount = pSpriteBatchQuery->getQueryResultsCount();
+
+    // Finish if no results.
+    if (resultCount == 0 )
+        return NULL;
+
+    // Fetch results.
+    typeSpriteBatchQueryResultVector& queryResults = pSpriteBatchQuery->getQueryResults();
+
+    // Set Max Buffer Size.
+    const U32 maxBufferSize = 4096;
+
+    // Create Returnable Buffer.
+    char* pBuffer = Con::getReturnBuffer(maxBufferSize);
+
+    // Set Buffer Counter.
+    U32 bufferCount = 0;
+
+    // Add picked sprites.
+    for ( U32 n = 0; n < resultCount; n++ )
+    {
+        // Output Object ID.
+        bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSpriteBatchItem->getBatchId() );
+
+        // Finish early if we run out of buffer space.
+        if ( bufferCount >= maxBufferSize )
+        {
+            // Warn.
+            Con::warnf("CompositeSprite::pickPoint() - Too many items picked to return to scripts!");
+            break;
+        }
+    }
+
+    // Clear sprite batch query.
+    pSpriteBatchQuery->clearQuery();
+
+    // Return buffer.
+    return pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(CompositeSprite, pickArea, const char*, 4, 6, "(startx/y, endx/y ) Picks sprites intersecting the specified area with optional group/layer masks.\n"
+                                                            "@param startx/y The coordinates of the start point as either (\"x y\") or (x,y)\n"
+                                                            "@param endx/y The coordinates of the end point as either (\"x y\") or (x,y)\n"
+                                                            "@return Returns list of sprite Ids.")
+{
+    // Fetch sprite batch query and clear results.
+    SpriteBatchQuery* pSpriteBatchQuery = object->getSpriteBatchQuery( true );
+
+    // Is the sprite batch query available?
+    if ( pSpriteBatchQuery == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "CompositeSprite::pickArea() - Cannot pick sprites if clipping mode is off." );
+
+        // Return nothing.
+        return NULL;
+    }
+
+    // Upper left and lower right bound.
+    Vector2 v1, v2;
+
+    // The index of the first optional parameter.
+    U32 firstArg;
+
+    // Grab the number of elements in the first two parameters.
+    U32 elementCount1 = Utility::mGetStringElementCount(argv[2]);
+    U32 elementCount2 = 1;
+    if (argc > 3)
+        elementCount2 = Utility::mGetStringElementCount(argv[3]);
+
+    // ("x1 y1 x2 y2")
+    if ((elementCount1 == 4) && (argc < 9))
+    {
+        v1 = Utility::mGetStringElementVector(argv[2]);
+        v2 = Utility::mGetStringElementVector(argv[2], 2);
+        firstArg = 3;
+    }
+   
+    // ("x1 y1", "x2 y2")
+    else if ((elementCount1 == 2) && (elementCount2 == 2) && (argc > 3) && (argc < 10))
+    {
+        v1 = Utility::mGetStringElementVector(argv[2]);
+        v2 = Utility::mGetStringElementVector(argv[3]);
+        firstArg = 4;
+    }
+   
+    // (x1, y1, x2, y2)
+    else if (argc > 5)
+    {
+        v1 = Vector2(dAtof(argv[2]), dAtof(argv[3]));
+        v2 = Vector2(dAtof(argv[4]), dAtof(argv[5]));
+        firstArg = 6;
+    }
+   
+    // Invalid
+    else
+    {
+        Con::warnf("CompositeSprite::pickArea() - Invalid number of parameters!");
+        return NULL;
+    }
+
+    // Fetch the render transform.
+    const b2Transform& renderTransform = object->getRenderTransform();
+    
+    // Translate into local space.
+    v1 -= renderTransform.p;
+    v2 -= renderTransform.p;
+
+    // Calculate normalized AABB.
+    b2AABB aabb;
+    aabb.lowerBound.x = getMin( v1.x, v2.x );
+    aabb.lowerBound.y = getMin( v1.y, v2.y );
+    aabb.upperBound.x = getMax( v1.x, v2.x );
+    aabb.upperBound.y = getMax( v1.y, v2.y );
+
+    // Rotate the AABB into local space.
+    CoreMath::mRotateAABB( aabb, -renderTransform.q.GetAngle(), aabb );
+
+    // Perform query.
+    pSpriteBatchQuery->renderQueryArea( aabb );
+
+    // Fetch result count.
+    const U32 resultCount = pSpriteBatchQuery->getQueryResultsCount();
+
+    // Finish if no results.
+    if (resultCount == 0 )
+        return NULL;
+
+    // Fetch results.
+    typeSpriteBatchQueryResultVector& queryResults = pSpriteBatchQuery->getQueryResults();
+
+    // Set Max Buffer Size.
+    const U32 maxBufferSize = 4096;
+
+    // Create Returnable Buffer.
+    char* pBuffer = Con::getReturnBuffer(maxBufferSize);
+
+    // Set Buffer Counter.
+    U32 bufferCount = 0;
+
+    // Add picked objects.
+    for ( U32 n = 0; n < resultCount; n++ )
+    {
+        // Output Object ID.
+        bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSpriteBatchItem->getBatchId() );
+
+        // Finish early if we run out of buffer space.
+        if ( bufferCount >= maxBufferSize )
+        {
+            // Warn.
+            Con::warnf("CompositeSprite::pickArea() - Too many items picked to return to scripts!");
+            break;
+        }
+    }
+
+    // Clear sprite batch query.
+    pSpriteBatchQuery->clearQuery();
+
+    // Return buffer.
+    return pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(CompositeSprite, pickRay, const char*, 4, 6,  "(startx/y, endx/y) Picks sprites intersecting the specified ray with optional group/layer masks.\n"
+                                                            "@param startx/y The coordinates of the start point as either (\"x y\") or (x,y)\n"
+                                                            "@param endx/y The coordinates of the end point as either (\"x y\") or (x,y)\n"
+                                                            "@return Returns list of sprite Ids")
+{
+    // Fetch sprite batch query and clear results.
+    SpriteBatchQuery* pSpriteBatchQuery = object->getSpriteBatchQuery( true );
+
+    // Is the sprite batch query available?
+    if ( pSpriteBatchQuery == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "CompositeSprite::pickRay() - Cannot pick sprites if clipping mode is off." );
+
+        // Return nothing.
+        return NULL;
+    }
+
+    // Upper left and lower right bound.
+    Vector2 v1, v2;
+
+    // The index of the first optional parameter.
+    U32 firstArg;
+
+    // Grab the number of elements in the first two parameters.
+    U32 elementCount1 = Utility::mGetStringElementCount(argv[2]);
+    U32 elementCount2 = 1;
+    if (argc > 3)
+        elementCount2 = Utility::mGetStringElementCount(argv[3]);
+
+    // ("x1 y1 x2 y2")
+    if ((elementCount1 == 4) && (argc < 9))
+    {
+        v1 = Utility::mGetStringElementVector(argv[2]);
+        v2 = Utility::mGetStringElementVector(argv[2], 2);
+        firstArg = 3;
+    }
+   
+    // ("x1 y1", "x2 y2")
+    else if ((elementCount1 == 2) && (elementCount2 == 2) && (argc > 3) && (argc < 10))
+    {
+        v1 = Utility::mGetStringElementVector(argv[2]);
+        v2 = Utility::mGetStringElementVector(argv[3]);
+        firstArg = 4;
+    }
+   
+    // (x1, y1, x2, y2)
+    else if (argc > 5)
+    {
+        v1 = Vector2(dAtof(argv[2]), dAtof(argv[3]));
+        v2 = Vector2(dAtof(argv[4]), dAtof(argv[5]));
+        firstArg = 6;
+    }
+   
+    // Invalid
+    else
+    {
+        Con::warnf("CompositeSprite::pickRay() - Invalid number of parameters!");
+        return NULL;
+    }
+
+    // Fetch the render transform.
+    const b2Transform& renderTransform = object->getRenderTransform();
+
+    // Transform into local space.
+    v1 = b2MulT( renderTransform, v1 );
+    v2 = b2MulT( renderTransform, v2 );
+
+    // Perform query.
+    pSpriteBatchQuery->renderQueryRay( v1, v2 );
+
+    // Sanity!
+    AssertFatal( pSpriteBatchQuery->getIsRaycastQueryResult(), "Invalid non-ray-cast query result returned." );
+
+    // Fetch result count.
+    const U32 resultCount = pSpriteBatchQuery->getQueryResultsCount();
+
+    // Finish if no results.
+    if (resultCount == 0 )
+        return NULL;
+
+    // Sort ray-cast result.
+    pSpriteBatchQuery->sortRaycastQueryResult();
+
+    // Fetch results.
+    typeSpriteBatchQueryResultVector& queryResults = pSpriteBatchQuery->getQueryResults();
+
+    // Set Max Buffer Size.
+    const U32 maxBufferSize = 4096;
+
+    // Create Returnable Buffer.
+    char* pBuffer = Con::getReturnBuffer(maxBufferSize);
+
+    // Set Buffer Counter.
+    U32 bufferCount = 0;
+
+    // Add Picked Objects to List.
+    for ( U32 n = 0; n < resultCount; n++ )
+    {
+        // Output Object ID.
+        bufferCount += dSprintf( pBuffer + bufferCount, maxBufferSize-bufferCount, "%d ", queryResults[n].mpSpriteBatchItem->getBatchId() );
+
+        // Finish early if we run out of buffer space.
+        if ( bufferCount >= maxBufferSize )
+        {
+            // Warn.
+            Con::warnf("CompositeSprite::pickRay() - Too many items picked to return to scripts!");
+            break;
+        }
+    }
+
+    // Clear sprite batch query.
+    pSpriteBatchQuery->clearQuery();
+
+    // Return buffer.
+    return pBuffer;
+}

+ 1 - 1
engine/source/2d/sceneobject/ImageFont.cc

@@ -114,7 +114,7 @@ void ImageFont::initPersistFields()
     addProtectedField("image", TypeImageAssetPtr, Offset(mImageAsset, ImageFont), &setImage, &getImage, &writeImage, "");
     addProtectedField("text", TypeString, 0, setText, getText, &writeText, "The text to be displayed." );  
     addProtectedField("textAlignment", TypeEnum, Offset(mTextAlignment, ImageFont), &setTextAlignment, &defaultProtectedGetFn, &writeTextAlignment, 1, &gTextAlignmentTable, "");
-    addProtectedField("fontSize", TypeT2DVector, Offset(mFontSize, ImageFont), &setFontSize, &defaultProtectedGetFn,&writeFontSize, "" );
+    addProtectedField("fontSize", TypeVector2, Offset(mFontSize, ImageFont), &setFontSize, &defaultProtectedGetFn,&writeFontSize, "" );
     addProtectedField("fontPadding", TypeF32, Offset(mFontPadding, ImageFont), &setFontPadding, &defaultProtectedGetFn, &writeFontPadding, "" );
 }
 

+ 1 - 1
engine/source/2d/sceneobject/ParticlePlayer.cc

@@ -705,7 +705,7 @@ void ParticlePlayer::sceneRenderOverlay( const SceneRenderState* sceneRenderStat
         return;
 
     // Draw camera pause distance.
-    pScene->mDebugDraw.DrawCircle( getRenderPosition(), mCameraIdleDistance, b2Color(1.0f, 1.0f, 0.0f ) );
+    pScene->mDebugDraw.DrawCircle( getRenderPosition(), mCameraIdleDistance, ColorF(1.0f, 1.0f, 0.0f ) );
 }
 
 //-----------------------------------------------------------------------------

+ 291 - 219
engine/source/2d/sceneobject/SceneObject.cc

@@ -87,30 +87,22 @@ static U32 sSceneObjectMasterSerialId = 0;
 // Collision shape property names.
 static bool collisionShapePropertiesInitialized = false;
 
-static StringTableEntry shapeCustomPropertyName;
+static StringTableEntry shapeCustomNodeName;
 
 static StringTableEntry shapeDensityName;
 static StringTableEntry shapeFrictionName;
 static StringTableEntry shapeRestitutionName;
 static StringTableEntry shapeSensorName;
+static StringTableEntry shapePointName;
+static StringTableEntry shapePrevPointName;
+static StringTableEntry shapeNextPointName;
 
 static StringTableEntry circleTypeName;
 static StringTableEntry circleRadiusName;
 static StringTableEntry circleOffsetName;
-
 static StringTableEntry polygonTypeName;
-static StringTableEntry polygonPointName;
-
 static StringTableEntry chainTypeName;
-static StringTableEntry chainPointName;
-static StringTableEntry chainAdjacentStartName;
-static StringTableEntry chainAdjacentEndName;
-
 static StringTableEntry edgeTypeName;
-static StringTableEntry edgeStartName;
-static StringTableEntry edgeEndName;
-static StringTableEntry edgeAdjacentStartName;
-static StringTableEntry edgeAdjacentEndName;
 
 //------------------------------------------------------------------------------
 
@@ -187,9 +179,6 @@ SceneObject::SceneObject() :
     mpAttachedGui(NULL),
     mpAttachedGuiSceneWindow(NULL),
 
-    /// Pathing.
-    mAttachedToPath(NULL),
-
     /// Safe deletion.
     mBeingSafeDeleted(false),
     mSafeDeleteReady(true),
@@ -208,30 +197,21 @@ SceneObject::SceneObject() :
     // Initialize collision shape field names.
     if ( !collisionShapePropertiesInitialized )
     {
-        shapeCustomPropertyName     = StringTable->insert( "CollisionShapes" );
+        shapeCustomNodeName     = StringTable->insert( "CollisionShapes" );
 
         shapeDensityName        = StringTable->insert( "Density" );
         shapeFrictionName       = StringTable->insert( "Friction" );
         shapeRestitutionName    = StringTable->insert( "Restitution" );
         shapeSensorName         = StringTable->insert( "Sensor" );
-
+        shapePointName          = StringTable->insert( "Point" );
+        shapePrevPointName      = StringTable->insert( "PreviousPoint" );
+        shapeNextPointName      = StringTable->insert( "NextPoint" );
         circleTypeName          = StringTable->insert( "Circle" );
         circleRadiusName        = StringTable->insert( "Radius" );
         circleOffsetName        = StringTable->insert( "Offset" );
-
         polygonTypeName         = StringTable->insert( "Polygon" );
-        polygonPointName        = StringTable->insert( "Point" );
-
         chainTypeName           = StringTable->insert( "Chain" );
-        chainPointName          = polygonPointName;
-        chainAdjacentStartName  = StringTable->insert( "AdjacentStartPoint" );
-        chainAdjacentEndName    = StringTable->insert( "AdjacentEndPoint" );
-
         edgeTypeName            = StringTable->insert( "Edge" );
-        edgeStartName           = StringTable->insert( "Point0" );
-        edgeEndName             = StringTable->insert( "Point1" );
-        edgeAdjacentStartName   = chainAdjacentStartName;
-        edgeAdjacentEndName     = chainAdjacentEndName;
 
         // Flag as initialized.
         collisionShapePropertiesInitialized = true;
@@ -366,6 +346,8 @@ void SceneObject::initPersistFields()
     /// Input events.
     addField("UseInputEvents", TypeBool, Offset(mUseInputEvents, SceneObject), &writeUseInputEvents, "");
 
+    addField("PickingAllowed", TypeBool, Offset(mPickingAllowed, SceneObject), &writePickingAllowed, "");
+
     // Script callbacks.
     addField("UpdateCallback", TypeBool, Offset(mUpdateCallback, SceneObject), &writeUpdateCallback, "");
     addField("CollisionCallback", TypeBool, Offset(mCollisionCallback, SceneObject), &writeCollisionCallback, "");
@@ -823,19 +805,19 @@ void SceneObject::sceneRenderOverlay( const SceneRenderState* sceneRenderState )
     // AABB debug draw.
     if ( debugMask & Scene::SCENE_DEBUG_AABB )
     {
-        pScene->mDebugDraw.DrawAABB( mCurrentAABB );
+        pScene->mDebugDraw.DrawAABB( mCurrentAABB, ColorF(0.7f, 0.7f, 0.9f) );
     }
 
     // OOBB debug draw.
     if ( debugMask & Scene::SCENE_DEBUG_OOBB )
     {
-        pScene->mDebugDraw.DrawOOBB( mRenderOOBB );
+        pScene->mDebugDraw.DrawOOBB( mRenderOOBB, ColorF(0.9f, 0.9f, 1.0f) );
     }
 
     // Asleep debug draw.
     if ( !getAwake() && debugMask & Scene::SCENE_DEBUG_SLEEP )
     {
-        pScene->mDebugDraw.DrawAsleep( mRenderOOBB );
+        pScene->mDebugDraw.DrawAsleep( mRenderOOBB, ColorF( 0.0f, 1.0f, 0.0f ) );
     }
 
     // Collision Shapes.
@@ -849,8 +831,8 @@ void SceneObject::sceneRenderOverlay( const SceneRenderState* sceneRenderState )
     {
         const b2Vec2 renderPosition = getRenderPosition();
 
-        pScene->mDebugDraw.DrawPoint( renderPosition + getLocalCenter(), 6, b2Color( 0.0f, 1.0f, 0.4f ) );
-        pScene->mDebugDraw.DrawPoint( renderPosition, 4, b2Color( 0.0f, 0.4f, 1.0f ) );
+        pScene->mDebugDraw.DrawPoint( renderPosition + getLocalCenter(), 6, ColorF( 0.0f, 1.0f, 0.4f ) );
+        pScene->mDebugDraw.DrawPoint( renderPosition, 4, ColorF( 0.0f, 0.4f, 1.0f ) );
     }
 
     // Sort Points.
@@ -1454,27 +1436,20 @@ void SceneObject::applyAngularImpulse( const F32 impulse, const bool wake )
 
 //-----------------------------------------------------------------------------
 
-void SceneObject::setCollisionMasks( const U32 groupMask, const U32 layerMask )
-{
-    // Set Group/Layer Collision Masks.
-    mCollisionGroupMask = groupMask;
-    mCollisionLayerMask = layerMask;
-}
-
-//-----------------------------------------------------------------------------
-
 void SceneObject::setCollisionAgainst( const SceneObject* pSceneObject, const bool clearMasks )
 {
     // Do we need to clear existing masks?
     if ( clearMasks )
     {
         // Yes, so just set the masks to the referenced-objects' masks.
-        setCollisionMasks( pSceneObject->getSceneGroupMask(), pSceneObject->getSceneLayerMask() );
+        setCollisionGroupMask( pSceneObject->getCollisionGroupMask() );
+        setCollisionLayerMask( pSceneObject->getCollisionLayerMask() ); 
     }
     else
     {
         // No, so merge with existing masks.
-        setCollisionMasks( getCollisionGroupMask() | pSceneObject->getSceneGroupMask(), getCollisionLayerMask() | pSceneObject->getSceneLayerMask() );
+        setCollisionGroupMask( getCollisionGroupMask() | pSceneObject->getCollisionGroupMask() );
+        setCollisionLayerMask( getCollisionLayerMask() | pSceneObject->getCollisionLayerMask() ); 
     }
 }
 
@@ -1588,7 +1563,7 @@ void SceneObject::initializeContactGathering( void )
     }
 
     // Generate current contacts.
-    mpCurrentContacts = new typeContactVector();
+    mpCurrentContacts = new Scene::typeContactVector();
 }
 
 //-----------------------------------------------------------------------------
@@ -1620,7 +1595,7 @@ void SceneObject::onEndCollision( const TickContact& tickContact )
     AssertFatal( tickContact.mpSceneObjectA == this || tickContact.mpSceneObjectB == this, "SceneObject::onEndCollision() - Contact does not involve this scene object." );
 
     // Remove contact.
-    for( typeContactVector::iterator contactItr = mpCurrentContacts->begin(); contactItr != mpCurrentContacts->end(); ++contactItr )
+    for( Scene::typeContactVector::iterator contactItr = mpCurrentContacts->begin(); contactItr != mpCurrentContacts->end(); ++contactItr )
     {
         // Is this is the correct contact?
         if ( contactItr->mpContact == tickContact.mpContact )
@@ -2895,8 +2870,8 @@ void SceneObject::copyTo( SimObject* obj )
     pSceneObject->setSleepingAllowed( getSleepingAllowed() );
 
     /// Collision control.
-    pSceneObject->setCollisionGroups( getCollisionGroupMask() );
-    pSceneObject->setCollisionLayers( getCollisionLayerMask() );
+    pSceneObject->setCollisionGroupMask( getCollisionGroupMask() );
+    pSceneObject->setCollisionLayerMask( getCollisionLayerMask() );
     pSceneObject->setCollisionSuppress( getCollisionSuppress() );
     pSceneObject->setGatherContacts( getGatherContacts() );
     pSceneObject->setDefaultDensity( getDefaultDensity() );
@@ -3339,31 +3314,6 @@ void SceneObject::notifyComponentsUpdate( void )
 
 //-----------------------------------------------------------------------------
 
-BehaviorInstance* SceneObject::behavior(const char *name)
-{
-    // Debug Profiling.
-    PROFILE_SCOPE(SceneObject_BehaviorName);
-
-    StringTableEntry stName = StringTable->insert(name);
-    VectorPtr<SimComponent *>&componentList = lockComponentList();
-
-    for( SimComponentIterator nItr = componentList.begin(); nItr != componentList.end(); nItr++ )
-    {
-        BehaviorInstance* pComponent = dynamic_cast<BehaviorInstance*>(*nItr);
-        if( pComponent && StringTable->insert(pComponent->getTemplateName()) == stName )
-        {
-            unlockComponentList();
-            return pComponent;
-        }
-    }
-
-    unlockComponentList();
-
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
 U32 SceneObject::getGlobalSceneObjectCount( void )
 {
     return sGlobalSceneObjectCount;
@@ -3371,13 +3321,13 @@ U32 SceneObject::getGlobalSceneObjectCount( void )
 
 //-----------------------------------------------------------------------------
 
-void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void SceneObject::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SceneObject_OnTamlCustomWrite);
 
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch collision shape count.
     const U32 collisionShapeCount = getCollisionShapeCount();
@@ -3386,8 +3336,8 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
     if ( collisionShapeCount == 0 )
         return;
 
-    // Add collision shape property.
-    TamlCustomProperty* pCollisionShapeProperty = customProperties.addProperty( shapeCustomPropertyName );
+    // Add collision shape node.
+    TamlCustomNode* pCustomCollisionShapes = customNodes.addNode( shapeCustomNodeName );
 
     // Iterate collision shapes.
     for ( U32 shapeIndex = 0; shapeIndex < collisionShapeCount; ++shapeIndex )
@@ -3395,30 +3345,30 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
         // Fetch collision shape definition.
         b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );
 
-        // Add collision shape alias.
-        // NOTE:    The name of the alias will get updated shortly.
-        TamlPropertyAlias* pCollisionShapeAlias = pCollisionShapeProperty->addAlias( StringTable->EmptyString );
+        // Add collision shape node.
+        // NOTE:    The name of the node will get updated shortly.
+        TamlCustomNode* pCollisionShapeNode = pCustomCollisionShapes->addNode( StringTable->EmptyString );
 
         // Add common collision shape fields.
         if ( mNotEqual( getDefaultDensity(), fixtureDef.density ) )
-            pCollisionShapeAlias->addField( shapeDensityName, fixtureDef.density );
+            pCollisionShapeNode->addField( shapeDensityName, fixtureDef.density );
 
         if ( mNotEqual( getDefaultFriction(), fixtureDef.friction ) )
-            pCollisionShapeAlias->addField( shapeFrictionName, fixtureDef.friction );
+            pCollisionShapeNode->addField( shapeFrictionName, fixtureDef.friction );
 
         if ( mNotEqual( getDefaultRestitution(), fixtureDef.restitution ) )
-            pCollisionShapeAlias->addField( shapeRestitutionName, fixtureDef.restitution );
+            pCollisionShapeNode->addField( shapeRestitutionName, fixtureDef.restitution );
 
         if ( fixtureDef.isSensor == true )
-            pCollisionShapeAlias->addField( shapeSensorName, fixtureDef.isSensor );
+            pCollisionShapeNode->addField( shapeSensorName, fixtureDef.isSensor );
 
         // Populate collision shape appropriately.
         switch( fixtureDef.shape->GetType() )
         {
         case b2Shape::e_circle:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( circleTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( circleTypeName );
 
                 // Fetch shape.
                 const b2CircleShape* pShape = dynamic_cast<const b2CircleShape*>( fixtureDef.shape );
@@ -3427,18 +3377,18 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid circle shape type returned." );
 
                 // Add radius property.
-                pCollisionShapeAlias->addField( circleRadiusName, pShape->m_radius );
+                pCollisionShapeNode->addField( circleRadiusName, pShape->m_radius );
 
                 // Add offset property (if not zero).
                 if ( !Vector2(pShape->m_p).isZero() )
-                    pCollisionShapeAlias->addField( circleOffsetName, pShape->m_p );
+                    pCollisionShapeNode->addField( circleOffsetName, pShape->m_p );
             }
             break;
 
         case b2Shape::e_polygon:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( polygonTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( polygonTypeName );
 
                 // Fetch shape.
                 const b2PolygonShape* pShape = dynamic_cast<const b2PolygonShape*>( fixtureDef.shape );
@@ -3452,20 +3402,22 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Add shape properties.
                 for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                 {
-                    // Format point index name.
-                    char pointIndexBuffer[16];
-                    dSprintf( pointIndexBuffer, sizeof(pointIndexBuffer), "%s%d", polygonPointName, pointIndex );
-                    
-                    // Add point property.
-                    pCollisionShapeAlias->addField( pointIndexBuffer, pShape->GetVertex( pointIndex ) );
+                    // Add point node.
+                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );
+
+                    // Fetch point.
+                    const b2Vec2& point = pShape->GetVertex( pointIndex );
+
+                    // Add point fields.
+                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, point );
                 }
             }
             break;
 
         case b2Shape::e_chain:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( chainTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( chainTypeName );
 
                 // Fetch shape.
                 const b2ChainShape* pShape = dynamic_cast<const b2ChainShape*>( fixtureDef.shape );
@@ -3479,28 +3431,33 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Add shape properties.
                 for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                 {
-                    // Format point index name.
-                    char pointIndexBuffer[16];
-                    dSprintf( pointIndexBuffer, sizeof(pointIndexBuffer), "%s%d", chainPointName, pointIndex );
-                    
-                    // Add point property.
-                    pCollisionShapeAlias->addField( pointIndexBuffer, pShape->m_vertices[pointIndex] );
+                    // Add point node.
+                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );
+
+                    // Add point fields.
+                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertices[pointIndex] );
                 }
 
                 // Add adjacent start point (if specified).
                 if ( pShape->m_hasPrevVertex )
-                    pCollisionShapeAlias->addField( chainAdjacentStartName, pShape->m_prevVertex );
+                {
+                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
+                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_prevVertex );
+                }
 
                 // Add adjacent end point (if specified).
                 if ( pShape->m_hasNextVertex )
-                    pCollisionShapeAlias->addField( chainAdjacentEndName, pShape->m_nextVertex );
+                {
+                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
+                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_nextVertex );
+                }
             }
             break;
 
         case b2Shape::e_edge:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( edgeTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( edgeTypeName );
 
                 // Fetch shape.
                 const b2EdgeShape* pShape = dynamic_cast<const b2EdgeShape*>( fixtureDef.shape );
@@ -3508,17 +3465,27 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Sanity!
                 AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid edge shape type returned." );
 
-                // Add start/end points.
-                pCollisionShapeAlias->addField( edgeStartName, pShape->m_vertex1 );
-                pCollisionShapeAlias->addField( edgeEndName, pShape->m_vertex2 );
+                // Add start point.
+                TamlCustomNode* pStartPointNode = pCollisionShapeNode->addNode( shapePointName );
+                pStartPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex1 );
+
+                // Add end point.
+                TamlCustomNode* pEndPointNode = pCollisionShapeNode->addNode( shapePointName );
+                pEndPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex2 );
 
                 // Add adjacent start point (if specified).
                 if ( pShape->m_hasVertex0 )
-                    pCollisionShapeAlias->addField( edgeAdjacentStartName, pShape->m_vertex0 );
+                {
+                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
+                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex0 );
+                }
 
                 // Add adjacent end point (if specified).
                 if ( pShape->m_hasVertex3 )
-                    pCollisionShapeAlias->addField( edgeAdjacentEndName, pShape->m_vertex3 );
+                {
+                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
+                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex3 );
+                }
             }
             break;
 
@@ -3531,29 +3498,32 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
 
 //-----------------------------------------------------------------------------
 
-void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void SceneObject::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SceneObject_OnTamlCustomRead);
 
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find collision shape custom property.
-    const TamlCustomProperty* pCollisionShapeProperty = customProperties.findProperty( shapeCustomPropertyName );
+    // Find collision shape custom node.
+    const TamlCustomNode* pCustomCollisionShapes = customNodes.findNode( shapeCustomNodeName );
 
     // Finish if we don't have collision shapes.
-    if ( pCollisionShapeProperty == NULL )
+    if ( pCustomCollisionShapes == NULL )
         return;
 
+    // Fetch children shapes.
+    const TamlCustomNodeVector& collisionShapeChildren = pCustomCollisionShapes->getChildren();
+
     // Iterate collision shapes.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pCollisionShapeProperty->begin(); propertyAliasItr != pCollisionShapeProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator shapeNodeItr = collisionShapeChildren.begin(); shapeNodeItr != collisionShapeChildren.end(); ++shapeNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch shape node.
+        TamlCustomNode* pShapeNode = *shapeNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch shape name.
+        StringTableEntry shapeName = pShapeNode->getNodeName();
 
         // Ready common fields.
         F32 shapeDensity     = getDefaultDensity();
@@ -3564,46 +3534,49 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
         S32 shapeIndex;
 
         // Is this a circle shape?
-        if ( aliasName == circleTypeName )
+        if ( shapeName == circleTypeName )
         {
             // Yes, so ready fields.
             F32 radius = 0.0f;
             b2Vec2 offset( 0.0f, 0.0f );
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
+                    pField->getFieldValue( shapeSensor );
                 }
                 // Check circle fields.
                 else if ( fieldName == circleRadiusName )
                 {
-                    pPropertyField->getFieldValue( radius );
+                    pField->getFieldValue( radius );
                 }
                 else if ( fieldName == circleOffsetName )
                 {
-                    pPropertyField->getFieldValue( offset );
+                    pField->getFieldValue( offset );
                 }                   
             }
 
@@ -3621,51 +3594,68 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createCircleCollisionShape( radius, offset );
         }
         // Is this a polygon shape?
-        else if ( aliasName == polygonTypeName )
+        else if ( shapeName == polygonTypeName )
         {
-            // Yes, so ready fields.
-            b2Vec2 points[b2_maxPolygonVertices];
-            U32 pointCount = 0;
+            // Yes, so fetch shape fields.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
 
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
+                    pField->getFieldValue( shapeSensor );
                 }
-                // Check polygon fields.
-                else if ( pPropertyField->fieldNameBeginsWith( polygonPointName ) )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Reset points.
+            b2Vec2 points[b2_maxPolygonVertices];
+            U32 pointCount = 0;
+
+            // Do we have any shape children.
+            if ( shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    // Is the point count at maximum?
-                    if ( pointCount == b2_maxPolygonVertices )
-                    {
-                        // Yes, so warn.
-                        Con::warnf( "SceneObject::onTamlCustomRead() - Polygon point count exceed the maximum points '%d'.", b2_maxPolygonVertices );
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Skip if it's not a point.
+                    if ( pChildNode->getNodeName() != shapePointName )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
                         continue;
-                    }
 
+                    // Read point.
                     b2Vec2 point;
-                    pPropertyField->getFieldValue( point );
+                    pChildNode->getNodeTextField().getFieldValue( point );
                     points[pointCount++] = point;
                 }
             }
@@ -3683,65 +3673,99 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createPolygonCollisionShape( pointCount, points );
         }
         // Is this a chain shape?
-        else if ( aliasName == chainTypeName )
+        else if ( shapeName == chainTypeName )
         {
             // Yes, so ready fields.
             Vector<b2Vec2> points;
-            bool hasAdjacentStartPoint;
-            bool hasAdjacentEndPoint;
+            bool hasAdjacentStartPoint = false;
+            bool hasAdjacentEndPoint = false;
             b2Vec2 adjacentStartPoint;
             b2Vec2 adjacentEndPoint;
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
-                }
-                // Check chain fields.
-                else if ( pPropertyField->fieldNameBeginsWith( chainPointName ) )
-                {
-                    b2Vec2 point;
-                    pPropertyField->getFieldValue( point );
-                    points.push_back( point );
-                }
-                else if ( fieldName == chainAdjacentStartName )
-                {
-                    pPropertyField->getFieldValue( adjacentStartPoint );
-                    hasAdjacentStartPoint = true;
+                    pField->getFieldValue( shapeSensor );
                 }
-                else if ( fieldName == chainAdjacentEndName )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Do we have any shape children.
+            // NOTE: Only do this if the old methods has not been used.
+            if ( points.size() == 0 && shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    pPropertyField->getFieldValue( adjacentEndPoint );
-                    hasAdjacentEndPoint = true;
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Fetch the node name.
+                    StringTableEntry nodeName = pChildNode->getNodeName();
+
+                    // Skip if it's not a point.
+                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
+                        continue;
+
+                    if ( nodeName == shapePointName )
+                    {
+                        // Read point.
+                        b2Vec2 point;
+                        pChildNode->getNodeTextField().getFieldValue( point );
+                        points.push_back( point );
+                    }
+                    else if ( nodeName == shapePrevPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
+                        hasAdjacentStartPoint = true;
+                    }
+                    else if ( nodeName == shapeNextPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
+                        hasAdjacentEndPoint = true;
+                    }
                 }
             }
 
             // Is point count valid?
-            if ( points.size() == 0 )
+            if ( points.size() == 0 || points.size() < 2 )
             {
                 // No, so warn.
-                Con::warnf( "SceneObject::onTamlCustomRead() - No points on chain collision shape." );
+                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or less than two) on chain collision shape." );
 
                 continue;
             }
@@ -3750,63 +3774,111 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createChainCollisionShape( points.size(), points.address(), hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
         }
         // Is this an edge shape?
-        else if ( aliasName == edgeTypeName )
+        else if ( shapeName == edgeTypeName )
         {
             // Yes, so ready fields.
             b2Vec2 point0;
             b2Vec2 point1;
-            bool hasAdjacentStartPoint;
-            bool hasAdjacentEndPoint;
+            U32 pointCount = 0;
+            bool hasAdjacentStartPoint = false;
+            bool hasAdjacentEndPoint = false;
             b2Vec2 adjacentStartPoint;
             b2Vec2 adjacentEndPoint;
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
+                    pField->getFieldValue( shapeSensor );
                 }
-                // Check edge fields.
-                else if ( fieldName == edgeStartName )
-                {
-                    pPropertyField->getFieldValue( point0 );
-                }
-                else if ( fieldName == edgeEndName )
-                {
-                    pPropertyField->getFieldValue( point1 );
-                }
-                else if ( fieldName == edgeAdjacentStartName )
-                {
-                    pPropertyField->getFieldValue( adjacentStartPoint );
-                    hasAdjacentStartPoint = true;
-                }
-                else if ( fieldName == edgeAdjacentEndName )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Do we have any shape children.
+            if ( shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    pPropertyField->getFieldValue( adjacentEndPoint );
-                    hasAdjacentEndPoint = true;
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Fetch the node name.
+                    StringTableEntry nodeName = pChildNode->getNodeName();
+
+                    // Skip if it's not a point.
+                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
+                        continue;
+
+                    if ( nodeName == shapePointName )
+                    {
+                        // Ignore if too many points.
+                        if ( pointCount >= 2 )
+                            continue;
+
+                        // Read point.               
+                        if ( pointCount == 0 )
+                            pChildNode->getNodeTextField().getFieldValue( point0 );
+                        else
+                            pChildNode->getNodeTextField().getFieldValue( point1 );
+
+                        pointCount++;
+                    }
+                    else if ( nodeName == shapePrevPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
+                        hasAdjacentStartPoint = true;
+                    }
+                    else if ( nodeName == shapeNextPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
+                        hasAdjacentEndPoint = true;
+                    }
                 }
             }
 
+            // Is point count valid?
+            if ( pointCount == 0 || pointCount != 2 )
+            {
+                // No, so warn.
+                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or not two points) on edge collision shape." );
+
+                continue;
+            }
+
             // Create shape.
             shapeIndex = createEdgeCollisionShape( point0, point1, hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
         }
@@ -3814,7 +3886,7 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
         else
         {
             // Warn.
-            Con::warnf( "Unknown shape type of '%s' encountered.", aliasName );
+            Con::warnf( "Unknown shape type of '%s' encountered.", shapeName );
 
             // Sanity!
             AssertFatal( false, "SceneObject::onTamlCustomRead() - Unknown shape type detected." );
@@ -3873,9 +3945,9 @@ S32 QSORT_CALLBACK SceneObject::sceneObjectLayerDepthSort(const void* a, const v
 
 static EnumTable::Enums bodyTypeLookup[] =
                 {
-                { b2_staticBody,    "static"    },
-                { b2_kinematicBody, "kinematic" },
-                { b2_dynamicBody,   "dynamic"   },
+                { b2_staticBody,    "Static"    },
+                { b2_kinematicBody, "Kinematic" },
+                { b2_dynamicBody,   "Dynamic"   },
                 };
 
 EnumTable bodyTypeTable(sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums), &bodyTypeLookup[0]);
@@ -3884,10 +3956,10 @@ EnumTable bodyTypeTable(sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums), &body
 
 static EnumTable::Enums collisionShapeTypeLookup[] =
                 {
-                { b2Shape::e_circle,             "circle"   },
-                { b2Shape::e_edge,               "edge"     },
-                { b2Shape::e_polygon,            "polygon"  },
-                { b2Shape::e_chain,              "chain"    },
+                { b2Shape::e_circle,             "Circle"   },
+                { b2Shape::e_edge,               "Edge"     },
+                { b2Shape::e_polygon,            "Polygon"  },
+                { b2Shape::e_chain,              "Chain"    },
                 };
 
 EnumTable collisionShapeTypeTable(sizeof(collisionShapeTypeLookup) / sizeof(EnumTable::Enums), &collisionShapeTypeLookup[0]);

+ 14 - 17
engine/source/2d/sceneobject/SceneObject.h

@@ -165,7 +165,7 @@ protected:
     bool                    mCollisionSuppress;
     b2FixtureDef            mDefaultFixture;
     bool                    mGatherContacts;
-    typeContactVector*      mpCurrentContacts;
+    Scene::typeContactVector* mpCurrentContacts;
 
     /// General collision shape access.
     typeCollisionFixtureDefVector mCollisionFixtureDefs;
@@ -204,9 +204,6 @@ protected:
     GuiControl*             mpAttachedGui;
     SceneWindow*            mpAttachedGuiSceneWindow;
 
-    /// Pathing.
-    SimObjectPtr<SceneObject> mAttachedToPath;
-
     /// Safe deletion.
     bool                    mBeingSafeDeleted;
     bool                    mSafeDeleteReady;
@@ -241,8 +238,8 @@ protected:
     void                    initializeContactGathering( void );
 
     /// Taml callbacks.
-    virtual void            onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void            onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     SceneObject();
@@ -360,11 +357,10 @@ public:
     inline F32              getInertia( void ) const                    { if ( mpScene ) return mpBody->GetInertia(); else return 0.0f; }
 
     /// Collision control.
-    void                    setCollisionMasks( const U32 groupMask, const U32 layerMask = MASK_ALL );
     void                    setCollisionAgainst( const SceneObject* pSceneObject, const bool clearMasks );
-    inline void             setCollisionLayers( const U32 layerMask )   { setCollisionMasks(getCollisionGroupMask(), layerMask); }
-    inline void             setCollisionGroups( const U32 groupMask )   { setCollisionMasks(groupMask, getCollisionLayerMask()); }
+    inline void             setCollisionGroupMask( const U32 groupMask ) { mCollisionGroupMask = groupMask; }
     inline U32              getCollisionGroupMask(void) const           { return mCollisionGroupMask; }
+    inline void             setCollisionLayerMask( const U32 layerMask ) { mCollisionLayerMask = layerMask; }
     inline U32              getCollisionLayerMask(void) const           { return mCollisionLayerMask; }
     void                    setDefaultDensity( const F32 density, const bool updateShapes = true );
     inline F32              getDefaultDensity( void ) const             { return mDefaultFixture.density; }
@@ -374,7 +370,7 @@ public:
     inline F32              getDefaultRestitution( void ) const         { return mDefaultFixture.restitution; }
     inline void             setCollisionSuppress( const bool status )   { mCollisionSuppress = status; }
     inline bool             getCollisionSuppress(void) const            { return mCollisionSuppress; }
-    inline const typeContactVector* getCurrentContacts( void ) const    { return mpCurrentContacts; }
+    inline const Scene::typeContactVector* getCurrentContacts( void ) const    { return mpCurrentContacts; }
     inline U32              getCurrentContactCount( void ) const        { if ( mpCurrentContacts != NULL ) return mpCurrentContacts->size(); else return 0; }
     virtual void            setGatherContacts( const bool gatherContacts ) { mGatherContacts = gatherContacts; initializeContactGathering(); }
     inline bool             getGatherContacts( void ) const             { return mGatherContacts; }
@@ -534,9 +530,9 @@ public:
     void                    detachGui( void );
     inline void             updateAttachedGui( void );
 
-    /// Pathing.
-    inline                  void setAttachedToPath(SceneObject* path){ mAttachedToPath = path; }
-    inline SceneObject*     getAttachedToPath() const                   { return mAttachedToPath; }
+    // Picking.
+    inline void             setPickingAllowed( const bool pickingAllowed ) { mPickingAllowed = pickingAllowed; }
+    inline bool             getPickingAllowed(void) const               { return mPickingAllowed; }
 
     /// Cloning.
     virtual void            copyFrom( SceneObject* pSceneObject, const bool copyDynamicFields );
@@ -561,11 +557,9 @@ public:
 
     /// Miscellaneous.
     inline const char*      scriptThis(void) const                      { return Con::getIntArg(getId()); }
-    inline bool             getIsPickingAllowed(void) const             { return mPickingAllowed; }
     inline bool             getIsAlwaysInScope(void) const              { return mAlwaysInScope; }
     inline void             setWorldQueryKey( const U32 key )           { mWorldQueryKey = key; }
     inline U32              getWorldQueryKey( void ) const              { return mWorldQueryKey; }
-    BehaviorInstance*       behavior(const char *name);
     static U32              getGlobalSceneObjectCount( void );
     inline U32              getSerialId( void ) const                   { return mSerialId; }
 
@@ -666,9 +660,9 @@ protected:
     static bool             writeDefaultFriction( void* obj, StringTableEntry pFieldName ) {return mNotEqual(static_cast<SceneObject*>(obj)->getDefaultFriction(), 0.2f); }
     static bool             setDefaultRestitution(void* obj, const char* data) { static_cast<SceneObject*>(obj)->setDefaultRestitution(dAtof(data)); return false; }
     static bool             writeDefaultRestitution( void* obj, StringTableEntry pFieldName ) { return mNotEqual(static_cast<SceneObject*>(obj)->getDefaultRestitution(), 0.0f); }
-    static bool             setCollisionGroups(void* obj, const char* data) { static_cast<SceneObject*>(obj)->setCollisionGroups(dAtoi(data)); return false; }
+    static bool             setCollisionGroups(void* obj, const char* data) { static_cast<SceneObject*>(obj)->setCollisionGroupMask(dAtoi(data)); return false; }
     static bool             writeCollisionGroups( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionGroupMask() != MASK_ALL; }
-    static bool             setCollisionLayers(void* obj, const char* data) { static_cast<SceneObject*>(obj)->setCollisionLayers(dAtoi(data)); return false; }
+    static bool             setCollisionLayers(void* obj, const char* data) { static_cast<SceneObject*>(obj)->setCollisionLayerMask(dAtoi(data)); return false; }
     static bool             writeCollisionLayers( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionLayerMask() != MASK_ALL; }
     static bool             writeCollisionSuppress( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionSuppress() == true; }
     static bool             setGatherContacts(void* obj, const char* data)  { static_cast<SceneObject*>(obj)->setGatherContacts(dAtoi(data)); return false; }
@@ -710,6 +704,9 @@ protected:
     /// Input events.
     static bool             writeUseInputEvents( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getUseInputEvents() == true; }
 
+    /// Picking.
+    static bool             writePickingAllowed( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getPickingAllowed() == true; }    
+
     /// Script callbacks.
     static bool             writeUpdateCallback( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getUpdateCallback() == true; }
     static bool             writeCollisionCallback( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionCallback() == true; }

+ 92 - 0
engine/source/2d/sceneobject/SceneObjectList.cc

@@ -0,0 +1,92 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "sceneObjectList.h"
+#include "2d/sceneObject/sceneObject.h"
+#include "collection/findIterator.h"
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::pushBack(SceneObject* obj)
+{
+	if (find(begin(),end(),obj) == end())
+		push_back(obj);
+}	
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::pushBackForce(SceneObject* obj)
+{
+	iterator itr = find(begin(),end(),obj);
+	if (itr == end()) 
+	{
+		push_back(obj);
+	}
+	else 
+	{
+		// Move to the back...
+		SceneObject* pBack = *itr;
+		removeStable(pBack);
+		push_back(pBack);
+	}
+}	
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::pushFront(SceneObject* obj)
+{
+	if (find(begin(),end(),obj) == end())
+		push_front(obj);
+}	
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::remove(SceneObject* obj)
+{
+	iterator ptr = find(begin(),end(),obj);
+	if (ptr != end()) 
+		erase(ptr);
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::removeStable(SceneObject* obj)
+{
+	iterator ptr = find(begin(),end(),obj);
+	if (ptr != end()) 
+		erase(ptr);
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectList::sortId()
+{
+	dQsort(address(),size(),sizeof(value_type),compareId);
+}	
+
+//-----------------------------------------------------------------------------
+
+S32 QSORT_CALLBACK SceneObjectList::compareId(const void* a,const void* b)
+{
+   return (*reinterpret_cast<const SceneObject* const*>(a))->getId() -
+      (*reinterpret_cast<const SceneObject* const*>(b))->getId();
+}

+ 62 - 0
engine/source/2d/sceneobject/SceneObjectList.h

@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SCENE_OBJECT_LIST_H_
+#define _SCENE_OBJECT_LIST_H_
+
+#ifndef _VECTOR_H_
+#include "collection/vector.h"
+#endif
+
+//-----------------------------------------------------------------------------
+
+class SceneObject;
+
+//-----------------------------------------------------------------------------
+
+class SceneObjectList : public VectorPtr<SceneObject*>
+{
+	static S32 QSORT_CALLBACK compareId(const void* a,const void* b);
+
+public:
+	///< Add the SceneObject* to the end of the list, unless it's already in the list.
+	void pushBack(SceneObject*); 
+
+	///< Add the SceneObject* to the end of the list, moving it there if it's already present in the list.
+	void pushBackForce(SceneObject*);
+
+	///< Add the SceneObject* to the start of the list.
+	void pushFront(SceneObject*);
+
+	///< Remove the SceneObject* from the list; may disrupt order of the list.
+	void remove(SceneObject*);         
+
+	/// Remove the SimObject* from the list; guaranteed to preserve list order.
+	void removeStable(SceneObject* pObject);
+
+	inline SceneObject* at(S32 index) const {  if(index >= 0 && index < size()) return (*this)[index]; return NULL; }
+
+	///< Sort the list by object ID.
+	void sortId();
+};
+
+#endif // _SCENE_OBJECT_LIST_H_

+ 296 - 0
engine/source/2d/sceneobject/SceneObjectSet.cc

@@ -0,0 +1,296 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "sim/simBase.h"
+#include "string/stringTable.h"
+#include "console/console.h"
+#include "io/fileStream.h"
+#include "input/actionMap.h"
+#include "io/resource/resourceManager.h"
+#include "io/fileObject.h"
+#include "console/consoleInternal.h"
+#include "debug/profiler.h"
+#include "console/consoleTypeValidators.h"
+#include "memory/frameAllocator.h"
+
+#include "2d/sceneObject/sceneObject.h"
+#include "2d/sceneObject/sceneObjectSet.h"
+
+// Script bindings.
+#include "2d/sceneObject/sceneObjectSet_ScriptBinding.h"
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(SceneObjectSet);
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::addObject(SceneObject* obj)
+{
+	if ( obj == NULL )
+		return;
+
+   mObjectList.pushBack(obj);
+   deleteNotify((SimObject*)obj);
+}
+
+void SceneObjectSet::removeObject(SceneObject* obj)
+{
+	if ( obj == NULL )
+		return;
+
+   mObjectList.remove(obj);
+   clearNotify((SimObject*)obj);
+}
+
+void SceneObjectSet::pushObject(SceneObject* pObj)
+{
+   mObjectList.pushBackForce(pObj);
+   deleteNotify((SimObject*)pObj);
+}
+
+void SceneObjectSet::popObject()
+{
+   MutexHandle handle;
+
+   if (mObjectList.size() == 0) 
+   {
+      AssertWarn(false, "Stack underflow in SceneObjectSet::popObject");
+      return;
+   }
+
+   SceneObject* pObject = mObjectList[mObjectList.size() - 1];
+
+   mObjectList.removeStable(pObject);
+   clearNotify((SimObject*)pObject);
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::addTamlChild( SimObject* pSimObject )
+{
+    // Sanity!
+    AssertFatal( pSimObject != NULL, "SceneObjectSet::addTamlChild() - Cannot add a NULL child object." );
+
+    addObject( dynamic_cast<SceneObject*>(pSimObject) );
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::callOnChildren( const char * method, S32 argc, const char *argv[], bool executeOnChildGroups )
+{
+   // Prep the arguments for the console exec...
+   // Make sure and leave args[1] empty.
+   const char* args[21];
+   args[0] = method;
+   for (S32 i = 0; i < argc; i++)
+      args[i + 2] = argv[i];
+
+   for( iterator i = begin(); i != end(); i++ )
+   {
+      SceneObject *childObj = static_cast<SceneObject*>(*i);
+	  if ( childObj == NULL )
+		  continue;
+
+      if( childObj->isMethod( method ) )
+         Con::execute(childObj, argc + 2, args);
+
+      if( executeOnChildGroups )
+      {
+         SceneObjectSet* childSet = dynamic_cast<SceneObjectSet*>(*i);
+         if ( childSet )
+            childSet->callOnChildren( method, argc, argv, executeOnChildGroups );
+      }
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+bool SceneObjectSet::reOrder( SceneObject *obj, SceneObject *target )
+{
+   iterator itrS, itrD;
+   if ( (itrS = find(begin(),end(),obj)) == end() )
+   {
+      return false;  // object must be in list
+   }
+
+   if ( obj == target )
+   {
+      return true;   // don't reorder same object but don't indicate error
+   }
+
+   if ( !target )    // if no target, then put to back of list
+   {
+      if ( itrS != (end()-1) )      // don't move if already last object
+      {
+         mObjectList.erase(itrS);    // remove object from its current location
+         mObjectList.push_back(obj); // push it to the back of the list
+      }
+   }
+   else              // if target, insert object in front of target
+   {
+      if ( (itrD = find(begin(),end(),target)) == end() )
+         return false;              // target must be in list
+
+      mObjectList.erase(itrS);
+
+      //Tinman - once itrS has been erased, itrD won't be pointing at the same place anymore - re-find...
+      itrD = find(begin(),end(),target);
+      mObjectList.insert(itrD,obj);
+   }
+
+   return true;
+}   
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::onDeleteNotify(SimObject *object)
+{
+   removeObject(dynamic_cast<SceneObject*>(object));
+   Parent::onDeleteNotify(object);
+}
+
+void SceneObjectSet::onRemove()
+{
+   mObjectList.sortId();
+   if (mObjectList.size())
+   {
+      // This backwards iterator loop doesn't work if the
+      // list is empty, check the size first.
+      for (SceneObjectList::iterator ptr = mObjectList.end() - 1;
+         ptr >= mObjectList.begin(); ptr--)
+      {
+         clearNotify(*ptr);
+      }
+   }
+
+   Parent::onRemove();
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::deleteObjects( void )
+{
+        while(size() > 0 )
+        {
+            mObjectList[0]->deleteObject();
+        }
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObjectSet::clear()
+{
+   while (size() > 0)
+      removeObject(mObjectList.last());
+}
+//-----------------------------------------------------------------------------
+
+SimObject *SceneObjectSet::findObject(const char *namePath)
+{
+   // find the end of the object name
+   S32 len;
+   for(len = 0; namePath[len] != 0 && namePath[len] != '/'; len++)
+      ;
+
+   StringTableEntry stName = StringTable->lookupn(namePath, len);
+   if(!stName)
+      return NULL;
+
+   for(SceneObjectSet::iterator i = begin(); i != end(); i++)
+   {
+      if((*i)->getName() == stName)
+      {
+         if(namePath[len] == 0)
+            return *i;
+         return (*i)->findObject(namePath + len + 1);
+      }
+   }
+   return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+SceneObject* SceneObjectSet::findObjectByInternalName(const char* internalName, bool searchChildren)
+{
+   iterator i;
+   for (i = begin(); i != end(); i++)
+   {
+      SceneObject *childObj = static_cast<SceneObject*>(*i);
+      if(childObj->getInternalName() == internalName)
+         return childObj;
+      else if (searchChildren)
+      {
+         SceneObjectSet* childSet = dynamic_cast<SceneObjectSet*>(*i);
+         if (childSet)
+         {
+            SceneObject* found = childSet->findObjectByInternalName(internalName, searchChildren);
+            if (found) return found;
+         }
+      }
+   }
+
+   return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+inline void SceneObjectSetIterator::Stack::push_back(SceneObjectSet* set)
+{
+   increment();
+   last().set = set;
+   last().itr = set->begin();
+}
+
+//-----------------------------------------------------------------------------
+
+SceneObjectSetIterator::SceneObjectSetIterator(SceneObjectSet* set)
+{
+   VECTOR_SET_ASSOCIATION(stack);
+
+   if (!set->empty())
+      stack.push_back(set);
+}
+
+//-----------------------------------------------------------------------------
+
+SceneObject* SceneObjectSetIterator::operator++()
+{
+   SceneObjectSet* set;
+   if ((set = dynamic_cast<SceneObjectSet*>(*stack.last().itr)) != 0) 
+   {
+      if (!set->empty()) 
+      {
+         stack.push_back(set);
+         return *stack.last().itr;
+      }
+   }
+
+   while (++stack.last().itr == stack.last().set->end()) 
+   {
+      stack.pop_back();
+      if (stack.empty())
+         return 0;
+   }
+   return *stack.last().itr;
+}	

+ 172 - 0
engine/source/2d/sceneobject/SceneObjectSet.h

@@ -0,0 +1,172 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SCENE_OBJECT_SET_H_
+#define _SCENE_OBJECT_SET_H_
+
+#ifndef _SCENE_OBJECT_LIST_H_
+#include "2d/sceneObject/sceneObjectList.h"
+#endif
+
+#ifndef _FIND_ITERATOR_H_
+#include "collection/findIterator.h"
+#endif
+
+#ifndef _SIMDICTIONARY_H_
+#include "sim/simDictionary.h"
+#endif
+
+#ifndef _SIM_OBJECT_H_
+#include "sim/simObject.h"
+#endif
+
+#ifndef _TAML_CHILDREN_H_
+#include "persistence/taml/tamlChildren.h"
+#endif
+
+//-----------------------------------------------------------------------------
+
+class SceneObject;
+
+//-----------------------------------------------------------------------------
+
+class SceneObjectSet : public SimObject, public TamlChildren
+{
+   typedef SimObject Parent;
+
+protected:
+    SceneObjectList mObjectList;
+
+public:
+    SceneObjectSet()
+    {
+        VECTOR_SET_ASSOCIATION(mObjectList);
+    }
+
+    ~SceneObjectSet()
+    {
+    }
+
+    typedef SceneObjectList::iterator iterator;
+    typedef SceneObjectList::value_type value;
+
+    inline SceneObject* front( void )    { return mObjectList.front(); }
+    inline SceneObject* first( void )    { return mObjectList.first(); }
+    inline SceneObject* last( void )		{ return mObjectList.last(); }
+    inline bool empty( void )			{ return mObjectList.empty();   }
+    inline S32 size( void ) const		{ return mObjectList.size(); }
+    inline iterator begin( void )		{ return mObjectList.begin(); }
+    inline iterator end( void )			{ return mObjectList.end(); }
+    value operator[] (S32 index)         { return mObjectList[U32(index)]; }
+
+    inline iterator find( iterator first, iterator last, SceneObject *obj ) { return ::find(first, last, obj); }
+    inline iterator find( SceneObject *obj ) { return ::find(begin(), end(), obj); }
+
+    template <typename T> inline bool containsType( void )
+    {
+        for( iterator itr = begin(); itr != end(); ++itr )
+        {
+            if ( dynamic_cast<T*>(*itr) != NULL )
+                return true;
+        }
+
+        return false;
+    }
+
+    virtual bool reOrder( SceneObject *obj, SceneObject *target=0 );
+    SceneObject* at(S32 index) const { return mObjectList.at(index); }
+
+    void deleteObjects( void );
+    void clear();
+
+    virtual void onRemove();
+    virtual void onDeleteNotify(SimObject *object);
+
+    virtual void addObject(SceneObject*);
+    virtual void removeObject(SceneObject*);
+
+    virtual void pushObject(SceneObject*);
+    virtual void popObject();
+
+    void bringObjectToFront(SceneObject* obj) { reOrder(obj, front()); }
+    void pushObjectToBack(SceneObject* obj) { reOrder(obj, NULL); }
+
+    virtual U32 getTamlChildCount( void ) const { return (U32)size(); }
+
+    virtual SimObject* getTamlChild( const U32 childIndex ) const
+    {
+        // Sanity!
+        AssertFatal( childIndex < (U32)size(), "SceneObjectSet::getTamlChild() - Child index is out of range." );
+
+        // For when the assert is not used.
+        if ( childIndex >= (U32)size() )
+            return NULL;
+
+        return (SimObject*)at( childIndex );
+    }
+
+    virtual void addTamlChild( SimObject* pSimObject );
+
+    void callOnChildren( const char * method, S32 argc, const char *argv[], bool executeOnChildGroups = true );
+
+    virtual SimObject *findObject(const char *name);
+    SceneObject*	findObjectByInternalName(const char* internalName, bool searchChildren = false);
+
+    DECLARE_CONOBJECT(SceneObjectSet);
+
+#ifdef TORQUE_DEBUG
+    inline void _setVectorAssoc( const char *file, const U32 line ) { mObjectList.setFileAssociation( file, line ); }
+#endif
+};
+
+#ifdef TORQUE_DEBUG
+#  define SCENEOBJECT_SET_ASSOCIATION( x ) x._setVectorAssoc( __FILE__, __LINE__ )
+#else
+#  define SCENEOBJECT_SET_ASSOCIATION( x )
+#endif
+
+//-----------------------------------------------------------------------------
+
+class SceneObjectSetIterator
+{
+protected:
+    struct Entry
+    {
+        SceneObjectSet* set;
+        SceneObjectSet::iterator itr;
+    };
+
+    class Stack : public Vector<Entry> {
+    public:
+        void push_back(SceneObjectSet*);
+    };
+    Stack stack;
+
+public:
+    SceneObjectSetIterator(SceneObjectSet*);
+    SceneObject* operator++();
+    SceneObject* operator*() {
+        return stack.empty()? 0: *stack.last().itr;
+    }
+};
+
+#endif // _SCENE_OBJECT_SET_H_

+ 194 - 0
engine/source/2d/sceneobject/SceneObjectSet_ScriptBinding.h

@@ -0,0 +1,194 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, dumpObjects, void, 2, 2, "() Dumps the object data within the set\n"
+              "@return No return value")
+{
+   SceneObjectSet::iterator itr;
+   for(itr = object->begin(); itr != object->end(); itr++)
+   {
+      SceneObject *obj = *itr;
+      bool isSet = dynamic_cast<SceneObjectSet *>(obj) != 0;
+      const char *name = obj->getName();
+      if(name)
+         Con::printf("   %d,\"%s\": %s %s", obj->getId(), name,
+         obj->getClassName(), isSet ? "(g)":"");
+      else
+         Con::printf("   %d: %s %s", obj->getId(), obj->getClassName(),
+         isSet ? "(g)" : "");
+   }
+}
+
+ConsoleMethod(SceneObjectSet, add, void, 3, 0, "(obj1,...) Adds given list of objects to the SceneObjectSet.\n"
+              "@param obj_i (i is unilimited) Variable list of objects to add\n"
+              "@return No return value")
+{
+   for(S32 i = 2; i < argc; i++)
+   {
+      SceneObject *obj = Sim::findObject<SceneObject>(argv[i]);
+      if(obj)
+         object->addObject(obj);
+      else
+         Con::printf("Set::add: Object \"%s\" doesn't exist", argv[i]);
+   }
+}
+
+ConsoleMethod(SceneObjectSet, remove, void, 3, 0, "(obj1,...) Removes given listy of objects from the SceneObjectSet.\n"
+              "@param obj_i (i is unilimited) Variable list of objects to remove\n"
+              "@return No return value")
+{
+   for(S32 i = 2; i < argc; i++)
+   {
+      SceneObject *obj = Sim::findObject<SceneObject>(argv[i]);
+      if(obj && object->find(object->begin(),object->end(),obj) != object->end())
+         object->removeObject(obj);
+      else
+         Con::printf("Set::remove: Object \"%s\" does not exist in set", argv[i]);
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, deleteObjects, void, 2, 2,    "() Deletes all the objects in the SceneObjectSet.\n"
+                                                    "@return No return value")
+{
+    object->deleteObjects();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, clear, void, 2, 2, "() Clears the SceneObjectSet\n"
+              "@return No return value")
+{
+   object->clear();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod( SceneObjectSet, callOnChildren, void, 3, 0,
+   "( string method, string args... ) Call a method on all objects contained in the set.\n\n"
+   "@param method The name of the method to call.\n"
+   "@param args The arguments to the method.\n\n"
+   "@note This method recurses into all SimSets that are children to the set.\n\n"
+   "@see callOnChildrenNoRecurse" )
+{
+   object->callOnChildren( argv[2], argc - 3, argv + 3 );
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, reorderChild, void, 4,4," (child1, child2) Uses SceneObjectSet reorder to push child 1 before child 2 - both must already be child controls of this control\n"
+              "@param child1 The child you wish to set first\n"
+              "@param child2 The child you wish to set after child1\n"
+              "@return No return value.")
+{
+   SceneObject* pObject = Sim::findObject<SceneObject>(argv[2]);
+   SceneObject* pTarget	 = Sim::findObject<SceneObject>(argv[3]);
+
+   if(pObject && pTarget)
+   {
+      object->reOrder(pObject,pTarget);
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, getCount, S32, 2, 2, "() @return Returns the number of objects in the SceneObjectSet")
+{
+   return object->size();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, getObject, S32, 3, 3, "(objIndex) @return Returns the ID of the desired object or -1 on failure")
+{
+   S32 objectIndex = dAtoi(argv[2]);
+   if(objectIndex < 0 || objectIndex >= S32(object->size()))
+   {
+      Con::printf("Set::getObject index out of range.");
+      return -1;
+   }
+   return ((*object)[objectIndex])->getId();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, isMember, bool, 3, 3, "(object) @return Returns true if specified object is a member of the set, and false otherwise")
+{
+   SceneObject *testObject = Sim::findObject<SceneObject>(argv[2]);
+   if(!testObject)
+   {
+      Con::printf("SceneObjectSet::isMember: %s is not an object.", argv[2]);
+      return false;
+   }
+
+   for(SceneObjectSet::iterator i = object->begin(); i != object->end(); i++)
+   {
+      if(*i == testObject)
+      {
+         return true;
+      }
+   }
+
+   return false;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod( SceneObjectSet, findObjectByInternalName, S32, 3, 4, "(string name, [bool searchChildren]) Returns the object with given internal name\n"
+              "@param name The internal name of the object you wish to find\n"
+              "@param searchChildren Set this true if you wish to search all children as well.\n"
+              "@return Returns the ID of the object.")
+{
+
+   StringTableEntry pcName = StringTable->insert(argv[2]);
+   bool searchChildren = false;
+   if (argc > 3)
+      searchChildren = dAtob(argv[3]);
+
+   SceneObject* child = object->findObjectByInternalName(pcName, searchChildren);
+   if(child)
+      return child->getId();
+   return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, bringToFront, void, 3, 3, "(object) Brings object to front of set.\n"
+              "@return No return value.")
+{
+   SceneObject *obj = Sim::findObject<SceneObject>(argv[2]);
+   if(!obj)
+      return;
+   object->bringObjectToFront(obj);
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObjectSet, pushToBack, void, 3, 3, "(object) Sends item to back of set.\n"
+              "@return No return value.")
+{
+   SceneObject *obj = Sim::findObject<SceneObject>(argv[2]);
+   if(!obj)
+      return;
+   object->pushObjectToBack(obj);
+}

+ 147 - 155
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -514,24 +514,6 @@ ConsoleMethod(SceneObject, getPosition, const char*, 2, 2, "() Gets the object's
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, getPositionX, F32, 2, 2, "() Gets the object's x position.\n"
-                                                       "@return (float x) The horizontal position of the object")
-{
-    // Get Position X-Component.
-    return object->getPosition().x;
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(SceneObject, getPositionY, F32, 2, 2, "() Gets the object's y position.\n"
-                                                       "@return (float y) The vertical position of the object")
-{
-    // Get Position Y-Component.
-    return object->getPosition().y;
-}
-
-//-----------------------------------------------------------------------------
-
 ConsoleMethod(SceneObject, getRenderPosition, const char*, 2, 2, "() Gets the current render position.\n"
                                                                     "@return (float x/float y) The x and y (horizontal and vertical) render position of the object.")
 {
@@ -982,7 +964,7 @@ ConsoleMethod(SceneObject, getContact, const char*, 3, 3,    "(contactIndex) Get
     }
 
     // Fetch contacts.
-    const typeContactVector* pCurrentContacts = object->getCurrentContacts();
+    const Scene::typeContactVector* pCurrentContacts = object->getCurrentContacts();
 
     // Sanity!
     AssertFatal( pCurrentContacts != NULL, "SceneObject::getContact() - Contacts not initialized correctly." );
@@ -1080,7 +1062,8 @@ ConsoleMethod(SceneObject, setCollisionMasks, void, 3, 4,    "(groupMask, [layer
     const U32 layerMask = (argc > 3) ? dAtoi(argv[3]) : MASK_ALL;
 
     // Set Collision Masks.
-    object->setCollisionMasks( groupMask, layerMask );
+    object->setCollisionGroupMask( groupMask );
+    object->setCollisionLayerMask( layerMask );
 }
 
 //-----------------------------------------------------------------------------
@@ -1125,121 +1108,121 @@ ConsoleMethod(SceneObject, setCollisionAgainst, void, 3, 4, "(SceneObject object
 //-----------------------------------------------------------------------------
 
 ConsoleMethod(SceneObject, setCollisionLayers, void, 3, 2 + MASK_BITCOUNT, "(layers$) - Sets the collision layers(s).\n"
-                                                                                  "@param layers A list of layers numbers to collide with.\n"
+                                                                                  "@param layers A list of layers to collide with.\n"
                                                       "@return No return value.")
 {
-   // The mask.
-   U32 mask = 0;
+    // The mask.
+    U32 mask = 0;
 
-   // Grab the element count of the first parameter.
-   const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+    // Grab the element count of the first parameter.
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
 
-   // Make sure we get at least one number.
-   if (elementCount < 1)
-   {
-      object->setCollisionLayers(0);
-      return;
-   }
+    // Make sure we get at least one number.
+    if (elementCount < 1)
+    {
+        object->setCollisionLayerMask(MASK_ALL);
+        return;
+    }
 
-   // Space separated list.
-   if (argc == 3)
-   {
-      // Convert the string to a mask.
-      for (U32 i = 0; i < elementCount; i++)
-      {
-         S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
+    // Space separated list.
+    if (argc == 3)
+    {
+        // Convert the string to a mask.
+        for (U32 i = 0; i < elementCount; i++)
+        {
+            S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
          
-         // Make sure the group is valid.
-         if ((bit < 0) || (bit >= MASK_BITCOUNT))
-         {
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
             Con::warnf("SceneObject::setCollisionLayers() - Invalid layer specified (%d); skipped!", bit);
             continue;
-         }
+            }
          
-         mask |= (1 << bit);
-      }
-   }
+            mask |= (1 << bit);
+        }
+    }
 
-   // Comma separated list.
-   else
-   {
-      // Convert the list to a mask.
-      for (U32 i = 2; i < (U32)argc; i++)
-      {
-         S32 bit = dAtoi(argv[i]);
+    // Comma separated list.
+    else
+    {
+        // Convert the list to a mask.
+        for (U32 i = 2; i < (U32)argc; i++)
+        {
+            S32 bit = dAtoi(argv[i]);
          
-         // Make sure the group is valid.
-         if ((bit < 0) || (bit >= MASK_BITCOUNT))
-         {
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
             Con::warnf("SceneObject::setCollisionLayers() - Invalid layer specified (%d); skipped!", bit);
             continue;
-         }
+            }
 
-         mask |= (1 << bit);
-      }
-   }
-   // Set Collision Groups.
-   object->setCollisionLayers(mask);
+            mask |= (1 << bit);
+        }
+    }
+    // Set Collision Layers
+    object->setCollisionLayerMask(mask);
 }
 
 //-----------------------------------------------------------------------------
 
 ConsoleMethod(SceneObject, setCollisionGroups, void, 3, 2 + MASK_BITCOUNT, "(groups$) - Sets the collision group(s).\n"
-                                                                                  "@param groups A list of collision group numbers to collide with.\n"
+                                                                                  "@param groups A list of collision groups to collide with.\n"
                                                                                 "@return No return value.")
 {
-   // The mask.
-   U32 mask = 0;
+    // The mask.
+    U32 mask = 0;
 
-   // Grab the element count of the first parameter.
-   const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+    // Grab the element count of the first parameter.
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
 
-   // Make sure we get at least one number.
-   if (elementCount < 1)
-   {
-      object->setCollisionGroups(0);
-      return;
-   }
+    // Make sure we get at least one number.
+    if (elementCount < 1)
+    {
+        object->setCollisionGroupMask(MASK_ALL);
+        return;
+    }
 
-   // Space separated list.
-   if (argc == 3)
-   {
-      // Convert the string to a mask.
-      for (U32 i = 0; i < elementCount; i++)
-      {
-         S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
+    // Space separated list.
+    if (argc == 3)
+    {
+        // Convert the string to a mask.
+        for (U32 i = 0; i < elementCount; i++)
+        {
+            S32 bit = dAtoi(Utility::mGetStringElement(argv[2], i));
          
-         // Make sure the group is valid.
-         if ((bit < 0) || (bit >= MASK_BITCOUNT))
-         {
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
             Con::warnf("SceneObject::setCollisionGroups() - Invalid group specified (%d); skipped!", bit);
             continue;
-         }
+            }
          
-         mask |= (1 << bit);
-      }
-   }
+            mask |= (1 << bit);
+        }
+    }
 
-   // Comma separated list.
-   else
-   {
-      // Convert the list to a mask.
-      for (U32 i = 2; i < (U32)argc; i++)
-      {
-         S32 bit = dAtoi(argv[i]);
+    // Comma separated list.
+    else
+    {
+        // Convert the list to a mask.
+        for (U32 i = 2; i < (U32)argc; i++)
+        {
+            S32 bit = dAtoi(argv[i]);
          
-         // Make sure the group is valid.
-         if ((bit < 0) || (bit >= MASK_BITCOUNT))
-         {
+            // Make sure the group is valid.
+            if ((bit < 0) || (bit >= MASK_BITCOUNT))
+            {
             Con::warnf("SceneObject::setCollisionGroups() - Invalid group specified (%d); skipped!", bit);
             continue;
-         }
+            }
 
-         mask |= (1 << bit);
-      }
-   }
-   // Set Collision Groups.
-   object->setCollisionGroups(mask);
+            mask |= (1 << bit);
+        }
+    }
+    // Set Collision Groups.
+    object->setCollisionGroupMask(mask);
 }
 
 //-----------------------------------------------------------------------------
@@ -1247,23 +1230,23 @@ ConsoleMethod(SceneObject, setCollisionGroups, void, 3, 2 + MASK_BITCOUNT, "(gro
 ConsoleMethod(SceneObject, getCollisionLayers, const char*, 2, 2, "() - Gets the collision layers.\n"
                                                                      "@return (collisionLayers) A list of collision layers.")
 {
-   U32 mask = object->getCollisionLayerMask();
+    U32 mask = object->getCollisionLayerMask();
 
-   bool first = true;
-   char* bits = Con::getReturnBuffer(128);
-   bits[0] = '\0';
-   for (S32 i = 0; i < MASK_BITCOUNT; i++)
-   {
-      if (mask & BIT(i))
-      {
-         char bit[4];
-         dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
-         first = false;
-         dStrcat(bits, bit);
-      }
-   }
+    bool first = true;
+    char* bits = Con::getReturnBuffer(128);
+    bits[0] = '\0';
+    for (S32 i = 0; i < MASK_BITCOUNT; i++)
+    {
+        if (mask & BIT(i))
+        {
+            char bit[4];
+            dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
+            first = false;
+            dStrcat(bits, bit);
+        }
+    }
 
-   return bits;
+    return bits;
 }
 
 //-----------------------------------------------------------------------------
@@ -1271,23 +1254,23 @@ ConsoleMethod(SceneObject, getCollisionLayers, const char*, 2, 2, "() - Gets the
 ConsoleMethod(SceneObject, getCollisionGroups, const char*, 2, 2, "() - Gets the collision groups.\n"
                                                                      "@return (collisionGroups) A list of collision groups.")
 {
-   U32 mask = object->getCollisionGroupMask();
+    U32 mask = object->getCollisionGroupMask();
 
-   bool first = true;
-   char* bits = Con::getReturnBuffer(128);
-   bits[0] = '\0';
-   for (S32 i = 0; i < MASK_BITCOUNT; i++)
-   {
-      if (mask & BIT(i))
-      {
-         char bit[4];
-         dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
-         first = false;
-         dStrcat(bits, bit);
-      }
-   }
+    bool first = true;
+    char* bits = Con::getReturnBuffer(128);
+    bits[0] = '\0';
+    for (S32 i = 0; i < MASK_BITCOUNT; i++)
+    {
+        if (mask & BIT(i))
+        {
+            char bit[4];
+            dSprintf(bit, 4, "%s%d", first ? "" : " ", i);
+            first = false;
+            dStrcat(bits, bit);
+        }
+    }
 
-   return bits;
+    return bits;
 }
 
 //-----------------------------------------------------------------------------
@@ -1617,7 +1600,7 @@ ConsoleMethod(SceneObject, getAngularDamping, F32, 2, 2, "() - Gets the angular
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, moveTo, bool, 4, 7,  "(worldPoint X/Y, time, [autoStop = true], [warpToTarget = true]) - Moves the object to the specified world point.\n"
+ConsoleMethod(SceneObject, moveTo, bool, 4, 7,  "(worldPoint X/Y, speed, [autoStop = true], [warpToTarget = true]) - Moves the object to the specified world point.\n"
                                                 "The point is moved by calculating the initial linear velocity required and applies it.\n"
                                                 "The object may never reach the point if it has linear damping applied or collides with another object.\n"
                                                 "@param worldPoint/Y The world point to move the object to.\n"
@@ -2380,13 +2363,23 @@ ConsoleMethod( SceneObject, createPolygonCollisionShape, S32, 3, 3,  "(localPoin
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod( SceneObject, createPolygonBoxCollisionShape, S32, 3, 7,  "(width, height, [localCentroidX, localCentroidY], [angle]) Creates a polygon box collision shape.\n"
+ConsoleMethod( SceneObject, createPolygonBoxCollisionShape, S32, 2, 7,  "(width, height, [localCentroidX, localCentroidY], [angle]) Creates a polygon box collision shape.\n"
                                                                             "@param width The width of the box."
                                                                             "@param height The height of the box."
                                                                             "@param localCentroidX/Y The local position of the box centroid."
                                                                             "@param angle The angle of the box."
                                                                             "@return (int shapeIndex) The index of the collision shape or (-1) if not created.")
 {
+    // Were any dimensions specified?
+    if( argc == 2 )
+    {
+        // No, so fetch the objects size.
+        const Vector2& size = object->getSize();
+
+        // Create a box surrounding the object.
+        return object->createPolygonBoxCollisionShape( size.x, size.y );
+    }
+
     // Width and height.
     const U32 widthHeightElementCount = Utility::mGetStringElementCount(argv[2]);
 
@@ -3678,18 +3671,6 @@ ConsoleMethod(SceneObject, detachGui, void, 2, 2, "() - Detach any GUI Control.\
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, getAttachedToPath, S32, 2, 2, "() - Gets the Path that this object is attached to.\n"
-                                                            "@return (Path path) The path that this object is attached to, or 0 if it is not attached to a path.")
-{
-   SceneObject* path = object->getAttachedToPath();
-   if (path)
-      return path->getId();
-   
-   return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
 ConsoleMethod(SceneObject, copyFrom, bool, 3, 4, "(SceneObject object, [copyDynamicFields? = false]) - Copies one scene object from another scene object.\n"
                                                     "The object being copied to needs to be of the same class as the object being copied from.\n"
                                                     "@param object The SceneObject to copy this object to.\n"
@@ -3718,19 +3699,30 @@ ConsoleMethod(SceneObject, copyFrom, bool, 3, 4, "(SceneObject object, [copyDyna
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, safeDelete, void, 2, 2, "() - Safely deletes object.\n"
-                                                                 "@return No return Value.")
+ConsoleMethod(SceneObject, setPickingAllowed, void, 3, 3,   "(bool pickingAllowed) - Sets whether picking is allowed or not.\n"
+                                                            "@param pickingAllowed Whether picking is allowed or not.\n"
+                                                            "@return No return Value.")
 {
-    // Script Delete.
-    object->safeDelete();
+    // Fetch flag.
+    const bool pickingAllowed = dAtob(argv[2]);
+
+    object->setPickingAllowed( pickingAllowed );
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SceneObject, getPickingAllowed, bool, 2, 2,   "() - Gets whether picking is allowed or not.\n"
+                                                            "@return Whether picking is allowed or not.")
+{
+    return object->getPickingAllowed();
 }
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, behavior, S32, 3, 3, "(string behaviorName) - Gets the behavior instance ID off of the object based on the behavior name passed.\n"
-                                                   "@param behaviorName The name of the behavior you want to get the instance ID of.\n"
-                                                   "@return (integer behaviorID) The id of the behavior instance.")
+ConsoleMethod(SceneObject, safeDelete, void, 2, 2, "() - Safely deletes object.\n"
+                                                                 "@return No return Value.")
 {
-   BehaviorInstance *inst = object->behavior(argv[2]);
-   return inst ? inst->getId() : 0;
+    // Script Delete.
+    object->safeDelete();
 }
+

+ 2 - 2
engine/source/2d/sceneobject/Trigger.cc

@@ -115,7 +115,7 @@ void Trigger::integrateObject( const F32 totalTime, const F32 elapsedTime, Debug
     }
 
     // Fetch current contacts.
-    const typeContactVector* pCurrentContacts = getCurrentContacts();
+    const Scene::typeContactVector* pCurrentContacts = getCurrentContacts();
 
     // Sanity!
     AssertFatal( pCurrentContacts != NULL, "Trigger::integrateObject() - Contacts not initialized correctly." );
@@ -126,7 +126,7 @@ void Trigger::integrateObject( const F32 totalTime, const F32 elapsedTime, Debug
         // Debug Profiling.
         PROFILE_SCOPE(Trigger_OnStayCallback);
 
-        for ( typeContactVector::const_iterator contactItr = pCurrentContacts->begin(); contactItr != pCurrentContacts->end(); ++contactItr )
+        for ( Scene::typeContactVector::const_iterator contactItr = pCurrentContacts->begin(); contactItr != pCurrentContacts->end(); ++contactItr )
         {
             // Fetch colliding object.
             SceneObject* pCollideWidth = contactItr->getCollideWith( this );

+ 33 - 30
engine/source/algorithm/hashFunction.cc

@@ -34,8 +34,8 @@
 
 #include "algorithm/hashFunction.h"
 
-#define hashsize(n) ((U32)1<<(n))
-#define hashmask(n) (hashsize(n)-1)
+#define hashsize(n) ((U32)1 << (n))
+#define hashmask(n) (hashsize(n) - 1)
 
 /*
 --------------------------------------------------------------------
@@ -65,15 +65,15 @@ to choose from.  I only looked at a billion or so.
 */
 #define mix(a,b,c) \
 { \
-   a -= b; a -= c; a ^= (c>>13); \
-   b -= c; b -= a; b ^= (a<<8); \
-   c -= a; c -= b; c ^= (b>>13); \
-   a -= b; a -= c; a ^= (c>>12);  \
-   b -= c; b -= a; b ^= (a<<16); \
-   c -= a; c -= b; c ^= (b>>5); \
-   a -= b; a -= c; a ^= (c>>3);  \
-   b -= c; b -= a; b ^= (a<<10); \
-   c -= a; c -= b; c ^= (b>>15); \
+   a -= b; a -= c; a ^= (c >> 13); \
+   b -= c; b -= a; b ^= (a << 8); \
+   c -= a; c -= b; c ^= (b >> 13); \
+   a -= b; a -= c; a ^= (c >> 12);  \
+   b -= c; b -= a; b ^= (a << 16); \
+   c -= a; c -= b; c ^= (b >> 5); \
+   a -= b; a -= c; a ^= (c >> 3);  \
+   b -= c; b -= a; b ^= (a << 10); \
+   c -= a; c -= b; c ^= (b >> 15); \
 }
 
 /*
@@ -109,7 +109,7 @@ U32 hash(register U8 *k, register U32 length, register U32 initval)
     // Debug Profiling.
     PROFILE_SCOPE(HashFunction);
 
-   register U32 a,b,c,len;
+   register U32 a, b, c, len;
 
    /* Set up the internal state */
    len = length;
@@ -119,32 +119,35 @@ U32 hash(register U8 *k, register U32 length, register U32 initval)
    /*---------------------------------------- handle most of the key */
    while (len >= 12)
    {
-      a += (k[0] +((U32)k[1]<<8) +((U32)k[2]<<16) +((U32)k[3]<<24));
-      b += (k[4] +((U32)k[5]<<8) +((U32)k[6]<<16) +((U32)k[7]<<24));
-      c += (k[8] +((U32)k[9]<<8) +((U32)k[10]<<16)+((U32)k[11]<<24));
-      mix(a,b,c);
-      k += 12; len -= 12;
+      a += (k[0] + ((U32)k[1] << 8) + ((U32)k[ 2] << 16) + ((U32)k[ 3] << 24));
+      b += (k[4] + ((U32)k[5] << 8) + ((U32)k[ 6] << 16) + ((U32)k[ 7] << 24));
+      c += (k[8] + ((U32)k[9] << 8) + ((U32)k[10] << 16) + ((U32)k[11] << 24));
+      mix(a, b, c);
+      k += 12;
+      len -= 12;
    }
 
    /*------------------------------------- handle the last 11 bytes */
    c += length;
-   switch(len)              /* all the case statements fall through */
+   
+   switch (len)              /* all the case statements fall through */
    {
-   case 11: c+=((U32)k[10]<<24);
-   case 10: c+=((U32)k[9]<<16);
-   case 9 : c+=((U32)k[8]<<8);
+      case 11: c += ((U32)k[10] << 24);
+      case 10: c += ((U32)k[9] << 16);
+      case 9 : c += ((U32)k[8] << 8);
       /* the first byte of c is reserved for the length */
-   case 8 : b+=((U32)k[7]<<24);
-   case 7 : b+=((U32)k[6]<<16);
-   case 6 : b+=((U32)k[5]<<8);
-   case 5 : b+=k[4];
-   case 4 : a+=((U32)k[3]<<24);
-   case 3 : a+=((U32)k[2]<<16);
-   case 2 : a+=((U32)k[1]<<8);
-   case 1 : a+=k[0];
+      case 8 : b += ((U32)k[7] << 24);
+      case 7 : b += ((U32)k[6] << 16);
+      case 6 : b += ((U32)k[5] << 8);
+      case 5 : b += k[4];
+      case 4 : a += ((U32)k[3] << 24);
+      case 3 : a += ((U32)k[2] << 16);
+      case 2 : a += ((U32)k[1] << 8);
+      case 1 : a += k[0];
       /* case 0: nothing left to add */
    }
-   mix(a,b,c);
+   
+   mix(a, b, c);
    /*-------------------------------------------- report the result */
    return c;
 }

+ 26 - 23
engine/source/assets/assetQuery.cc

@@ -51,13 +51,13 @@ void AssetQuery::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-void AssetQuery::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void AssetQuery::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
-    // Add property.
-    TamlCustomProperty* pProperty = customProperties.addProperty( ASSETQUERY_CUSTO_PROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pCustomNode = customNodes.addNode( ASSETQUERY_RESULTS_NODE_NAME );
 
     // Finish if no assets.
     if ( size() == 0 )
@@ -66,43 +66,46 @@ void AssetQuery::onTamlCustomWrite( TamlCustomProperties& customProperties )
     // Iterate asset.
     for( Vector<StringTableEntry>::iterator assetItr = begin(); assetItr != end(); ++assetItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pProperty->addAlias( ASSETQUERY_TYPE_NAME );
+        // Add asset node.
+        TamlCustomNode* pAssetNode = pCustomNode->addNode( ASSETQUERY_ASSETNODE_NAME );
 
-        // Add fields.
-        pAlias->addField( ASSETQUERY_ASSETID_FIELD_NAME, *assetItr );
+        // Add field.
+        pAssetNode->addField( ASSETQUERY_ASSETID_FIELD_NAME, *assetItr );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void AssetQuery::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void AssetQuery::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find custom property name.
-    const TamlCustomProperty* pProperty = customProperties.findProperty( ASSETQUERY_CUSTO_PROPERTY_NAME );
+    // Find custom node name.
+    const TamlCustomNode* pResultsNode = customNodes.findNode( ASSETQUERY_RESULTS_NODE_NAME );
 
-    // Finish if we don't have a property name.
-    if ( pProperty == NULL )
+    // Finish if we don't have a results name.
+    if ( pResultsNode == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry typeAliasName = StringTable->insert( ASSETQUERY_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry assetNodeName = StringTable->insert( ASSETQUERY_ASSETNODE_NAME );
 
-    // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pProperty->begin(); propertyAliasItr != pProperty->end(); ++propertyAliasItr )
+    // Fetch children asset nodes.
+    const TamlCustomNodeVector& assetNodes = pResultsNode->getChildren();
+
+    // Iterate asset nodes.
+    for( TamlCustomNodeVector::const_iterator assetNodeItr = assetNodes.begin(); assetNodeItr != assetNodes.end(); ++assetNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch asset node.
+        const TamlCustomNode* pAssetNode = *assetNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != typeAliasName )
+        // Skip if an unknown node name.
+        if ( pAssetNode->getNodeName() != assetNodeName )
             continue;
 
         // Fetch field.
-        const TamlPropertyField* pField = pAlias->findField( ASSETQUERY_ASSETID_FIELD_NAME );
+        const TamlCustomField* pField = pAssetNode->findField( ASSETQUERY_ASSETID_FIELD_NAME );
 
         // Do we find the field?
         if ( pField == NULL )

+ 4 - 4
engine/source/assets/assetQuery.h

@@ -37,8 +37,8 @@
 
 //-----------------------------------------------------------------------------
 
-#define ASSETQUERY_CUSTO_PROPERTY_NAME      "Results"
-#define ASSETQUERY_TYPE_NAME            "Asset"
+#define ASSETQUERY_RESULTS_NODE_NAME     "Results"
+#define ASSETQUERY_ASSETNODE_NAME        "Asset"
 #define ASSETQUERY_ASSETID_FIELD_NAME   "AssetId"
 
 //-----------------------------------------------------------------------------
@@ -49,8 +49,8 @@ private:
     typedef SimObject Parent;
 
 protected:
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
     static const char* getCount(void* obj, const char* data) { return Con::getIntArg(static_cast<AssetQuery*>(obj)->size()); }
     static bool writeCount( void* obj, StringTableEntry pFieldName ) { return false; }

+ 45 - 39
engine/source/assets/assetTagsManifest.cc

@@ -118,72 +118,75 @@ void AssetTagsManifest::renameAssetId( const char* pAssetIdFrom, const char* pAs
 
 //-----------------------------------------------------------------------------
 
-void AssetTagsManifest::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void AssetTagsManifest::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Finish if no tags.
     if ( mTagNameDatabase.size() == 0 )
         return;
 
-    // Add property.
-    TamlCustomProperty* pTagProperty = customProperties.addProperty( ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pTagsNode = customNodes.addNode( ASSETTAGS_TAGS_NODE_NAME );
 
     // Iterate tags.
     for( typeTagNameHash::iterator tagItr = mTagNameDatabase.begin(); tagItr != mTagNameDatabase.end(); ++tagItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pTagProperty->addAlias( ASSETTAGS_TAGS_TYPE_NAME );
+        // Add tag node.
+        TamlCustomNode* pTagNode = pTagsNode->addNode( ASSETTAGS_TAGS_TYPE_NAME );
 
         // Add fields.
-        pAlias->addField( ASSETTAGS_TAGS_NAME_FIELD, tagItr->key );
+        pTagNode->addField( ASSETTAGS_TAGS_NAME_FIELD, tagItr->key );
     }
 
-    // Add property.
-    TamlCustomProperty* pAssetTagProperty = customProperties.addProperty( ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pAssetTagsNode = customNodes.addNode( ASSETTAGS_ASSETS_NODE_NAME );
 
     // Iterate asset locations.
     for( typeAssetToTagHash::iterator assetTagItr = mAssetToTagDatabase.begin(); assetTagItr != mAssetToTagDatabase.end(); ++assetTagItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pAssetTagProperty->addAlias( ASSETTAGS_ASSETS_TYPE_NAME );
+        // Add asset tag node.
+        TamlCustomNode* pAssetNode = pAssetTagsNode->addNode( ASSETTAGS_ASSETS_TYPE_NAME );
 
         // Add fields.
-        pAlias->addField( ASSETTAGS_ASSETS_ASSETID_FIELD, assetTagItr->key );
-        pAlias->addField( ASSETTAGS_ASSETS_TAG_FIELD, assetTagItr->value->mTagName );
+        pAssetNode->addField( ASSETTAGS_ASSETS_ASSETID_FIELD, assetTagItr->key );
+        pAssetNode->addField( ASSETTAGS_ASSETS_TAG_FIELD, assetTagItr->value->mTagName );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void AssetTagsManifest::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find custom property name.
-    const TamlCustomProperty* pTagProperty = customProperties.findProperty( ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME );
+    // Find tags nodes.
+    const TamlCustomNode* pTagProperty = customNodes.findNode( ASSETTAGS_TAGS_NODE_NAME );
 
-    // Finish if we don't have a property name.
+    // Finish if we don't have a tags node name.
     if ( pTagProperty == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry tagAliasName = StringTable->insert( ASSETTAGS_TAGS_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry tagNodeName = StringTable->insert( ASSETTAGS_TAGS_TYPE_NAME );
 
-    // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pTagProperty->begin(); propertyAliasItr != pTagProperty->end(); ++propertyAliasItr )
+    // Fetch children asset nodes.
+    const TamlCustomNodeVector& tagNodes = pTagProperty->getChildren();
+
+    // Iterate tag nodes.
+    for( TamlCustomNodeVector::const_iterator tagNodeItr = tagNodes.begin(); tagNodeItr != tagNodes.end(); ++tagNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch tag node.
+        const TamlCustomNode* pTagNode = *tagNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != tagAliasName )
+        // Skip if an unknown node name.
+        if ( pTagNode->getNodeName() != tagNodeName )
             continue;
 
         // Fetch "Name" field.
-        const TamlPropertyField* pTagNameField = pAlias->findField( ASSETTAGS_TAGS_NAME_FIELD );
+        const TamlCustomField* pTagNameField = pTagNode->findField( ASSETTAGS_TAGS_NAME_FIELD );
 
         // Do we find the field?
         if ( pTagNameField == NULL )
@@ -197,28 +200,31 @@ void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProp
         createTag( pTagNameField->getFieldValue() );
     }
 
-    // Find custom property name.
-    const TamlCustomProperty* pAssetTagProperty = customProperties.findProperty( ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME );
+    // Find asset tags node.
+    const TamlCustomNode* pAssetTagProperty = customNodes.findNode( ASSETTAGS_ASSETS_NODE_NAME );
 
-    // Finish if we don't have a property name.
+    // Finish if we don't have an asset tags node name.
     if ( pAssetTagProperty == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry assetTagAliasName = StringTable->insert( ASSETTAGS_ASSETS_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry assetTagNodeName = StringTable->insert( ASSETTAGS_ASSETS_TYPE_NAME );
+
+    // Fetch children asset tag nodes.
+    const TamlCustomNodeVector& assetTagNodes = pAssetTagProperty->getChildren();
 
     // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pAssetTagProperty->begin(); propertyAliasItr != pAssetTagProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator assetTagNodeItr = assetTagNodes.begin(); assetTagNodeItr != assetTagNodes.end(); ++assetTagNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch asset node.
+        const TamlCustomNode* pAssetTagNode = *assetTagNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != assetTagAliasName )
+        // Skip if an unknown node name.
+        if ( pAssetTagNode->getNodeName() != assetTagNodeName )
             continue;
 
         // Fetch "AssetId" field.
-        const TamlPropertyField* pAssetIdField = pAlias->findField( ASSETTAGS_ASSETS_ASSETID_FIELD );
+        const TamlCustomField* pAssetIdField = pAssetTagNode->findField( ASSETTAGS_ASSETS_ASSETID_FIELD );
 
         // Do we find the field?
         if ( pAssetIdField == NULL )
@@ -229,7 +235,7 @@ void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProp
         }
 
         // Fetch "Tag" field.
-        const TamlPropertyField* pTagField = pAlias->findField( ASSETTAGS_ASSETS_TAG_FIELD );
+        const TamlCustomField* pTagField = pAssetTagNode->findField( ASSETTAGS_ASSETS_TAG_FIELD );
 
         // Do we find the field?
         if ( pTagField == NULL )

+ 4 - 4
engine/source/assets/assetTagsManifest.h

@@ -41,11 +41,11 @@
 
 //-----------------------------------------------------------------------------
 
-#define ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME      "Tags"
+#define ASSETTAGS_TAGS_NODE_NAME                "Tags"
 #define ASSETTAGS_TAGS_TYPE_NAME                "Tag"
 #define ASSETTAGS_TAGS_NAME_FIELD               "Name"
 
-#define ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME    "Assets"
+#define ASSETTAGS_ASSETS_NODE_NAME              "Assets"
 #define ASSETTAGS_ASSETS_TYPE_NAME              "Tag"
 #define ASSETTAGS_ASSETS_ASSETID_FIELD          "AssetId"
 #define ASSETTAGS_ASSETS_TAG_FIELD              "Name"
@@ -120,8 +120,8 @@ private:
     void renameAssetId( const char* pAssetIdFrom, const char* pAssetIdTo );
 
 protected:
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     AssetTagsManifest();

+ 13 - 0
engine/source/audio/audioFunctions.cc

@@ -726,6 +726,19 @@ ConsoleFunction(stopiOSAudioStream, void, 2, 2, "( streamId ) - Stops playing th
     }
 }
 
+ConsoleFunction(setiOSAudioStreamVolume, void, 3, 3, "setiPhoneAudioVolume( Stream ID, float volume )" )
+{
+    SimObjectId streamId = dAtoi( argv[1] );
+    iOSStreamSource* pStream = Sim::findObject<iOSStreamSource>( streamId );
+    
+    F32 volume = dAtof( argv[2] );
+    
+    if( pStream ) {
+        if( pStream->isPlaying() ) {
+            pStream->setVolume(volume);
+        }
+    }
+}
 #endif
 
 

+ 120 - 8
engine/source/collection/vector.h

@@ -79,7 +79,10 @@ class Vector
    U32         mLineAssociation;
 #endif
 
-   bool  resize(U32);
+   bool  resize(U32); // resizes, but does no construction/destruction
+   void  destroy(U32 start, U32 end);   ///< Destructs elements from <i>start</i> to <i>end-1</i>
+   void  construct(U32 start, U32 end); ///< Constructs elements from <i>start</i> to <i>end-1</i>
+   void  construct(U32 start, U32 end, const T* array);
   public:
    Vector(const U32 initialSize = 0);
    Vector(const U32 initialSize, const char* fileName, const U32 lineNum);
@@ -103,6 +106,8 @@ class Vector
    typedef S32    difference_type;
    typedef U32    size_type;
 
+   typedef difference_type (QSORT_CALLBACK *compare_func)(const T *a, const T *b);
+
    Vector<T>& operator=(const Vector<T>& p);
 
    iterator       begin();
@@ -112,6 +117,7 @@ class Vector
 
    S32 size() const;
    bool empty() const;
+   bool contains(const T&) const;
 
    void insert(iterator, const T&);
    void erase(iterator);
@@ -123,6 +129,9 @@ class Vector
 
    void push_front(const T&);
    void push_back(const T&);
+   U32 push_front_unique(const T&);
+   U32 push_back_unique(const T&);
+   S32 find_next( const T&, U32 start = 0 ) const;
    void pop_front();
    void pop_back();
 
@@ -146,7 +155,8 @@ class Vector
    U32  memSize() const;
    T*   address() const;
    U32  setSize(U32);
-   void increment(U32 = 1);
+   void increment( U32 = 1);
+   void increment(const T* array, U32 = 1);
    void decrement(U32 = 1);
    void insert(U32);
    void erase(U32);
@@ -154,7 +164,7 @@ class Vector
    void erase_fast(iterator);
    void clear();
    void compact();
-
+   void sort(compare_func f);
    T& first();
    T& last();
    const T& first() const;
@@ -246,6 +256,33 @@ template<class T> inline void Vector<T>::setFileAssociation(const char* file,
 }
 #endif
 
+template<class T> inline void  Vector<T>::destroy(U32 start, U32 end) // destroys from start to end-1
+{
+   // This check is a little generous as we can legitimately get (0,0) as
+   // our parameters... so it won't detect every invalid case but it does
+   // remain simple.
+   AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::destroy - out of bounds start/end.");
+
+   // destroys from start to end-1
+   while(start < end)
+      destructInPlace(&mArray[start++]);
+}
+
+template<class T> inline void  Vector<T>::construct(U32 start, U32 end) // destroys from start to end-1
+{
+   AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::construct - out of bounds start/end.");
+   while(start < end)
+      constructInPlace(&mArray[start++]);
+}
+
+template<class T> inline void  Vector<T>::construct(U32 start, U32 end, const T* array) // destroys from start to end-1
+{
+   AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::construct - out of bounds start/end.");
+    for (int i = 0; start + i < end; i++) {
+      constructInPlace(&mArray[start+i], &array[i]);
+    }
+}
+
 template<class T> inline U32 Vector<T>::memSize() const
 {
    return capacity() * sizeof(T);
@@ -265,18 +302,40 @@ template<class T> inline U32 Vector<T>::setSize(U32 size)
    return mElementCount;
 }
 
-template<class T> inline void Vector<T>::increment(U32 delta)
+template<class T> inline void Vector<T>::increment( U32 delta)
 {
+    U32 count = mElementCount;
+    if ((mElementCount += delta) > mArraySize)
+        resize(mElementCount);
+    construct(count, mElementCount);
+}
+
+template<class T> inline void Vector<T>::increment(const T* array, U32 delta)
+{
+   U32 count = mElementCount;
    if ((mElementCount += delta) > mArraySize)
       resize(mElementCount);
+    construct(count, mElementCount, array);
 }
 
 template<class T> inline void Vector<T>::decrement(U32 delta)
 {
+   AssertFatal(mElementCount != 0, "Vector<T>::decrement - cannot decrement zero-length vector.");
+
+   const U32 count = mElementCount;
+
+   // Determine new count after decrement...
+   U32 newCount = mElementCount;
    if (mElementCount > delta)
-      mElementCount -= delta;
+      newCount -= delta;
    else
-      mElementCount = 0;
+      newCount = 0;
+
+   // Destruct removed items...
+   destroy(newCount, count);
+
+   // Note new element count.
+   mElementCount = newCount;
 }
 
 template<class T> inline void Vector<T>::insert(U32 index)
@@ -349,6 +408,12 @@ template<class T> inline void Vector<T>::compact()
    resize(mElementCount);
 }
 
+typedef int (QSORT_CALLBACK *qsort_compare_func)(const void *, const void *);
+
+template<class T> inline void Vector<T>::sort(compare_func f)
+{
+   qsort(address(), size(), sizeof(T), (qsort_compare_func) f);
+}
 
 //-----------------------------------------------------------------------------
 
@@ -438,8 +503,55 @@ template<class T> inline void Vector<T>::push_front(const T& x)
 
 template<class T> inline void Vector<T>::push_back(const T& x)
 {
-   increment();
-   mArray[mElementCount - 1] = x;
+   increment(&x);
+//   mArray[mElementCount - 1] = x;
+}
+
+template<class T> inline U32 Vector<T>::push_front_unique(const T& x)
+{
+   S32 index = find_next(x);
+
+   if (index == -1)
+   {
+      index = 0;
+
+      insert(index);
+      mArray[index] = x;
+   }
+
+   return index;
+}
+
+template<class T> inline U32 Vector<T>::push_back_unique(const T& x)
+{
+   S32 index = find_next(x);
+
+   if (index == -1)
+   {
+      increment(&x);
+      index = mElementCount - 1;
+   }
+
+   return index;
+}
+
+template<class T> inline S32 Vector<T>::find_next( const T& x, U32 start ) const
+{
+   S32 index = -1;
+
+   if (start < mElementCount)
+   {
+      for (U32 i = start; i < mElementCount; i++)
+      {
+         if (mArray[i] == x)
+         {
+            index = i;
+            break;
+         }
+      }
+   }
+
+   return index;
 }
 
 template<class T> inline void Vector<T>::pop_front()

+ 65 - 55
engine/source/component/behaviors/behaviorComponent.cpp

@@ -886,10 +886,10 @@ const BehaviorComponent::typePortConnectionVector* BehaviorComponent::getBehavio
 
 //-----------------------------------------------------------------------------
 
-void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void BehaviorComponent::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch behavior count.
     const U32 behaviorCount = (U32)mBehaviors.size();
@@ -901,8 +901,8 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
     // Fetch behavior template asset field type.
     StringTableEntry behaviorTemplateAssetFieldType = StringTable->insert( BEHAVIORTEMPLATE_ASSET_FIELDTYPE );
 
-    // Add behavior property.
-    TamlCustomProperty* pBehaviorProperty = customProperties.addProperty( BEHAVIOR_CUSTOMPROPERTY_NAME );
+    // Add custom behaviors node.
+    TamlCustomNode* pCustomBehaviorNode = customNodes.addNode( BEHAVIOR_NODE_NAME );
 
     // Iterate behaviors.
     for( SimSet::iterator behaviorItr = mBehaviors.begin(); behaviorItr != mBehaviors.end(); ++behaviorItr )
@@ -913,11 +913,11 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
         // Fetch template.
         BehaviorTemplate* pBehaviorTemplate = pBehaviorInstance->getTemplate();
 
-        // Add behavior alias.
-        TamlPropertyAlias* pBehaviorAlias = pBehaviorProperty->addAlias( pBehaviorInstance->getTemplateName() );
+        // Add behavior node.
+        TamlCustomNode* pBehaviorNode = pCustomBehaviorNode->addNode( pBehaviorInstance->getTemplateName() );
 
         // Add behavior Id field.
-        pBehaviorAlias->addField( BEHAVIOR_ID_FIELD_NAME, pBehaviorInstance->getBehaviorId() );
+        pBehaviorNode->addField( BEHAVIOR_ID_FIELD_NAME, pBehaviorInstance->getBehaviorId() );
 
         // Fetch field count,
         const U32 behaviorFieldCount = pBehaviorTemplate->getBehaviorFieldCount();
@@ -942,7 +942,7 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
             const char* pFieldValue = pBehaviorInstance->getPrefixedDynamicDataField( pBehaviorField->mName, NULL, fieldType );
 
             // Add behavior field.
-            pBehaviorAlias->addField( pBehaviorField->mName, pFieldValue );
+            pBehaviorNode->addField( pBehaviorField->mName, pFieldValue );
         }
     }
 
@@ -953,8 +953,8 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
     if ( behaviorConnectionCount == 0 )
         return;
 
-    // Add behavior connection property.
-    TamlCustomProperty* pConnectionProperty = customProperties.addProperty( BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME );
+    // Add custom behavior connection property.
+    TamlCustomNode* pCustomConnection = customNodes.addNode( BEHAVIOR_CONNECTION_NODE_NAME );
     
     // Iterate instance connections.
     for( typeInstanceConnectionHash::iterator instanceItr = mBehaviorConnections.begin(); instanceItr != mBehaviorConnections.end(); ++instanceItr )
@@ -974,12 +974,12 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
                 // Fetch connection.
                 BehaviorPortConnection* pConnection = connectionItr;
 
-                // Add connection alias.
-                TamlPropertyAlias* pConnectionAlias = pConnectionProperty->addAlias( BEHAVIOR_CONNECTION_TYPE_NAME );
+                // Add connectionnode.
+                TamlCustomNode* pConnectionNode = pCustomConnection->addNode( BEHAVIOR_CONNECTION_TYPE_NAME );
 
                 // Add behavior field.
-                pConnectionAlias->addField( pConnection->mOutputName, pConnection->mOutputInstance->getBehaviorId() );
-                pConnectionAlias->addField( pConnection->mInputName, pConnection->mInputInstance->getBehaviorId() );
+                pConnectionNode->addField( pConnection->mOutputName, pConnection->mOutputInstance->getBehaviorId() );
+                pConnectionNode->addField( pConnection->mInputName, pConnection->mInputInstance->getBehaviorId() );
             }
         }
     }
@@ -987,16 +987,16 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
 
 //-----------------------------------------------------------------------------
 
-void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void BehaviorComponent::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find behavior custom property name.
-    const TamlCustomProperty* pCustomProperty = customProperties.findProperty( BEHAVIOR_CUSTOMPROPERTY_NAME );
+    // Find custom behaviors node.
+    const TamlCustomNode* pCustomBehaviorNode = customNodes.findNode( BEHAVIOR_NODE_NAME );
 
     // Do we have the property?
-    if ( pCustomProperty != NULL )
+    if ( pCustomBehaviorNode != NULL )
     {
         // Yes, so reset maximum behavior Id.
         S32 maximumBehaviorId = 0;
@@ -1007,23 +1007,26 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
         // Fetch behavior template asset field type.
         StringTableEntry behaviorTemplateAssetFieldType = StringTable->insert( BEHAVIORTEMPLATE_ASSET_FIELDTYPE );
 
-        // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pCustomProperty->begin(); propertyAliasItr != pCustomProperty->end(); ++propertyAliasItr )
+        // Fetch children behavior nodes.
+        const TamlCustomNodeVector& behaviorNodes = pCustomBehaviorNode->getChildren();
+
+        // Iterate behavior nodes.
+        for( TamlCustomNodeVector::const_iterator behaviorNodeItr = behaviorNodes.begin(); behaviorNodeItr != behaviorNodes.end(); ++behaviorNodeItr )
         {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+            // Fetch behavior node.
+            TamlCustomNode* pBehaviorNode = *behaviorNodeItr;
 
             // Fetch template.
-            BehaviorTemplate* pTemplate = dynamic_cast<BehaviorTemplate *>( Sim::findObject( pPropertyAlias->mAliasName ) );
+            BehaviorTemplate* pTemplate = dynamic_cast<BehaviorTemplate *>( Sim::findObject( pBehaviorNode->getNodeName() ) );
 
             // Find template?
             if( pTemplate == NULL )
             {
                 // No, so warn appropriately.
-                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Missing Behavior '%s'", pPropertyAlias->mAliasName );
+                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Missing Behavior '%s'", pBehaviorNode->getNodeName() );
 
                 if( isMethod( "onBehaviorMissing" ) )
-                    Con::executef( this, 2, "onBehaviorMissing", pPropertyAlias->mAliasName );
+                    Con::executef( this, 2, "onBehaviorMissing", pBehaviorNode->getNodeName() );
 
                 // Skip it.
                 continue;
@@ -1036,10 +1039,10 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
             if ( pBehaviorInstance == NULL )
             {
                 // No, so warn appropriately.
-                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Found behavior could not create an instance '%s'", pPropertyAlias->mAliasName );
+                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Found behavior could not create an instance '%s'", pBehaviorNode->getNodeName() );
 
                 if( isMethod( "onBehaviorMissing" ) )
-                    Con::executef( this, 2, "onBehaviorMissing", pPropertyAlias->mAliasName );
+                    Con::executef( this, 2, "onBehaviorMissing", pBehaviorNode->getNodeName() );
 
                 // Skip it.
                 continue;
@@ -1047,17 +1050,20 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
 
             S32 behaviorId = 0;
 
-            // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            // Fetch field nodes.
+            const TamlCustomFieldVector& fields = pBehaviorNode->getFields();
+
+            // Iterate fields.
+            for ( TamlCustomFieldVector::const_iterator nodeFieldItr = fields.begin(); nodeFieldItr != fields.end(); ++nodeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                TamlCustomField* pField = *nodeFieldItr;
 
                 // Fetch field name.
-                const char* pFieldName = pPropertyField->getFieldName();
+                const char* pFieldName = pField->getFieldName();
 
                 // Fetch field value.
-                const char* pFieldValue = pPropertyField->getFieldValue();
+                const char* pFieldValue = pField->getFieldValue();
 
                 // Is this the behavior field Id name?
                 if ( pFieldName == behaviorFieldIdName )
@@ -1071,7 +1077,7 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
                         // No, so warn.
                         Con::warnf( "BehaviorComponent::onTamlCustomRead() - Encountered an invalid behavior Id of '%d' on behavior '%s'.",
                             behaviorId,
-                            pPropertyAlias->mAliasName );
+                            pBehaviorNode->getNodeName() );
                     }
 
                     // Update maximum behavior Id found.
@@ -1096,7 +1102,7 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
                 }
 
                 // Set field.
-                pBehaviorInstance->setPrefixedDynamicDataField( pPropertyField->getFieldName(), NULL, pPropertyField->getFieldValue(), fieldType );
+                pBehaviorInstance->setPrefixedDynamicDataField( pField->getFieldName(), NULL, pField->getFieldValue(), fieldType );
             }
 
             // Add behavior.
@@ -1111,27 +1117,33 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
         mMasterBehaviorId = (U32)maximumBehaviorId+1;
     }
 
-    // Find behavior connections custom property name.
-    const TamlCustomProperty* pConnectionProperty = customProperties.findProperty( BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME );
+    // Find behavior connections custom node.
+    const TamlCustomNode* pCustomConnectionNode = customNodes.findNode( BEHAVIOR_CONNECTION_NODE_NAME );
 
-    // Do we have the property?
-    if ( pConnectionProperty != NULL )
+    // Do we have the custom connection node?
+    if ( pCustomConnectionNode != NULL )
     {
-        // Yes, so insert connection alias.
-        StringTableEntry connectionAlias = StringTable->insert( BEHAVIOR_CONNECTION_TYPE_NAME );
+        // Yes, so fetch the connection node name..
+        StringTableEntry connectionNodeName = StringTable->insert( BEHAVIOR_CONNECTION_TYPE_NAME );
+
+        // Fetch children connection nodes.
+        const TamlCustomNodeVector& connectionNodes = pCustomConnectionNode->getChildren();
 
         // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pConnectionProperty->begin(); propertyAliasItr != pConnectionProperty->end(); ++propertyAliasItr )
+        for( TamlCustomNodeVector::const_iterator connectionNodeItr = connectionNodes.begin(); connectionNodeItr != connectionNodes.end(); ++connectionNodeItr )
         {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+            // Fetch connection node.
+            TamlCustomNode* pConnectionNode = *connectionNodeItr;
 
             // Skip if the alias isn't a connection.
-            if ( pPropertyAlias->mAliasName != connectionAlias )
+            if ( pConnectionNode->getNodeName() != connectionNodeName )
                 continue;
 
+            // Fetch field nodes.
+            const TamlCustomFieldVector& connectionFieldNodes = pConnectionNode->getFields();
+
             // Are there two properties?
-            if ( pPropertyAlias->size() != 2 )
+            if ( connectionFieldNodes.size() != 2 )
             {
                 // No, so warn.
                 Con::warnf( "BehaviorComponent::onTamlCustomRead() - Encountered a behavior connection with more than two connection fields." );
@@ -1139,8 +1151,8 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
             }
 
             // Fetch property field #1.
-            TamlPropertyField* pPropertyField1 = *pPropertyAlias->begin();
-            TamlPropertyField* pPropertyField2 = *(pPropertyAlias->begin()+1);
+            TamlCustomField* pPropertyField1 = *connectionFieldNodes.begin();
+            TamlCustomField* pPropertyField2 = *(connectionFieldNodes.begin()+1);
            
             // Fetch behavior instances #1.
             BehaviorInstance* pBehaviorInstance1 = getBehaviorByInstanceId( dAtoi( pPropertyField1->getFieldValue() ) );
@@ -1296,9 +1308,7 @@ const char *BehaviorComponent::callOnBehaviors( U32 argc, const char *argv[] )
 {   
     if( mBehaviors.empty() )   
         return Parent::callOnBehaviors( argc, argv );
-   
-    const char *cbName = StringTable->insert(argv[0]);
-   
+      
     // Copy the arguments to avoid weird clobbery situations.
     FrameTemp<char *> argPtrs (argc);
    
@@ -1324,6 +1334,7 @@ const char *BehaviorComponent::callOnBehaviors( U32 argc, const char *argv[] )
             continue;
 
         // Lookup the Callback Namespace entry and then splice callback
+        const char *cbName = StringTable->insert(argv[0]);
         Namespace::Entry *pNSEntry = pNamespace->lookup(cbName);
         if( pNSEntry )
         {
@@ -1361,9 +1372,7 @@ const char *BehaviorComponent::_callMethod( U32 argc, const char *argv[], bool c
 {   
     if( mBehaviors.empty() )   
         return Parent::_callMethod( argc, argv, callThis );
-   
-    const char *cbName = StringTable->insert(argv[0]);
-   
+     
     // Copy the arguments to avoid weird clobbery situations.
     FrameTemp<char *> argPtrs (argc);
    
@@ -1386,6 +1395,7 @@ const char *BehaviorComponent::_callMethod( U32 argc, const char *argv[], bool c
             continue;
 
         // Lookup the Callback Namespace entry and then splice callback
+        const char *cbName = StringTable->insert(argv[0]);
         Namespace::Entry *pNSEntry = pNamespace->lookup(cbName);
         if( pNSEntry )
         {

+ 4 - 4
engine/source/component/behaviors/behaviorComponent.h

@@ -34,8 +34,8 @@
 //-----------------------------------------------------------------------------
 
 #define BEHAVIOR_ID_FIELD_NAME                  "Id"
-#define BEHAVIOR_CUSTOMPROPERTY_NAME                "Behaviors"
-#define BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME     "BehaviorConnections"
+#define BEHAVIOR_NODE_NAME                      "Behaviors"
+#define BEHAVIOR_CONNECTION_NODE_NAME           "BehaviorConnections"
 #define BEHAVIOR_CONNECTION_TYPE_NAME           "Connection"
 
 //-----------------------------------------------------------------------------
@@ -88,8 +88,8 @@ protected:
     virtual const char* _callMethod( U32 argc, const char *argv[], bool callThis = true );
 
     /// Taml callbacks.
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 private:
     void destroyBehaviorOutputConnections( BehaviorInstance* pOutputBehavior );

+ 1 - 3
engine/source/component/dynamicConsoleMethodComponent.cpp

@@ -134,8 +134,6 @@ const char *DynamicConsoleMethodComponent::callOnBehaviors( U32 argc, const char
    pThis = dynamic_cast<SimObject *>( this );
    AssertFatal( pThis, "DynamicConsoleMethodComponent::callOnBehaviors : this should always exist!" );
 
-   const char *cbName = StringTable->insert(argv[0]);
-
    const char* result = "";
    bool handled = false;
 
@@ -144,7 +142,7 @@ const char *DynamicConsoleMethodComponent::callOnBehaviors( U32 argc, const char
       VectorPtr<SimComponent *>&componentList = lockComponentList();
       for( SimComponentIterator nItr = (componentList.end()-1);  nItr >= componentList.begin(); nItr-- )
       {
-         argv[0] = cbName;
+         argv[0] = StringTable->insert(argv[0]);
 
          SimComponent *pComponent = (*nItr);
          AssertFatal( pComponent, "DynamicConsoleMethodComponent::callOnBehaviors - NULL component in list!" );

+ 137 - 61
engine/source/console/compiledEval.cc

@@ -28,6 +28,7 @@
 #include "io/resource/resourceManager.h"
 
 #include "string/findMatch.h"
+#include "string/stringUnit.h"
 #include "console/consoleInternal.h"
 #include "io/fileStream.h"
 #include "console/compiler.h"
@@ -301,73 +302,148 @@ static void setUnit(const char *string, U32 index, const char *replace, const ch
    return;
 }
 
-// Gets a component of an object's field value or a variable and returns it
-// in val.
-static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[], S32 count )
+//-----------------------------------------------------------------------------
+
+static bool isDigitsOnly( const char* pString )
 {
-   const char* prevVal = NULL;
-   // Grab value from object.
-   if( object && field )
-      prevVal = object->getDataField( field, array );
-   // Otherwise, grab from the string stack. The value coming in will always
-   // be a string because that is how multicomponent variables are handled.
-   else
-      prevVal = STR.getStringValue();
+    // Sanity.
+    AssertFatal( pString != NULL, "isDigits() - Cannot check a NULL string." );
 
-   // Make sure we got a value.
-   if( prevVal && *prevVal )
-   {
-      // 'x', 'y', and 'z' grab the 1st, 2nd, or 3rd component of the
-      // variable or field.
-      if( subField == StringTable->insert( "x" ) )
-         getUnit( prevVal, 0, " ", val, count );
-      else if( subField == StringTable->insert( "y" ) )
-         getUnit( prevVal, 1, " ", val, count );
-      else if( subField == StringTable->insert( "z" ) )
-         getUnit( prevVal, 2, " ", val, count );
-   }
+    const char* pDigitCursor = pString;
+    if ( *pDigitCursor == 0 )
+        return false;
+
+    // Check for digits only.
+    do
+    {
+        if ( dIsdigit( *pDigitCursor++ ) )
+            continue;
+
+        return false;
+    }
+    while( *pDigitCursor != 0 );
+
+    return true;
 }
 
+//-----------------------------------------------------------------------------
+
+static const StringTableEntry _xyzw[] = 
+{
+    StringTable->insert( "x" ),
+    StringTable->insert( "y" ),
+    StringTable->insert( "z" ),
+    StringTable->insert( "w" )
+};
+
+static const StringTableEntry _rgba[] = 
+{
+    StringTable->insert( "r" ),
+    StringTable->insert( "g" ),
+    StringTable->insert( "b" ),
+    StringTable->insert( "a" )
+};
+
+static const StringTableEntry _size[] = 
+{
+    StringTable->insert( "width" ),
+    StringTable->insert( "height" )
+};
+
+static const StringTableEntry _count = StringTable->insert( "count" );
+
+//-----------------------------------------------------------------------------
+
+// Gets a component of an object's field value or a variable and returns it in val.
+static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, FrameTemp<char>& val )
+{
+    const char* prevVal = NULL;
+   
+    // Grab value from object.
+    if( object && field )
+        prevVal = object->getDataField( field, array );
+   
+    // Otherwise, grab from the string stack. The value coming in will always
+    // be a string because that is how multicomponent variables are handled.
+    else
+        prevVal = STR.getStringValue();
+
+    // Make sure we got a value.
+    if ( prevVal && *prevVal )
+    {
+        if ( subField == _count )
+            dSprintf( val, val.getObjectCount(), "%d", StringUnit::getUnitCount( prevVal, " \t\n" ) );
+
+        else if ( subField == _xyzw[0] || subField == _rgba[0] || subField == _size[0] )
+            dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") );
+
+        else if ( subField == _xyzw[1] || subField == _rgba[1] || subField == _size[1] )
+            dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") );
+
+        else if ( subField == _xyzw[2] || subField == _rgba[2] )
+            dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") );
+
+        else if ( subField == _xyzw[3] || subField == _rgba[3] )
+            dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") );
+
+        else if ( *subField == '_' && isDigitsOnly(subField+1) )
+            dStrcpy( val, StringUnit::getUnit( prevVal, dAtoi(subField+1), " \t\n") );
+
+        else
+            val[0] = 0;
+    }
+    else
+        val[0] = 0;
+}
+
+//-----------------------------------------------------------------------------
+
 // Sets a component of an object's field value based on the sub field. 'x' will
 // set the first field, 'y' the second, and 'z' the third.
 static void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField )
 {
-   char val[1024] = "";
-   const char* prevVal;
-   // Set the value on an object field.
-   if( object && field )
-      prevVal = object->getDataField( field, array );
-
-   // Set the value on a variable.
-   else if( gEvalState.currentVariable )
-      prevVal = gEvalState.getStringVariable();
-
-   // Insert the value into the specified component of the string.
-   bool set = false;
-   if( subField == StringTable->insert( "x" ) )
-   {
-      setUnit( prevVal, 0, STR.getStringValue(), " ", val, 1024 );
-      set = true;
-   }
-   else if( subField == StringTable->insert( "y" ) )
-   {
-      setUnit( prevVal, 1, STR.getStringValue(), " ", val, 1024 );
-      set = true;
-   }
-   else if( subField == StringTable->insert( "z" ) )
-   {
-      setUnit( prevVal, 2, STR.getStringValue(), " ", val, 1024 );
-      set = true;
-   }
+    // Copy the current string value
+    char strValue[1024];
+    dStrncpy( strValue, STR.getStringValue(), 1024 );
 
-   if( set )
-   {
-      // Update the field or variable.
-      if( object && field )
-         object->setDataField( field, array, val );
-      else if( gEvalState.currentVariable )
-         gEvalState.setStringVariable( val );
-   }
+    char val[1024] = "";
+    const char* prevVal = NULL;
+
+    // Set the value on an object field.
+    if( object && field )
+        prevVal = object->getDataField( field, array );
+
+    // Set the value on a variable.
+    else if( gEvalState.currentVariable )
+        prevVal = gEvalState.getStringVariable();
+
+    // Ensure that the variable has a value
+    if (!prevVal)
+	    return;
+
+    if ( subField == _xyzw[0] || subField == _rgba[0] || subField == _size[0] )
+	    dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") );
+
+    else if ( subField == _xyzw[1] || subField == _rgba[1] || subField == _size[1] )
+        dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") );
+
+    else if ( subField == _xyzw[2] || subField == _rgba[2] )
+        dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") );
+
+    else if ( subField == _xyzw[3] || subField == _rgba[3] )
+        dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") );
+
+    else if ( *subField == '_' && isDigitsOnly(subField+1) )
+        dStrcpy( val, StringUnit::setUnit( prevVal, dAtoi(subField+1), strValue, " \t\n") );
+
+    if ( val[0] != 0 )
+    {
+        // Update the field or variable.
+        if( object && field )
+            object->setDataField( field, 0, val );
+        else if( gEvalState.currentVariable )
+            gEvalState.setStringVariable( val );
+    }
 }
 
 const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, const char **argv, bool noCalls, StringTableEntry packageName, S32 setFrame)
@@ -1160,7 +1236,7 @@ breakContinue:
             {
                // The field is not being retrieved from an object. Maybe it's
                // a special accessor?
-               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer, VAL_BUFFER_SIZE );
+               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );
                intStack[UINT+1] = dAtoi( valBuffer );
             }
             UINT++;
@@ -1173,7 +1249,7 @@ breakContinue:
             {
                // The field is not being retrieved from an object. Maybe it's
                // a special accessor?
-               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer, VAL_BUFFER_SIZE );
+               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );
                floatStack[FLT+1] = dAtof( valBuffer );
             }
             FLT++;
@@ -1189,7 +1265,7 @@ breakContinue:
             {
                // The field is not being retrieved from an object. Maybe it's
                // a special accessor?
-               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer, VAL_BUFFER_SIZE );
+               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );
                STR.setStringValue( valBuffer );
             }
 

+ 30 - 52
engine/source/console/consoleTypes.cc

@@ -33,56 +33,6 @@
 #include "2d/core/Vector2.h"
 #endif
 
-//////////////////////////////////////////////////////////////////////////
-// TypeString
-//////////////////////////////////////////////////////////////////////////
-ConsoleType( Vector2, TypeT2DVector, sizeof(Vector2), "" )
-
-ConsoleGetType( TypeT2DVector )
-{
-    Vector2* pVector = (Vector2*)dptr;  
-    return pVector->scriptThis();
-}
-
-ConsoleSetType( TypeT2DVector )
-{
-    Vector2* pVector = (Vector2*)dptr;  
-    F32 x;
-    F32 y;
-    if( argc == 1 )
-    {
-        S32 args = dSscanf(argv[0], "%g %g", &x, &y);
-        if ( args == 2 )
-        {
-            pVector->Set( x, y );
-            return;
-        }
-        else if ( args == 1 )
-        {
-            pVector->Set( x, x );
-            return;
-        }
-        else
-        {
-            // Warn.
-            Con::warnf("Vector2 must be set as { x [,y] }");
-            return;
-        }
-
-    }
-    else if ( argc == 2 )
-    {
-        pVector->Set( dAtof(argv[0]), dAtof(argv[1]) );
-        return;
-    }
-    else
-    {
-        // Warn.
-        Con::warnf("Vector2 must be set as { x [,y] }");
-        return;
-    }
-}
-
 //////////////////////////////////////////////////////////////////////////
 // TypeString
 //////////////////////////////////////////////////////////////////////////
@@ -537,14 +487,15 @@ ConsoleGetType( TypeSimObjectPtr )
 {
    SimObject **obj = (SimObject**)dptr;
    char* returnBuffer = Con::getReturnBuffer(256);
-   dSprintf(returnBuffer, 256, "%s", *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : "");
+   const char* Id =  *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : StringTable->EmptyString;
+   dSprintf(returnBuffer, 256, "%s", Id);
    return returnBuffer;
 }
 
 //////////////////////////////////////////////////////////////////////////
 // TypeSimObjectName
 //////////////////////////////////////////////////////////////////////////
-ConsoleType( SimObjectPtr, TypeSimObjectName, sizeof(SimObject*), "" )
+ConsoleType( SimObjectName, TypeSimObjectName, sizeof(SimObject*), "" )
 
 ConsoleSetType( TypeSimObjectName )
 {
@@ -564,3 +515,30 @@ ConsoleGetType( TypeSimObjectName )
    dSprintf(returnBuffer, 128, "%s", *obj && (*obj)->getName() ? (*obj)->getName() : "");
    return returnBuffer;
 }
+
+
+//////////////////////////////////////////////////////////////////////////
+// TypeSimObjectId
+//////////////////////////////////////////////////////////////////////////
+ConsoleType( SimObjectId, TypeSimObjectId, sizeof(SimObject*), "" )
+
+ConsoleSetType( TypeSimObjectId )
+{
+   if(argc == 1)
+   {
+      SimObject **obj = (SimObject **)dptr;
+      *obj = Sim::findObject(argv[0]);
+   }
+   else
+      Con::printf("(TypeSimObjectId) Cannot set multiple args to a single S32.");
+}
+
+ConsoleGetType( TypeSimObjectId )
+{
+   SimObject **obj = (SimObject**)dptr;
+   char* returnBuffer = Con::getReturnBuffer(128);
+   dSprintf(returnBuffer, 128, "%s", *obj ? (*obj)->getIdString() : StringTable->EmptyString );
+   return returnBuffer;
+}
+
+

+ 1 - 1
engine/source/console/consoleTypes.h

@@ -51,6 +51,6 @@ DefineConsoleType( TypeEnum )
 DefineConsoleType( TypeFlag )
 DefineConsoleType( TypeSimObjectPtr )
 DefineConsoleType( TypeSimObjectName )
-DefineConsoleType( TypeT2DVector )
+DefineConsoleType( TypeSimObjectId )
 
 #endif

+ 13 - 0
engine/source/graphics/TextureDictionary.cc

@@ -43,6 +43,19 @@ void TextureDictionary::create()
         smTable[i] = NULL;
 }
 
+//-----------------------------------------------------------------------------
+
+TextureObject* TextureDictionary::find( StringTableEntry textureKey )
+{
+    U32 key = HashPointer(textureKey) % smHashTableSize;
+    TextureObject *walk = smTable[key];
+    for(; walk; walk = walk->hashNext)
+    {
+        if(walk->mTextureKey == textureKey)
+            break;
+    }
+    return walk;
+}
 
 //-----------------------------------------------------------------------------
 

+ 3 - 2
engine/source/graphics/TextureDictionary.h

@@ -40,8 +40,9 @@ public:
     static void destroy();
 
     static void insert(TextureObject *object);
-    static TextureObject *find(StringTableEntry textureKey, TextureHandle::TextureHandleType type, bool clamp);
-    static void remove(TextureObject *object);
+    static TextureObject* find( StringTableEntry textureKey );
+    static TextureObject* find( StringTableEntry textureKey, TextureHandle::TextureHandleType type, bool clamp );
+    static void remove( TextureObject *object );
 };
 
 #endif // _TEXTURE_DICTIONARY_H_

+ 1 - 1
engine/source/graphics/TextureHandle.h

@@ -119,7 +119,7 @@ public:
         BitmapTexture = 100,
 
         /// Same as BitmapTexture except that the bitmap is kept which occupies main memory however
-        /// it does not required loading if textures need to be restored.
+        /// it does not require loading if textures need to be restored.
         BitmapKeepTexture = 200,
     };
 

+ 32 - 0
engine/source/graphics/TextureManager.cc

@@ -689,6 +689,38 @@ void TextureManager::refresh( TextureObject* pTextureObject )
 
 //--------------------------------------------------------------------------------------------------------------------
 
+void TextureManager::refresh( const char *textureName )
+{
+    // Finish if no texture name specified.
+    AssertFatal( textureName != NULL, "Texture Manager:  Cannot refresh a NULL texture name." );
+
+    // Find the texture object.
+    TextureObject* pTextureObject = TextureDictionary::find( textureName );
+
+    // Finish if no texture for this texture name.
+    if ( pTextureObject == NULL )
+        return;
+
+    // Finish if the texture object is a kept bitmap.
+    if ( pTextureObject->getHandleType() == TextureHandle::BitmapKeepTexture )
+        return;
+
+    // Load the bitmap.
+    GBitmap* pBitmap = loadBitmap( pTextureObject->mTextureKey );
+
+    // Finish if bitmap could not be loaded.
+    if ( pBitmap == NULL )
+        return;
+
+    // Register texture.
+    TextureObject* pNewTextureObject = registerTexture(pTextureObject->mTextureKey, pBitmap, pTextureObject->mHandleType, pTextureObject->mClamp);
+
+    // Sanity!
+    AssertFatal(pNewTextureObject == pTextureObject, "A new texture was returned during refresh.");
+}
+
+//--------------------------------------------------------------------------------------------------------------------
+
 void TextureManager::createGLName( TextureObject* pTextureObject )
 {
     // Finish if not appropriate.

+ 2 - 1
engine/source/graphics/TextureManager.h

@@ -56,7 +56,7 @@ public:
     {
         BeginZombification,
         BeginResurrection,
-        EndResurrection
+        EndResurrection,
     };
 
     typedef void (*TextureEventCallback)(const TextureEventCode eventCode, void *userData);
@@ -93,6 +93,7 @@ public:
     static void killManager();
     static void resurrectManager();
     static void flush();
+    static void refresh( const char *textureName );
     static S32 getBitmapResidentSize( void ) { return mBitmapResidentSize; }
     static S32 getTextureResidentSize( void ) { return mTextureResidentSize; }
     static S32 getTextureResidentWasteSize( void ) { return mTextureResidentWasteSize; }

+ 1 - 1
engine/source/math/rectClipper.cpp

@@ -116,7 +116,7 @@ RectClipper::clipLine(const Point2I& in_rStart,
       x2  = x1 + (t * dx);
    }
 
-   if (flipped == true) 
+   if (flipped)
    {
       out_rEnd.x   = S32(x1 + 0.5f);
       out_rEnd.y   = S32(y1 + 0.5f);

+ 3 - 2
engine/source/memory/frameAllocator.h

@@ -254,6 +254,8 @@ public:
       FrameAllocator::setWaterMark( mWaterMark );
    }
 
+   U32 getObjectCount( void ) const { return mNumObjectsInMemory; }
+
    /// NOTE: This will return the memory, NOT perform a ones-complement
    T* operator ~() { return mMemory; };
    /// NOTE: This will return the memory, NOT perform a ones-complement
@@ -279,14 +281,13 @@ public:
    operator T() { return *mMemory; }
    operator const T() const { return *mMemory; }
 
+
    // This ifdef is to satisfy the ever so pedantic GCC compiler
    //  Which seems to upset visual studio.
-#if defined(TORQUE_COMPILER_GCC)   
    T& operator[]( const U32 idx ) { return mMemory[idx]; }
    const T& operator[]( const U32 idx ) const { return mMemory[idx]; }
    T& operator[]( const S32 idx ) { return mMemory[idx]; }
    const T& operator[]( const S32 idx ) const { return mMemory[idx]; }   
-#endif
 };
 
 //-----------------------------------------------------------------------------

+ 4 - 8
engine/source/module/moduleDefinition.cc

@@ -48,8 +48,6 @@ ModuleDefinition::ModuleDefinition() :
     mVersionId( 0 ),
     mBuildId( 0 ),
     mEnabled( true ),
-    mPublished( true ),
-    mPurchased( false ),
     mSynchronized( false ),
     mDeprecated( false ),
     mCriticalMerge( false ),
@@ -84,21 +82,19 @@ void ModuleDefinition::initPersistFields()
     Parent::initPersistFields();
 
     /// Module configuration.
-    addProtectedField( "ModuleId", TypeString, Offset(mModuleId, ModuleDefinition), &setModuleId, &defaultProtectedGetFn, "A unique string Id for the module.  It can contain any characters except a comma." );
+    addProtectedField( "ModuleId", TypeString, Offset(mModuleId, ModuleDefinition), &setModuleId, &defaultProtectedGetFn, "A unique string Id for the module.  It can contain any characters except a comma or semi-colon (the asset scope character)." );
     addProtectedField( "VersionId", TypeS32, Offset(mVersionId, ModuleDefinition), &setVersionId, &defaultProtectedGetFn, "The version Id.  Breaking changes to a module should use a higher version Id." );
     addProtectedField( "BuildId", TypeS32, Offset(mBuildId, ModuleDefinition), &setBuildId, &defaultProtectedGetFn, &writeBuildId, "The build Id.  Non-breaking changes to a module should use a higher build Id.  Optional: If not specified then the build Id will be zero." );
     addProtectedField( "Enabled", TypeBool, Offset(mEnabled, ModuleDefinition), &setEnabled, &defaultProtectedGetFn, &writeEnabled, "Whether the module is enabled or not.  When disabled, it is effectively ignored.  Optional: If not specified then the module is enabled." );
-    addProtectedField( "Published", TypeBool, Offset(mPublished, ModuleDefinition), &setPublished, &defaultProtectedGetFn, &writePublished, "Whether the module should be published to the web-services or not.  Optional: If not specified then the module is published." );
-    addProtectedField( "Purchased", TypeBool, Offset(mPurchased, ModuleDefinition), &setPurchased, &defaultProtectedGetFn, &writePurchased, "Whether the module should be purchased via the web-services or not.  Optional: If not specified then the module is purchased." );
-    addProtectedField( "Synchronized", TypeBool, Offset(mSynchronized, ModuleDefinition), &setSynchronized, &defaultProtectedGetFn, &writeSynchronized, "Whether the module is should be synchronized or not.  Optional: If not specified then the module is not synchronized." );
+    addProtectedField( "Synchronized", TypeBool, Offset(mSynchronized, ModuleDefinition), &setSynchronized, &defaultProtectedGetFn, &writeSynchronized, "Whether the module should be synchronized or not.  Optional: If not specified then the module is not synchronized." );
     addProtectedField( "Deprecated", TypeBool, Offset(mDeprecated, ModuleDefinition), &setDeprecated, &defaultProtectedGetFn, &writeDeprecated, "Whether the module is deprecated or not.  Optional: If not specified then the module is not deprecated." );
     addProtectedField( "CriticalMerge", TypeBool, Offset(mCriticalMerge, ModuleDefinition), &setDeprecated, &defaultProtectedGetFn, &writeCriticalMerge, "Whether the merging of a module prior to a restart is critical or not.  Optional: If not specified then the module is not merge critical." );
-    addProtectedField( "Description", TypeString, Offset(mModuleDescription, ModuleDefinition), &setModuleDescription, &defaultProtectedGetFn, &writeModuleDescription, "The description displayed for debugging purposes." );
+    addProtectedField( "Description", TypeString, Offset(mModuleDescription, ModuleDefinition), &setModuleDescription, &defaultProtectedGetFn, &writeModuleDescription, "The description typically used for debugging purposes but can be used for anything." );
     addProtectedField( "Author", TypeString, Offset(mAuthor, ModuleDefinition), &setAuthor, &defaultProtectedGetFn, &writeAuthor, "The author of the module." );
     addProtectedField( "Group", TypeString, Offset(mModuleGroup, ModuleDefinition), &setModuleGroup, &defaultProtectedGetFn, "The module group used typically when loading modules as a group." );
     addProtectedField( "Type", TypeString, Offset(mModuleType, ModuleDefinition), &setModuleType, &defaultProtectedGetFn, &writeModuleType, "The module type typically used to distinguish modules during module enumeration.  Optional: If not specified then the type is empty although this can still be used as a pseudo 'global' type for instance." );
     addProtectedField( "Dependencies", TypeString, Offset(mDependencies, ModuleDefinition), &setDependencies, &getDependencies, &writeDependencies, "A comma-separated list of module Ids/VersionIds (<ModuleId>=<VersionId>,<ModuleId>=<VersionId>,etc) which this module depends upon. Optional: If not specified then no dependencies are assumed." );
-    addProtectedField( "ScriptFile", TypeString, Offset(mScriptFile, ModuleDefinition), &setScriptFile, &defaultProtectedGetFn, &writeScriptFile, "The name of the script file to execute when loading the module.  Optional." );
+    addProtectedField( "ScriptFile", TypeString, Offset(mScriptFile, ModuleDefinition), &setScriptFile, &defaultProtectedGetFn, &writeScriptFile, "The name of the script file to compile when loading the module.  Optional." );
     addProtectedField( "CreateFunction", TypeString, Offset(mCreateFunction, ModuleDefinition), &setCreateFunction, &defaultProtectedGetFn, &writeCreateFunction, "The name of the function used to create the module.  Optional: If not specified then no create function is called." );
     addProtectedField( "DestroyFunction", TypeString, Offset(mDestroyFunction, ModuleDefinition), &setDestroyFunction, &defaultProtectedGetFn, &writeDestroyFunction, "The name of the function used to destroy the module.  Optional: If not specified then no destroy function is called." );
     addProtectedField( "AssetTagsManifest", TypeString, Offset(mAssetTagsManifest, ModuleDefinition), &setAssetTagsManifest, &defaultProtectedGetFn, &writeAssetTagsManifest, "The name of tags asset manifest file if this module contains asset tags.  Optional: If not specified then no asset tags will be found for this module.  Currently, only a single asset tag manifest should exist." );

+ 0 - 10
engine/source/module/moduleDefinition.h

@@ -84,8 +84,6 @@ private:
     U32                             mVersionId;
     U32                             mBuildId;
     bool                            mEnabled;
-    bool                            mPublished;
-    bool                            mPurchased;
     bool                            mSynchronized;
     bool                            mDeprecated;
     bool                            mCriticalMerge;
@@ -137,10 +135,6 @@ public:
     inline bool             getEnabled( void ) const                            { return mEnabled; }
     inline void             setSynchronized( const bool synchronized )          { if ( checkUnlocked() ) { mSynchronized = synchronized; } }
     inline bool             getSynchronized( void ) const                       { return mSynchronized; }
-    inline void             setPublished( const bool published )                { if ( checkUnlocked() ) { mPublished = published; } }
-    inline bool             getPublished( void ) const                          { return mPublished; }
-    inline void             setPurchased( const bool purchased )                { if ( checkUnlocked() ) { mPurchased = purchased; } }
-    inline bool             getPurchased( void ) const                          { return mPurchased; }
     inline void             setDeprecated( const bool deprecated )              { if ( checkUnlocked() ) { mDeprecated = deprecated; } }
     inline bool             getDeprecated( void ) const                         { return mDeprecated; }
     inline void             setCriticalMerge( const bool mergeCritical )        { if ( checkUnlocked() ) { mCriticalMerge = mergeCritical; } }
@@ -205,10 +199,6 @@ protected:
     static bool             writeBuildId( void* obj, StringTableEntry pFieldName )      { return static_cast<ModuleDefinition*>(obj)->getBuildId() != 0; }
     static bool             setEnabled(void* obj, const char* data)                     { static_cast<ModuleDefinition*>(obj)->setEnabled( dAtob(data) ); return false; }
     static bool             writeEnabled( void* obj, StringTableEntry pFieldName )      { return static_cast<ModuleDefinition*>(obj)->getEnabled() == false; }
-    static bool             setPublished(void* obj, const char* data)                   { static_cast<ModuleDefinition*>(obj)->setPublished( dAtob(data) ); return false; }
-    static bool             writePublished( void* obj, StringTableEntry pFieldName )    { return static_cast<ModuleDefinition*>(obj)->getPublished() == false; }
-    static bool             setPurchased(void* obj, const char* data)                   { static_cast<ModuleDefinition*>(obj)->setPurchased( dAtob(data) ); return false; }
-    static bool             writePurchased( void* obj, StringTableEntry pFieldName )    { return static_cast<ModuleDefinition*>(obj)->getPurchased() == true; }
     static bool             setSynchronized(void* obj, const char* data)                { static_cast<ModuleDefinition*>(obj)->setSynchronized( dAtob(data) ); return false; }
     static bool             writeSynchronized( void* obj, StringTableEntry pFieldName ) { return static_cast<ModuleDefinition*>(obj)->getSynchronized() == true; }
     static bool             setDeprecated(void* obj, const char* data)                  { static_cast<ModuleDefinition*>(obj)->setDeprecated( dAtob(data) ); return false; }

+ 2513 - 2453
engine/source/module/moduleManager.cc

@@ -1,2453 +1,2513 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "moduleManager.h"
-
-#ifndef _MODULE_MERGE_DEFINITION_H
-#include "moduleMergeDefinition.h"
-#endif
-
-#ifndef _TAML_MODULE_ID_UPDATE_VISITOR_H_
-#include "tamlModuleIdUpdateVisitor.h"
-#endif
-
-#ifndef _MODULE_CALLBACKS_H_
-#include "moduleCallbacks.h"
-#endif
-
-#ifndef _CONSOLETYPES_H_
-#include "console/consoleTypes.h"
-#endif
-
-// Script bindings.
-#include "moduleManager_ScriptBinding.h"
-
-//-----------------------------------------------------------------------------
-
-IMPLEMENT_CONOBJECT( ModuleManager );
-
-//-----------------------------------------------------------------------------
-
-ModuleManager ModuleDatabase;
-
-//-----------------------------------------------------------------------------
-
-S32 QSORT_CALLBACK moduleDefinitionVersionIdSort( const void* a, const void* b )
-{
-    // Fetch module definitions.
-   ModuleDefinition* pDefinition1 = *(ModuleDefinition**)a;
-   ModuleDefinition* pDefinition2 = *(ModuleDefinition**)b;
-
-   // Fetch version Ids.
-   const U32 versionId1 = pDefinition1->getVersionId();
-   const U32 versionId2 = pDefinition2->getVersionId();
-
-   // We sort higher version Id first.
-   return versionId1 > versionId2 ? -1 : versionId1 < versionId2 ? 1 : 0;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleManager() :
-    mEnforceDependencies(true),
-    mEchoInfo(true),
-    mDatabaseLocks( 0 )
-{
-    // Set module extension.
-    dStrcpy( mModuleExtension, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::onAdd()
-{
-    if( !Parent::onAdd() )
-        return false;
-
-    // Register listeners.
-    mNotificationListeners.registerObject();
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::onRemove()
-{
-    // Clear database.
-    clearDatabase();
-
-    // Unregister object.
-    mNotificationListeners.unregisterObject();
-
-    // Call parent.
-    Parent::onRemove();
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::initPersistFields()
-{
-    // Call parent.
-    Parent::initPersistFields();
-
-    addField( "EnforceDependencies", TypeBool, Offset(mEnforceDependencies, ModuleManager), "Whether the module manager enforces any dependencies on module definitions it discovers or not." );
-    addField( "EchoInfo", TypeBool, Offset(mEchoInfo, ModuleManager), "Whether the module manager echos extra information to the console or not." );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::onDeleteNotify( SimObject *object )
-{
-    // Cast to a module definition.
-    ModuleDefinition* pModuleDefinition = dynamic_cast<ModuleDefinition*>( object );
-
-    // Ignore if not appropriate.
-    if ( pModuleDefinition == NULL )
-        return;
-
-    // Warn.
-    Con::warnf( "Module Manager::onDeleteNotify() - Notified of a module definition deletion for module Id '%s' of version Id '%d' however this should not happen and can cause module database corruption.",
-        pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::setModuleExtension( const char* pExtension )
-{
-    // Sanity!
-    AssertFatal( pExtension != NULL, "Cannot set module extension with NULL extension." );
-
-    // Did we find an extension period?
-    if ( *pExtension == '.' )
-    {
-        // Yes, so warn.
-        Con::warnf("Module Manager: Failed to set extension as supplied extension contains an initial period: '%s'.", pExtension );
-        return false;
-    }
-
-    // Is the extension too large?
-    if ( dStrlen( pExtension ) > sizeof( mModuleExtension ) )
-    {
-        // Yes, so warn.
-        Con::warnf("Module Manager: Failed to set extension as supplied extension is too large: '%s'.", pExtension );
-        return false;
-    }
-
-    // Set module extension.
-    dStrcpy( mModuleExtension, pExtension );
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::scanModules( const char* pPath, const bool rootOnly )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pPath != NULL, "Cannot scan module with NULL path." );
-
-    // Expand module location.
-    char pathBuffer[1024];
-    Con::expandPath( pathBuffer, sizeof(pathBuffer), pPath );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Started scanning '%s'...", pathBuffer );
-    }
-
-    Vector<StringTableEntry> directories;
-
-    // Find directories.
-    if ( !Platform::dumpDirectories( pathBuffer, directories, 1 ) )
-    {
-        // Failed so warn.
-        Con::warnf( "Module Manager: Failed to scan module directories in path '%s'.", pathBuffer );
-        return false;
-    }
-
-    // Fetch extension length.
-    const U32 extensionLength = dStrlen( mModuleExtension );
-
-    Vector<Platform::FileInfo> files;
-
-    // Iterate directories.
-    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
-    {
-        // Fetch base path.
-        StringTableEntry basePath = *basePathItr;
-
-        // Skip if we're only processing the root and this is not the root.
-        if ( rootOnly && basePathItr != directories.begin() )
-            continue;
-
-        // Find files.
-        files.clear();
-        if ( !Platform::dumpPath( basePath, files, 0 ) )
-        {
-            // Failed so warn.
-            Con::warnf( "Module Manager: Failed to scan modules files in directory '%s'.", basePath );
-            return false;
-        }
-
-        // Iterate files.
-        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
-        {
-            // Fetch file info.
-            Platform::FileInfo* pFileInfo = fileItr;
-
-            // Fetch filename.
-            const char* pFilename = pFileInfo->pFileName;
-
-            // Find filename length.
-            const U32 filenameLength = dStrlen( pFilename );
-
-            // Skip if extension is longer than filename.
-            if ( extensionLength > filenameLength )
-                continue;
-
-            // Skip if extension not found.
-            if ( dStricmp( pFilename + filenameLength - extensionLength, mModuleExtension ) != 0 )
-                continue;
-
-            // Register module.
-            registerModule( basePath, pFileInfo->pFileName );
-        }
-
-        // Stop processing if we're only processing the root.
-        if ( rootOnly )
-            break;
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Finished scanning '%s'.", pathBuffer );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::loadModuleGroup( const char* pModuleGroup )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleGroup != NULL, "Cannot load module group with NULL group name." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Loading group '%s':" ,moduleGroup );
-    }
-
-    // Is the module group already loaded?
-    if ( findGroupLoaded( moduleGroup ) != NULL )
-    {
-        // Yes, so warn.
-        Con::warnf( "Module Manager: Cannot load group '%s' as it is already loaded.", moduleGroup );
-        return false;
-    }
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr == mGroupModules.end() )
-    {
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
-            return true;
-        }
-    }
-
-    // Yes, so fetch the module Ids.
-    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-    // Iterate module groups.
-    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-    {
-        // Fetch module Id.
-        StringTableEntry moduleId = *moduleIdItr;
-
-        // Finish if we could not resolve the dependencies for module Id (of any version Id).
-        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-            return false;
-    }
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is already loaded but at version Id '%d'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        // Info.
-        Con::printf( "Module Manager: Group '%s' and its dependencies is comprised of the following '%d' module(s):", moduleGroup, moduleReadyQueue.size() );
-
-        // Iterate the modules echoing them.
-        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-        {
-            // Fetch the ready entry.
-            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-            // Info.
-            Con::printf( "> module Id '%s' at version Id '%d':", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-        }
-    }
-
-    // Add module group.
-    mGroupsLoaded.push_back( moduleGroup );
-
-    // Reset modules loaded count.
-    U32 modulesLoadedCount = 0;
-
-    // Iterate the modules, executing their script files and call their create function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module already loaded.
-        if ( pLoadedEntry != NULL )
-        {
-            // Yes, so increase load count.
-            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
-
-            // Skip.
-            continue;
-        }
-
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printSeparator();
-            Con::printf( "Module Manager: Loading group '%s' : module Id '%s' at version Id '%d' in group '%s' using the script file '%s'.",
-                moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-        }
-
-        // Is the module deprecated?
-        if ( pLoadReadyModuleDefinition->getDeprecated() )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' in group '%s' is deprecated.  You should use a newer version!",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
-        }
-
-        // Add the path expando for module.
-        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
-
-        // Create a scope set.
-        SimSet* pScopeSet = new SimSet;
-        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
-        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
-
-        // Increase load count.
-        pReadyEntry->mpModuleDefinition->increaseLoadCount();
-
-        // Queue module loaded.
-        mModulesLoaded.push_back( *pReadyEntry );
-
-        // Bump modules loaded count.
-        modulesLoadedCount++;
-
-        // Raise notifications.
-        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
-
-        // Do we have a script file-path specified?
-        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
-        {
-            // Yes, so execute the script file.
-            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
-
-            // Did we execute the script file?
-            if ( scriptFileExecuted )
-            {
-                // Yes, so is the create method available?
-                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
-                {
-                    // Yes, so call the create method.
-                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
-                }
-            }
-            else
-            {
-                // No, so warn.
-                Con::errorf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-            }
-        }
-
-        // Raise notifications.
-        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish loading '%d' module(s) for group '%s'.", modulesLoadedCount, moduleGroup );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::unloadModuleGroup( const char* pModuleGroup )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleGroup != NULL, "Cannot unload module group with NULL group name." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Unloading group '%s':" , moduleGroup );
-    }
-
-    // Find the group loaded iterator.
-    typeGroupVector::iterator groupLoadedItr = findGroupLoaded( moduleGroup );
-
-    // Is the module group already loaded?
-    if ( groupLoadedItr == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload group '%s' as it is not loaded.", moduleGroup );
-        return false;
-    }
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr == mGroupModules.end() )
-    {
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
-            return true;
-        }
-    }
-
-    // Yes, so fetch the module Ids.
-    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-    // Iterate module groups.
-    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-    {
-        // Fetch module Id.
-        StringTableEntry moduleId = *moduleIdItr;
-
-        // Finish if we could not resolve the dependencies for module Id (of any version Id).
-        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-            return false;
-    }
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot unload module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is loaded but at version Id '%d'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Remove module group.
-    mGroupsLoaded.erase_fast( groupLoadedItr );
-
-    // Reset modules unloaded count.
-    U32 modulesUnloadedCount = 0;
-
-    // Iterate the modules in reverse order calling their destroy function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module loaded.
-        if ( pLoadedEntry == NULL )
-        {
-            // No, so warn.
-            if ( mEchoInfo )
-            {
-                Con::printf( "Module Manager: Unloading group '%s' but could not unload module Id '%s' at version Id '%d'.",
-                    moduleGroup, pLoadedEntry->mpModuleDefinition->getModuleId(), pLoadedEntry->mpModuleDefinition->getVersionId() );
-            }
-            // Skip.
-            continue;
-        }
-
-        // Reduce load count.
-        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
-
-        // Sanity!
-        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
-
-        // Do we need to unload?
-        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
-        {
-            // Yes, so info.
-            if ( mEchoInfo )
-            {
-                Con::printSeparator();
-                Con::printf( "Module Manager: Unload group '%s' with module Id '%s' at version Id '%d' in group '%s'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
-            }
-
-            // Raise notifications.
-            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
-
-            // Fetch the module Id loaded entry.
-            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Sanity!
-            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleGroup() - Cannot find module to unload it." );
-
-            // Dequeue module loaded.
-            mModulesLoaded.erase_fast( moduleLoadedItr );
-
-            // Fetch scope set.
-            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
-
-            // Is the destroy method available?
-            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
-            {
-                // Yes, so call the destroy method.
-                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
-            }
-
-            // Remove scope set.
-            pScopeSet->deleteObjects();
-            pScopeSet->unregisterObject();
-            pLoadReadyModuleDefinition->mScopeSet = 0;
-
-            // Remove path expando for module.
-            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Bump modules unloaded count.
-            modulesUnloadedCount++;
-
-            // Raise notifications.
-            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish unloading '%d' module(s) for group '%s'.", modulesUnloadedCount, moduleGroup );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 versionId )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot load explicit module Id with NULL module Id." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot load explicit module Id '%s' as it does not exist.", moduleId );
-        return false;
-    }
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d':", moduleId, versionId );
-    }
-
-    // Finish if we could not resolve the dependencies for module Id (of any version Id).
-    if ( !resolveModuleDependencies( moduleId, versionId, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-        return false;
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as the module Id is already loaded but at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        // Info.
-        Con::printf( "Module Manager: Explicit load of module Id '%s' at version Id '%d' and its dependencies is comprised of the following '%d' module(s):", moduleId, versionId, moduleReadyQueue.size() );
-
-        // Iterate the modules echoing them.
-        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-        {
-            // Fetch the ready entry.
-            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-            // Info.
-            Con::printf( "> module Id '%s' at version Id '%d'", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-        }
-    }
-
-    // Reset modules loaded count.
-    U32 modulesLoadedCount = 0;
-
-    // Iterate the modules, executing their script files and call their create function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module already loaded.
-        if ( pLoadedEntry != NULL )
-        {
-            // Yes, so increase load count.
-            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
-
-            // Skip.
-            continue;
-        }
-
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printSeparator();
-            Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d' using the script file '%s'.",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-        }
-
-        // Is the module deprecated?
-        if ( pLoadReadyModuleDefinition->getDeprecated() )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' is deprecated,  You should use a newer version!",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-        }
-
-        // Add the path expando for module.
-        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
-
-        // Create a scope set.
-        SimSet* pScopeSet = new SimSet;
-        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
-        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
-
-        // Increase load count.
-        pReadyEntry->mpModuleDefinition->increaseLoadCount();
-
-        // Queue module loaded.
-        mModulesLoaded.push_back( *pReadyEntry );
-
-        // Bump modules loaded count.
-        modulesLoadedCount++;
-
-        // Raise notifications.
-        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
-
-        // Do we have a script file-path specified?
-        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
-        {
-            // Yes, so execute the script file.
-            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
-
-            // Did we execute the script file?
-            if ( scriptFileExecuted )
-            {
-                // Yes, so is the create method available?
-                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
-                {
-                    // Yes, so call the create method.
-                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
-                }
-            }
-            else
-            {
-                // No, so warn.
-                Con::errorf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-            }
-        }
-
-        // Raise notifications.
-        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish loading '%d' explicit module(s).", modulesLoadedCount );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::unloadModuleExplicit( const char* pModuleId )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot unload explicit module Id with NULL module Id." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it does not exist.", moduleId );
-        return false;
-    }
-
-    // Find if the module is actually loaded.
-    ModuleDefinition* pLoadedModule = findLoadedModule( moduleId );
-
-    // Is the module loaded?
-    if ( pLoadedModule == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it is not loaded.", moduleId );
-        return false;
-    }
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Unloading explicit module Id '%s':" , moduleId );
-    }
-
-    // Finish if we could not resolve the dependencies for module Id (of any version Id).
-    if ( !resolveModuleDependencies( moduleId, pLoadedModule->getVersionId(), moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-        return false;
-
-    // Check the modules we want to unload to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' at version Id '%d' as the module Id is loaded but at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Reset modules unloaded count.
-    U32 modulesUnloadedCount = 0;
-
-    // Iterate the modules in reverse order calling their destroy function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module loaded.
-        if ( pLoadedEntry == NULL )
-        {
-            // No, so warn.
-            if ( mEchoInfo )
-            {
-                Con::printf( "Module Manager: Unloading explicit module Id '%s' at version Id '%d' but ignoring as it is not loaded.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-            }
-
-            // Skip.
-            continue;
-        }
-
-        // Reduce load count.
-        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
-
-        // Sanity!
-        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
-
-        // Do we need to unload?
-        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
-        {
-            // Yes, so info.
-            if ( mEchoInfo )
-            {
-                Con::printSeparator();
-                Con::printf( "Module Manager: Unload explicit module Id '%s' at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-            }
-
-            // Raise notifications.
-            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
-
-            // Fetch the module Id loaded entry.
-            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Sanity!
-            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleExplicit() - Cannot find module to unload it." );
-
-            // Dequeue module loaded.
-            mModulesLoaded.erase_fast( moduleLoadedItr );
-
-            // Fetch scope set.
-            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
-
-            // Is the destroy method available?
-            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
-            {
-                // Yes, so call the destroy method.
-                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
-            }
-
-            // Remove scope set.
-            pScopeSet->deleteObjects();
-            pScopeSet->unregisterObject();
-            pLoadReadyModuleDefinition->mScopeSet = 0;
-
-            // Remove path expando for module.
-            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Bump modules unloaded count.
-            modulesUnloadedCount++;
-
-            // Raise notifications.
-            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish unloading '%d' explicit module(s).", modulesUnloadedCount );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleDefinition* ModuleManager::findModule( const char* pModuleId, const U32 versionId )
-{
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
-
-    // Find module definition.
-    ModuleDefinitionEntry::iterator moduleItr = findModuleDefinition( StringTable->insert( pModuleId ), versionId );
-
-     // Finish if module was not found.
-    if ( moduleItr == NULL )
-        return NULL;
-
-    return *moduleItr;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleDefinition* ModuleManager::findLoadedModule( const char* pModuleId )
-{
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Iterate loaded modules.
-    for ( typeModuleLoadEntryVector::iterator loadedModuleItr = mModulesLoaded.begin(); loadedModuleItr != mModulesLoaded.end(); ++loadedModuleItr )
-    {
-        // Skip if not the module.
-        if ( loadedModuleItr->mpModuleDefinition->getModuleId() != moduleId )
-            continue;
-
-        return loadedModuleItr->mpModuleDefinition;
-    }
-
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::findModules( const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
-{
-    // Iterate module Ids.
-    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
-    {
-        // Fetch module definition entry.
-        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
-
-        // Iterate module definitions.
-        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
-
-            // Are we searching for loaded modules only?
-            if ( loadedOnly )
-            {
-                // Yes, so skip if the module is not loaded.
-                if ( pModuleDefinition->getLoadCount() == 0 )
-                    continue;
-
-                // Use module definition.
-                moduleDefinitions.push_back( pModuleDefinition );
-
-                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
-                break;
-            }
-
-            // use module definition.
-            moduleDefinitions.push_back( pModuleDefinition );
-        }
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::findModuleTypes( const char* pModuleType, const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
-{
-    // Fetch module type.
-    StringTableEntry moduleType = StringTable->insert( pModuleType );
-
-    // Iterate module Ids.
-    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
-    {
-        // Fetch module definition entry.
-        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
-
-        // Skip if note the module type we're searching for.
-        if ( pModuleDefinitionEntry->mModuleType != moduleType )
-            continue;
-
-        // Iterate module definitions.
-        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
-
-            // Are we searching for loaded modules only?
-            if ( loadedOnly )
-            {
-                // Yes, so skip if the module is not loaded.
-                if ( pModuleDefinition->getLoadCount() == 0 )
-                    continue;
-
-                // Use module definition.
-                moduleDefinitions.push_back( pModuleDefinition );
-
-                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
-                break;
-            }
-
-            // use module definition.
-            moduleDefinitions.push_back( pModuleDefinition );
-        }
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-StringTableEntry ModuleManager::copyModule( ModuleDefinition* pSourceModuleDefinition, const char* pTargetModuleId, const char* pTargetPath, const bool useVersionPathing )
-{
-    // Sanity!
-    AssertFatal( pSourceModuleDefinition != NULL, "Cannot copy module using a NULL source module definition." );
-    AssertFatal( pTargetModuleId != NULL, "Cannot copy module using a NULL target module Id." );
-    AssertFatal( pTargetPath != NULL, "Cannot copy module using a NULL target path." );
-
-    // Fetch the source module Id.
-    StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-    // Is the source module definition registered with this module manager?
-    if ( pSourceModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' as it is not registered with this module manager.", sourceModuleId );
-        return StringTable->EmptyString;
-    }
-
-    // Fetch the target module Id.
-    StringTableEntry targetModuleId = StringTable->insert( pTargetModuleId );
-
-    // Extend moduleId/VersionId pathing.
-    char versionPathBuffer[1024];
-
-    // Are we using version pathing?
-    if ( useVersionPathing )
-    {
-        // Yes, so format it.
-        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s/%s/%d",
-            pTargetPath, targetModuleId, pSourceModuleDefinition->getVersionId() );
-    }
-    else
-    {
-        // No, so a straight copy.
-        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s", pTargetPath );
-    }
-
-    // Expand the path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), versionPathBuffer );
-    pTargetPath = targetPathBuffer;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Started copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
-    }
-
-    // Is the target folder a directory?
-    if ( !Platform::isDirectory( pTargetPath ) )
-    {
-        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
-        char createDirectoryBuffer[1024];
-        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetPath, NULL, true );
-
-        // No, so can we create it?
-        if ( !Platform::createPath( createDirectoryBuffer ) )
-        {
-            // No, so warn.
-            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory was not found and could not be created.",
-                sourceModuleId, pTargetPath );
-            return StringTable->EmptyString;
-        }
-    }
-
-    // Copy the source module to the target folder.
-    if ( !Platform::pathCopy( pSourceModuleDefinition->getModulePath(), pTargetPath, false ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory copy failed.",
-            sourceModuleId, pTargetPath );
-        return StringTable->EmptyString;
-    }
-
-    // Format the new source module definition file-path.
-    char newModuleDefinitionSourceFileBuffer[1024];
-    dSprintf( newModuleDefinitionSourceFileBuffer, sizeof(newModuleDefinitionSourceFileBuffer), "%s/%s", pTargetPath, pSourceModuleDefinition->getModuleFile() );
-
-    // Finish if source/target module Ids are identical.
-    if ( sourceModuleId == targetModuleId )
-        return StringTable->insert( newModuleDefinitionSourceFileBuffer );
-
-    // Format the new target module definition file-path.
-    char newModuleDefinitionTargetFileBuffer[1024];
-    dSprintf( newModuleDefinitionTargetFileBuffer, sizeof(newModuleDefinitionTargetFileBuffer), "%s/%s.%s", pTargetPath, targetModuleId, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
-
-    // Rename the module definition.
-    if ( !Platform::fileRename( newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as renaming the module from '%s' to '%s' failed.",
-            sourceModuleId, pTargetPath, newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer );
-        return StringTable->EmptyString;
-    }
-
-    Vector<StringTableEntry> directories;
-
-    // Find directories.
-    if ( !Platform::dumpDirectories( pTargetPath, directories, -1 ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
-            sourceModuleId, pTargetPath );
-        return StringTable->EmptyString;
-    }
-
-    TamlModuleIdUpdateVisitor moduleIdUpdateVisitor;
-    moduleIdUpdateVisitor.setModuleIdFrom( sourceModuleId );
-    moduleIdUpdateVisitor.setModuleIdTo( targetModuleId );
-
-    Vector<Platform::FileInfo> files;
-
-    const char* pExtension = (const char*)"Taml";
-    const U32 extensionLength = dStrlen(pExtension);
-
-    // Iterate directories.
-    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
-    {
-        // Fetch base path.
-        StringTableEntry basePath = *basePathItr;
-
-        // Find files.
-        files.clear();
-        if ( !Platform::dumpPath( basePath, files, 0 ) )
-        {
-            // Warn.
-            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
-                sourceModuleId, pTargetPath );
-            return StringTable->EmptyString;
-        }
-
-        // Iterate files.
-        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
-        {
-            // Fetch file info.
-            Platform::FileInfo* pFileInfo = fileItr;
-
-            // Fetch filename.
-            const char* pFilename = pFileInfo->pFileName;
-
-            // Find filename length.
-            const U32 filenameLength = dStrlen( pFilename );
-
-            // Skip if extension is longer than filename.
-            if ( extensionLength >= filenameLength )
-                continue;
-
-            // Skip if extension not found.
-            if ( dStricmp( pFilename + filenameLength - extensionLength, pExtension ) != 0 )
-                continue;
-
-            char parseFileBuffer[1024];
-            dSprintf( parseFileBuffer, sizeof(parseFileBuffer), "%s/%s", pFileInfo->pFullPath, pFilename );
-
-            // Parse file.
-            if ( !moduleIdUpdateVisitor.parse( parseFileBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Module Manager: Failed to parse file '%s' whilst copying module Id '%s' using target directory '%s'.",
-                    parseFileBuffer, sourceModuleId, pTargetPath );
-                return StringTable->EmptyString;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Finished copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
-    }
-
-    return StringTable->insert( newModuleDefinitionTargetFileBuffer );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::synchronizeDependencies( ModuleDefinition* pRootModuleDefinition, const char* pTargetDependencyPath )
-{
-    // Sanity!
-    AssertFatal( pRootModuleDefinition != NULL, "Cannot synchronize dependencies with NULL root module definition." );
-    AssertFatal( pTargetDependencyPath != NULL, "Cannot synchronize dependencies with NULL target dependency path." );
-
-    // Fetch the root module Id.
-    StringTableEntry rootModuleId = pRootModuleDefinition->getModuleId();
-
-    // Is the root module definition registered with this module manager?
-    if ( pRootModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Cannot synchronize dependencies for module Id '%s' as it is not registered with this module manager.", rootModuleId );
-        return false;
-    }
-
-    // Expand the path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pTargetDependencyPath );
-    pTargetDependencyPath = targetPathBuffer;
-
-    // Is the target dependency folder a directory?
-    if ( !Platform::isDirectory( pTargetDependencyPath ) )
-    {
-        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
-        char createDirectoryBuffer[1024];
-        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetDependencyPath, NULL, true );
-
-        // No, so can we create it?
-        if ( !Platform::createPath( createDirectoryBuffer ) )
-        {
-            // No, so warn.
-            Con::warnf("Cannot synchronize dependencies for module Id '%s' using target directory '%s' as directory was not found and could not be created.",
-                rootModuleId, pTargetDependencyPath );
-            return false;
-        }
-    }
-
-    typeModuleLoadEntryVector       resolvingQueue;
-    typeModuleLoadEntryVector       sourceModulesNeeded;
-
-    // Could we resolve source dependencies?
-    if ( !resolveModuleDependencies( rootModuleId, pRootModuleDefinition->getVersionId(), pRootModuleDefinition->getModuleGroup(), true, resolvingQueue, sourceModulesNeeded ) )
-    {
-        // No, so warn.
-        Con::warnf("Cannot synchronize dependencies for root module Id '%s' as its dependencies could not be resolved.", rootModuleId );
-        return false;
-    }
-
-    // Sanity!
-    AssertFatal( sourceModulesNeeded.size() > 0, "Cannot synchronize dependencies as no modules were returned." );
-
-    // Remove the root module definition.
-    sourceModulesNeeded.pop_back();
-
-    // Initialize the target module manager and scan the target folder for modules.
-    ModuleManager targetModuleManager;
-    targetModuleManager.mEnforceDependencies = true;
-    targetModuleManager.mEchoInfo = false;
-    targetModuleManager.scanModules( pTargetDependencyPath );
-
-    char targetFolderGenerateBuffer[1024];
-
-    // Iterate module definitions.
-    for ( typeModuleLoadEntryVector::iterator sourceModuleItr = sourceModulesNeeded.begin(); sourceModuleItr != sourceModulesNeeded.end(); ++sourceModuleItr )
-    {
-        // Fetch module definition.
-        ModuleDefinition* pSourceModuleDefinition = sourceModuleItr->mpModuleDefinition;
-        
-        // Fetch the source module Id,
-        StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-        // Fetch the source module version Id.
-        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
-
-        // Fetch the source module build Id.
-        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
-
-        // Fetch module definition entry for this module Id in the target.
-        ModuleDefinitionEntry* pDefinitions = targetModuleManager.findModuleId( sourceModuleId );
-
-        // Is the module Id present in the target?
-        if ( pDefinitions == NULL )
-        {
-            // No, so format module Id folder path.
-            dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/", pTargetDependencyPath, sourceModuleId );
-
-            // Create module Id folder.
-            if ( !Platform::createPath( targetFolderGenerateBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Cannot synchronize dependencies for module Id '%s' as the target directory '%s' could not be created.", sourceModuleId, targetFolderGenerateBuffer );
-                return false;
-            }
-        }
-        else
-        {
-            // Yes, so fetch the module definition for this module Id and version Id in the target.
-            ModuleDefinitionEntry::iterator definitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
-
-            // Is the specific module definition present in the target?
-            if ( definitionItr != NULL )
-            {
-                // Yes, so fetch the module definition.
-                ModuleDefinition* pTargetModuleDefinition = *definitionItr;
-
-                // Fetch the target module build Id.
-                const U32 targetBuildId = pTargetModuleDefinition->getBuildId();
-
-                // Fetch the target module path.
-                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
-
-                // Remove the target module definition from the database.
-                targetModuleManager.removeModuleDefinition( pTargetModuleDefinition );
-
-                // Skip if the target definition is the same build Id.
-                if ( targetBuildId == sourceBuildId )
-                    continue;
-
-                // Delete the target module definition folder.
-                if ( !Platform::deleteDirectory( targetModulePath ) )
-                {
-                    // Warn.
-                    Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the old module at '%s' could not be deleted.",
-                        sourceModuleId, sourceVersionId, targetModulePath );
-                    return false;
-                }
-            }
-        }
-
-        // Format source module path.
-        char sourceFolderPath[1024];
-        Con::expandPath( sourceFolderPath, sizeof(sourceFolderPath), pSourceModuleDefinition->getModulePath() );
-
-        // Format target module path.
-        dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/%d", pTargetDependencyPath, sourceModuleId, sourceVersionId );
-
-        // Copy the source module to the target folder.
-        if ( !Platform::pathCopy( sourceFolderPath, targetFolderGenerateBuffer, false ) )
-        {
-            // Warn.
-            Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the module could not be copied to '%s'.",
-                sourceModuleId, sourceVersionId, targetFolderGenerateBuffer );
-            return false;
-        }
-    }
-
-    // Find any target modules left, These are orphaned modules not depended upon by any other module.
-    typeConstModuleDefinitionVector orphanedTargetModules;
-    targetModuleManager.findModules( false, orphanedTargetModules );
-
-    // Iterate module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = orphanedTargetModules.begin(); moduleDefinitionItr != orphanedTargetModules.end(); ++moduleDefinitionItr )
-    {
-        // Fetch orphaned module definition.
-        const ModuleDefinition* pOrphanedModuleDefinition = *moduleDefinitionItr;
-       
-        // Delete the target module definition folder.
-        if ( !Platform::deleteDirectory( pOrphanedModuleDefinition->getModulePath() ) )
-        {
-            // Warn.
-            Con::warnf("Cannot delete orphaned module Id '%s' at version Id '%d' from '%s'.",
-                pOrphanedModuleDefinition->getModuleId(), pOrphanedModuleDefinition->getVersionId(), pOrphanedModuleDefinition->getModulePath() );
-        }
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::canMergeModules( const char* pMergeSourcePath )
-{
-    // Sanity!
-    AssertFatal( pMergeSourcePath != NULL, "Cannot check merge modules with NULL source path." );
-
-    // Expand the source path.
-    char sourcePathBuffer[1024];
-    Con::expandPath( sourcePathBuffer, sizeof(sourcePathBuffer), pMergeSourcePath );
-    pMergeSourcePath = sourcePathBuffer;
-
-    // Is the path a valid directory?
-    if ( !Platform::isDirectory( sourcePathBuffer ) )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot check merge modules as path is invalid '%s'.", sourcePathBuffer );
-        return false;
-    }
-
-    // Initialize the source module manager and scan the source folder for modules.
-    ModuleManager mergeModuleManager;
-    mergeModuleManager.mEnforceDependencies = false;
-    mergeModuleManager.mEchoInfo = false;
-    mergeModuleManager.scanModules( pMergeSourcePath );
-
-    // Find all the merge modules.
-    typeConstModuleDefinitionVector mergeModules;
-    mergeModuleManager.findModules( false, mergeModules );
-
-    // Iterate found merge module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator mergeModuleItr = mergeModules.begin(); mergeModuleItr != mergeModules.end(); ++mergeModuleItr )
-    {
-        // Fetch module definition.
-        const ModuleDefinition* pMergeModuleDefinition = *mergeModuleItr;
-
-        // Fetch module Id.
-        StringTableEntry moduleId = pMergeModuleDefinition->getModuleId();
-
-        // Fetch version Id.
-        const U32 versionId = pMergeModuleDefinition->getVersionId();
-
-        // Fetch module group.
-        StringTableEntry moduleGroup = pMergeModuleDefinition->getModuleGroup();
-
-        // Cannot merge if module already exists.
-        if ( findModuleDefinition( moduleId, versionId ) != NULL )
-            return false;
-
-        // Cannot merge if module is part of a loaded group.
-        if ( findGroupLoaded( moduleGroup ) != NULL )
-            return false;
-    }
-
-    // Can merge modules.
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::mergeModules( const char* pMergeTargetPath, const bool removeMergeDefinition, const bool registerNewModules )
-{
-    // Sanity!
-    AssertFatal( pMergeTargetPath != NULL, "Cannot merge modules with a target path of NULL." );
-
-    // Is a module merge available?
-    if ( !isModuleMergeAvailable() )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot merge modules as a module merge is not available." );
-        return false;
-    }
-
-    // Expand the target path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pMergeTargetPath );
-    pMergeTargetPath = targetPathBuffer;
-
-    // Fetch module merge file path.
-    StringTableEntry moduleMergeFilePath = getModuleMergeFilePath();
-
-    // Read module merge definition.
-    Taml taml;
-    ModuleMergeDefinition* pModuleMergeDefinition = taml.read<ModuleMergeDefinition>( moduleMergeFilePath );
-    
-    // Do we have a module merge definition.
-    if ( pModuleMergeDefinition == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot merge modules as the module merge definition file failed to load '%s'.", moduleMergeFilePath );
-        return false;
-    }
-
-    // Fetch the merge source path.
-    StringTableEntry mergeSourcePath = pModuleMergeDefinition->getModuleMergePath();
-
-    // Remove the module merge definition.
-    pModuleMergeDefinition->deleteObject();
-    pModuleMergeDefinition = NULL;
-
-    // If we cannot merge the modules then we only process modules flagged as critical merge.
-    const bool criticalMergeOnly = !canMergeModules( mergeSourcePath );
-
-    // Initialize the target module manager and scan the target folder for modules.
-    ModuleManager targetModuleManager;
-    targetModuleManager.mEnforceDependencies = false;
-    targetModuleManager.mEchoInfo = false;
-    targetModuleManager.scanModules( pMergeTargetPath );
-
-    // Initialize the source module manager and scan the source folder for modules.
-    ModuleManager sourceModuleManager;
-    sourceModuleManager.mEnforceDependencies = false;
-    sourceModuleManager.mEchoInfo = false;
-    sourceModuleManager.scanModules( mergeSourcePath );
-
-    // Find all the source modules.
-    typeConstModuleDefinitionVector sourceModules;
-    sourceModuleManager.findModules( false, sourceModules );
-
-    // Iterate found merge module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator sourceModuleItr = sourceModules.begin(); sourceModuleItr != sourceModules.end(); ++sourceModuleItr )
-    {
-        // Fetch the source module definition.
-        const ModuleDefinition* pSourceModuleDefinition = *sourceModuleItr;
-
-        // Skip if we're performing a critical merge only and the module is not flagged as critical merge.
-        if ( criticalMergeOnly && pSourceModuleDefinition->getCriticalMerge() )
-            continue;
-
-        // Fetch source module Id.
-        const StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-        // Fetch source version Id.
-        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
-
-        // Fetch source build Id.
-        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
-
-        // Format module Id folder path.
-        char targetModuleIdBuffer[1024];
-        dSprintf( targetModuleIdBuffer, sizeof(targetModuleIdBuffer), "%s/%s/", pMergeTargetPath, sourceModuleId );
-
-        // Flag to indicate if the merged module needs registering.
-        bool shouldRegisterModule;
-
-        // Does the module Id exist?
-        if ( targetModuleManager.findModuleId( sourceModuleId ) == NULL )
-        {
-            // No, so create module Id folder.
-            if ( !Platform::createPath( targetModuleIdBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Cannot merge modules for module '%s' as the path '%s' could not be created.", sourceModuleId, targetModuleIdBuffer );
-                return false;
-            }
-
-            // Module Should be registered.
-            shouldRegisterModule = true;
-        }
-        else
-        {
-            // Yes, so find the target module definition that matches the source module definition.
-            ModuleDefinitionEntry::iterator targetModuleDefinitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
-
-            // Is there an existing target module definition entry?
-            if ( targetModuleDefinitionItr != NULL )
-            {
-                // Yes, so fetch the target module definition.
-                const ModuleDefinition* pTargetModuleDefinition = *targetModuleDefinitionItr;
-
-                // Fetch target module path.
-                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
-
-                // Yes, so we have to remove it first.
-                if ( !Platform::deleteDirectory( targetModulePath ) )
-                {
-                    // Module was not deleted so warn.
-                    Con::warnf( "Failed to remove module folder located at '%s'.  Module will be copied over.", targetModulePath );
-                }
-
-                // Is the build Id being downgraded?
-                if ( sourceBuildId < pTargetModuleDefinition->getBuildId() )
-                {
-                    // Yes, so warn.
-                    Con::warnf( "Encountered a downgraded build Id for module Id '%s' at version Id '%d'.", sourceModuleId, sourceBuildId );
-                }
-
-                // Module should not be registered.
-                shouldRegisterModule = false;
-            }
-            else
-            {
-                // Module Should be registered.
-                shouldRegisterModule = true;
-            }
-        }
-
-        // Fetch source module path.
-        StringTableEntry sourceModulePath = pSourceModuleDefinition->getModulePath();
-
-        // Format target version Id folder path.
-        char targetVersionIdBuffer[1024];
-        dSprintf( targetVersionIdBuffer, sizeof(targetVersionIdBuffer), "%s%d", targetModuleIdBuffer, sourceVersionId );
-
-        // Copy module (allow overwrites as we may have failed to remove the old folder in which case this is likely to fail as well).
-        if ( !Platform::pathCopy( sourceModulePath, targetVersionIdBuffer, false ) )
-        {
-            // Failed to copy module.
-            Con::warnf( "Failed to copy module folder located at '%s' to location '%s'.  The modules may now be corrupted.", sourceModulePath, targetVersionIdBuffer );
-        }
-
-        // Are we registering new modules and the module needs registering?
-        if ( registerNewModules && shouldRegisterModule )
-        {
-            // Yes, so scan module.
-            scanModules( targetVersionIdBuffer, true );
-        }
-
-        // Is the module part of a critical merge?
-        if ( criticalMergeOnly )
-        {
-            // Yes, so we need to remove the source module definition.
-            if ( !Platform::deleteDirectory( sourceModulePath ) )
-            {
-                // Module was not deleted so warn.
-                Con::warnf( "Failed to remove CRITICAL merge module folder located at '%s'.  Module will be copied over.", sourceModulePath );
-            }
-        }
-    }
-
-    // Do we need to remove the module merge definition file?
-    if ( removeMergeDefinition )
-    {
-        // Yes, so remove it.
-        Platform::fileDelete( moduleMergeFilePath );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::addListener( SimObject* pListener )
-{
-    // Sanity!
-    AssertFatal( pListener != NULL, "Cannot add notifications to a NULL object." );
-
-    // Ignore if already added.
-    if ( mNotificationListeners.find( pListener ) != mNotificationListeners.end() )
-        return;
-        
-    // Add as a listener.
-    mNotificationListeners.addObject( pListener );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::removeListener( SimObject* pListener )
-{
-    // Sanity!
-    AssertFatal( pListener != NULL, "Cannot remove notifications from a NULL object." );
-
-    // Remove as a listener.
-    mNotificationListeners.removeObject( pListener );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::clearDatabase( void )
-{
-    // Lock database.
-    AssertFatal( mDatabaseLocks == 0, "Cannot clear database if database is locked." );
-
-    // Iterate groups loaded.
-    while ( mGroupsLoaded.size() > 0 )
-    {
-        // Unload module group.
-        unloadModuleGroup( *mGroupsLoaded.begin() );
-    }
-
-    // Iterate any other explicit modules that are loaded.
-    while ( mModulesLoaded.size() > 0 )
-    {
-        // Fetch module definition.
-        ModuleDefinition* pModuleDefinition = mModulesLoaded.begin()->mpModuleDefinition;
-
-        // Unload explicit module.
-        unloadModuleExplicit( pModuleDefinition->getModuleId() );
-    }
-
-    // Iterate modules to delete module definitions.
-    for ( typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.begin(); moduleItr != mModuleIdDatabase.end(); ++moduleItr )
-    {
-        // Fetch modules definitions.
-        ModuleDefinitionEntry* pDefinitions = moduleItr->value;
-
-        // Iterate module definitions.
-        for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *definitionItr;
-
-            // Remove notification before we delete it.
-            clearNotify( pModuleDefinition );
-
-            // Delete module definition.
-            pModuleDefinition->deleteObject();
-        }
-
-        // Clear definitions.
-        delete pDefinitions;        
-    }
-
-    // Clear database.
-    mModuleIdDatabase.clear();
-
-    // Iterate module groups.
-    for ( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
-    {
-        // Delete module group vector.
-        delete moduleGroupItr->value;
-    }
-
-    // Clear module groups.
-    mGroupModules.clear();
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::removeModuleDefinition( ModuleDefinition* pModuleDefinition )
-{
-    // Sanity!
-    AssertFatal( pModuleDefinition != NULL, "Cannot remove module definition if it is NULL." );
-    
-    // Fetch module Id.
-    StringTableEntry moduleId = pModuleDefinition->getModuleId();
-
-    // Is the module definition registered with this module manager?
-    if ( pModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Cannot remove module definition '%s' as it is not registered with this module manager.", moduleId );
-        return false;
-    }
-
-    // Is the module definition loaded?
-    if ( pModuleDefinition->getLoadCount() > 0 )
-    {
-        // No, so warn.
-        Con::warnf("Cannot remove module definition '%s' as it is loaded.", moduleId );
-        return false;
-    }
-
-    // Find module Id.
-    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
-
-    // Sanity!
-    AssertFatal( moduleItr != mModuleIdDatabase.end(), "Failed to find module definition." );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = moduleItr->value;
-
-    // Fetch version Id.
-    const U32 versionId = pModuleDefinition->getVersionId();
-
-    // Iterate module definitions.
-    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-    {
-        // Skip if this isn't the version Id we're searching for.
-        if ( versionId != (*definitionItr)->getVersionId() )
-            continue;
-
-        // Remove definition entry.
-        pDefinitions->erase( definitionItr ); 
-
-        // Remove notification before we delete it.
-        clearNotify( pModuleDefinition );
-
-        // Delete module definition.
-        pModuleDefinition->deleteObject();
-
-        return true;
-    }
-
-    // Sanity!
-    AssertFatal( false, "Failed to find module definition." );
-
-    return false;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::registerModule( const char* pModulePath, const char* pModuleFile )
-{
-    // Sanity!
-    AssertFatal( pModulePath != NULL, "Cannot scan module with NULL module path." );
-    AssertFatal( pModuleFile != NULL, "Cannot scan module with NULL module file." );
-
-    // Make the module path a full-path.
-    char fullPathBuffer[1024];
-    Platform::makeFullPathName( pModulePath, fullPathBuffer, sizeof(fullPathBuffer) );
-    pModulePath = fullPathBuffer;
-
-
-    char formatBuffer[1024];
-
-    // Fetch module path trail character.
-    char modulePathTrail = pModulePath[dStrlen(pModulePath) - 1];
-
-    // Format module file-path.
-    dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleFile );
-
-    // Read the module file.
-    ModuleDefinition* pModuleDefinition = mTaml.read<ModuleDefinition>( formatBuffer );
-
-    // Did we read a module definition?
-    if ( pModuleDefinition == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Failed to read module definition in file '%s'.", formatBuffer );
-        return false;
-    }
-
-    // Set the module manager.
-    pModuleDefinition->setModuleManager( this );
-
-    // Set module definition path.
-    pModuleDefinition->setModulePath( pModulePath );
-
-    // Set module file.
-    pModuleDefinition->setModuleFile( pModuleFile );
-
-    // Set module file-path.
-    pModuleDefinition->setModuleFilePath( formatBuffer );
-
-    // Fetch module Id.
-    StringTableEntry moduleId = pModuleDefinition->getModuleId();
-
-    // Fetch module version Id.
-    const U32 versionId = pModuleDefinition->getVersionId();
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pModuleDefinition->getModuleGroup();
-
-    // Fetch module type.
-    StringTableEntry moduleType = pModuleDefinition->getModuleType();
-
-    // Is the module enabled?
-    if ( !pModuleDefinition->getEnabled() )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it is disabled.", pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module Id valid?
-    if ( moduleId == StringTable->EmptyString )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it has an unspecified module Id.",
-            pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module version Id valid?
-    if ( versionId == 0 )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found Manager: Registering module: '%s' but it has an invalid Version Id of '0'.",
-            pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module group already loaded?
-    if ( findGroupLoaded( moduleGroup ) != NULL )
-    {
-        // Yes, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it is in a module group '%s' which has already been loaded.",
-            pModuleDefinition->getModuleFilePath(),
-            moduleGroup );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Was a script-file specified?
-    if ( pModuleDefinition->getScriptFile() != StringTable->EmptyString )
-    {
-        // Yes, so format module script file-path.
-        dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleDefinition->getScriptFile() );
-        pModuleDefinition->setModuleScriptFilePath( formatBuffer );
-    }
-
-    // Format module signature,
-    dSprintf( formatBuffer, sizeof(formatBuffer), "%s_%d_%d", moduleId, versionId, pModuleDefinition->getBuildId() );
-    pModuleDefinition->setSignature( formatBuffer );
-
-    // Locked the module definition.
-    pModuleDefinition->setLocked( true );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions != NULL )
-    {
-        // Yes, so find the module definition.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Does this version Id already exist?
-        if ( definitionItr != NULL )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but it is already registered as module Id '%s' at version Id '%d'.",
-                pModuleDefinition->getModuleFilePath(), moduleId, versionId );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-
-        // Is the module group the same as the module definitions we already have?
-        if ( moduleGroup != pDefinitions->mModuleGroup )
-        {
-            // No, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but its module group '%s' is not the same as other module definitions of the same module Id.",
-                pModuleDefinition->getModuleFilePath(), moduleGroup );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-
-        // Is the module type the same as the module definitions we already have?
-        if ( moduleType != pDefinitions->mModuleType )
-        {
-            // No, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but its module type '%s' is not the same as other module definitions of the same module Id.",
-                pModuleDefinition->getModuleFilePath(), moduleGroup );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-    }
-    else
-    {
-        // No, so create a vector of definitions.
-        pDefinitions = new ModuleDefinitionEntry( moduleId, moduleGroup, moduleType );
-
-        // Insert module Id definitions.
-        mModuleIdDatabase.insert( moduleId, pDefinitions );
-    }
-    
-    // Add module definition.
-    pDefinitions->push_back( pModuleDefinition );
-
-    // Sort module definitions by version Id so that higher versions appear first.
-    dQsort( pDefinitions->address(), pDefinitions->size(), sizeof(ModuleDefinition*), moduleDefinitionVersionIdSort );
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr != mGroupModules.end() )
-    {
-        // Yes, so fetch module Ids.
-        typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-        // Is the module Id already present?
-        bool moduleIdFound = false;
-        for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-        {
-            // Skip if this isn't the Id.
-            if ( *moduleIdItr == moduleId )
-            {
-                moduleIdFound = true;
-                break;
-            }
-        }
-
-        // Add if module Id was not found.
-        if ( !moduleIdFound )
-            pModuleIds->push_back( moduleId );
-    }
-    else
-    {
-        // No, so insert a module Id vector.
-        moduleGroupItr = mGroupModules.insert( pModuleDefinition->getModuleGroup(), new typeModuleIdVector() );
-
-        // Add module Id.
-        moduleGroupItr->value->push_back( moduleId );
-    }
-
-    // Notify if the module definition is destroyed.
-    deleteNotify( pModuleDefinition );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-#if 1
-        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s' ].",
-            pModuleDefinition->getModuleFilePath(),
-            pModuleDefinition->getModuleId(),
-            pModuleDefinition->getVersionId(),
-            pModuleDefinition->getBuildId(),
-            pModuleDefinition->getModuleDescription()
-            );
-#else
-        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s', Group='%s', Dependencies='%s', ScriptFile='%s', CreateFunction='%s', DestroyFunction='%s' ].",
-            pModuleDefinition->getModuleFilePath(),
-            pModuleDefinition->getModuleId(),
-            pModuleDefinition->getVersionId(),
-            pModuleDefinition->getBuildId(),
-            pModuleDefinition->getModuleDescription(),
-            pModuleDefinition->getModuleGroup(),
-            pModuleDefinition->getDataField( StringTable->insert("Dependencies"), NULL ),
-            pModuleDefinition->getScriptFile(),
-            pModuleDefinition->getCreateFunction(),
-            pModuleDefinition->getDestroyFunction()
-            );
-#endif
-    }
-
-    // Emit notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        Con::executef( *notifyItr, 2, "onModuleRegister", pModuleDefinition->getIdString() );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePreLoadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePreLoad( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePreLoad" ) )
-            Con::executef( pListener, 2, "onModulePreLoad", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePostLoadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePostLoad( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePostLoad" ) )
-            Con::executef( pListener, 2, "onModulePostLoad", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePreUnloadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePreUnload( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePreUnload" ) )
-            Con::executef( pListener, 2, "onModulePreUnload", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePostUnloadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePostUnload( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePostUnload" ) )
-            Con::executef( pListener, 2, "onModulePostUnload", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleDefinitionEntry* ModuleManager::findModuleId( StringTableEntry moduleId )
-{
-    // Sanity!
-    AssertFatal( moduleId != NULL, "A module Id cannot be NULL." );
-
-    // Is the module Id valid?
-    if ( moduleId == StringTable->EmptyString )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Invalid Module Id." );
-        return NULL;
-    }
-
-    // Find module Id.
-    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
-
-    // Return appropriately.
-    return moduleItr != mModuleIdDatabase.end() ? moduleItr->value : NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleDefinitionEntry::iterator ModuleManager::findModuleDefinition( StringTableEntry moduleId, const U32 versionId )
-{
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Finish if no module definitions for the module Id.
-    if ( pDefinitions == NULL )
-        return NULL;
-
-    // Iterate module definitions.
-    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-    {
-        // Skip if this isn't the version Id we're searching for.
-        if ( versionId != (*definitionItr)->getVersionId() )
-            continue;
-
-        // Return module definition iterator.
-        return definitionItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::resolveModuleDependencies( StringTableEntry moduleId, const U32 versionId, StringTableEntry moduleGroup, bool synchronizedOnly, typeModuleLoadEntryVector& moduleResolvingQueue, typeModuleLoadEntryVector& moduleReadyQueue )
-{
-    // Fetch the module Id ready entry.
-    ModuleLoadEntry* pLoadReadyEntry = findModuleReady( moduleId, moduleReadyQueue );
-
-    // Is there a load entry?
-    if ( pLoadReadyEntry )
-    {
-        // Yes, so finish if the version Id is not important,
-        if ( versionId == 0 )
-            return true;
-
-        // Finish if the version Id are compatible.
-        if ( versionId == pLoadReadyEntry->mpModuleDefinition->getVersionId() )
-            return true;
-
-        // Is it a strict version Id?
-        if ( pLoadReadyEntry->mStrictVersionId )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: A module dependency was detected loading module Id '%s' at version Id '%d' in group '%s' but an version Id '%d' is also required.",
-                moduleId, versionId, pLoadReadyEntry->mpModuleDefinition->getVersionId(), moduleGroup );
-            return false;
-        }
-
-        // No, so find the required module version Id.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Did we find the requested module definition.
-        if ( definitionItr == NULL )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Set the new module definition.
-        pLoadReadyEntry->mpModuleDefinition = *definitionItr;
-
-        // Set strict version Id.
-        pLoadReadyEntry->mStrictVersionId = true;
-                
-        return true;
-    }
-
-    // Is the module Id load resolving?
-    if ( findModuleResolving( moduleId, moduleResolvingQueue ) != NULL )
-    {
-        // Yes, so a cycle has been detected so warn.
-        Con::warnf( "Module Manager: A cyclic dependency was detected resolving module Id '%s' at version Id '%d' in group '%s'.",
-            moduleId, versionId, moduleGroup );
-        return false;
-    }
-
-    // Reset selected module definition.
-    ModuleDefinition* pSelectedModuleDefinition = NULL;
-
-    // Do we want the latest version Id?
-    if ( versionId == 0 )
-    {
-        // Yes, so find the module Id.
-        typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.find( moduleId );
-
-        // Did we find the module Id?
-        if ( moduleIdItr == mModuleIdDatabase.end() )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Fetch first module definition which should be the highest version Id.
-        pSelectedModuleDefinition = (*moduleIdItr->value)[0];
-    }
-    else
-    {
-        // No, so find the module Id at the specific version Id.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Did we find the module definition?
-        if ( definitionItr == NULL )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Select the module definition.
-        pSelectedModuleDefinition = *definitionItr;
-    }
-
-    // If we're only resolving synchronized modules and the module is not synchronized then finish.
-    if ( synchronizedOnly && !pSelectedModuleDefinition->getSynchronized() )
-        return true;
-
-    // Create a load entry.
-    ModuleLoadEntry loadEntry( pSelectedModuleDefinition, false );
-
-    // Fetch module dependencies.
-    const ModuleDefinition::typeModuleDependencyVector& moduleDependencies = pSelectedModuleDefinition->getDependencies();
-
-    // Do we have any module dependencies?
-    if ( moduleDependencies.size() > 0 )
-    {
-        // Yes, so queue this module as resolving.
-        moduleResolvingQueue.push_back( loadEntry );
-
-        // Iterate module dependencies.
-        for( ModuleDefinition::typeModuleDependencyVector::const_iterator dependencyItr = moduleDependencies.begin(); dependencyItr != moduleDependencies.end(); ++dependencyItr )
-        {            
-            // Finish if we could not the dependent module Id at the version Id.
-            if ( !resolveModuleDependencies( dependencyItr->mModuleId, dependencyItr->mVersionId, moduleGroup, synchronizedOnly, moduleResolvingQueue, moduleReadyQueue ) )
-                return false;
-        }
-
-        // Remove module as resolving.
-        moduleResolvingQueue.pop_back();
-    }
-
-    // Queue module as ready.
-    moduleReadyQueue.push_back( loadEntry );        
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleLoadEntry* ModuleManager::findModuleResolving( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleResolvingQueue )
-{
-    // Iterate module load resolving queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleResolvingQueue.begin(); loadEntryItr != moduleResolvingQueue.end(); ++loadEntryItr )
-    {
-        // Finish if found.
-        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
-            return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleLoadEntry* ModuleManager::findModuleReady( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleReadyQueue )
-{
-    // Iterate module load ready queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleReadyQueue.begin(); loadEntryItr != moduleReadyQueue.end(); ++loadEntryItr )
-    {
-        // Finish if found.
-        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
-            return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::typeModuleLoadEntryVector::iterator ModuleManager::findModuleLoaded( StringTableEntry moduleId, const U32 versionId )
-{
-    // Iterate module loaded queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = mModulesLoaded.begin(); loadEntryItr != mModulesLoaded.end(); ++loadEntryItr )
-    {
-        // Skip if not the module Id we're looking for.
-        if ( moduleId != loadEntryItr->mpModuleDefinition->getModuleId() )
-            continue;
-
-        // Skip if we are searching for a specific version and it does not match.
-        if ( versionId != 0 && versionId != loadEntryItr->mpModuleDefinition->getVersionId() )
-            continue;
-
-        return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::typeGroupVector::iterator ModuleManager::findGroupLoaded( StringTableEntry moduleGroup )
-{
-    // Iterate groups loaded queue.
-    for( typeGroupVector::iterator groupsLoadedItr = mGroupsLoaded.begin(); groupsLoadedItr != mGroupsLoaded.end(); ++groupsLoadedItr )
-    {
-        // Finish if found.
-        if ( moduleGroup == *groupsLoadedItr )
-            return groupsLoadedItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-StringTableEntry ModuleManager::getModuleMergeFilePath( void ) const
-{
-    // Format merge file path.
-    char filePathBuffer[1024];
-    dSprintf( filePathBuffer, sizeof(filePathBuffer), "%s/%s", Platform::getExecutablePath(), MODULE_MANAGER_MERGE_FILE );
-
-    return StringTable->insert( filePathBuffer );
-}
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "moduleManager.h"
+
+#ifndef _MODULE_MERGE_DEFINITION_H
+#include "moduleMergeDefinition.h"
+#endif
+
+#ifndef _TAML_MODULE_ID_UPDATE_VISITOR_H_
+#include "tamlModuleIdUpdateVisitor.h"
+#endif
+
+#ifndef _MODULE_CALLBACKS_H_
+#include "moduleCallbacks.h"
+#endif
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+// Script bindings.
+#include "moduleManager_ScriptBinding.h"
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT( ModuleManager );
+
+//-----------------------------------------------------------------------------
+
+ModuleManager ModuleDatabase;
+
+//-----------------------------------------------------------------------------
+
+S32 QSORT_CALLBACK moduleDefinitionVersionIdSort( const void* a, const void* b )
+{
+    // Fetch module definitions.
+   ModuleDefinition* pDefinition1 = *(ModuleDefinition**)a;
+   ModuleDefinition* pDefinition2 = *(ModuleDefinition**)b;
+
+   // Fetch version Ids.
+   const U32 versionId1 = pDefinition1->getVersionId();
+   const U32 versionId2 = pDefinition2->getVersionId();
+
+   // We sort higher version Id first.
+   return versionId1 > versionId2 ? -1 : versionId1 < versionId2 ? 1 : 0;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleManager() :
+    mEnforceDependencies(true),
+    mEchoInfo(true),
+    mDatabaseLocks( 0 )
+{
+    // Set module extension.
+    dStrcpy( mModuleExtension, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::onAdd()
+{
+    if( !Parent::onAdd() )
+        return false;
+
+    // Register listeners.
+    mNotificationListeners.registerObject();
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::onRemove()
+{
+    // Clear database.
+    clearDatabase();
+
+    // Unregister object.
+    mNotificationListeners.unregisterObject();
+
+    // Call parent.
+    Parent::onRemove();
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    addField( "EnforceDependencies", TypeBool, Offset(mEnforceDependencies, ModuleManager), "Whether the module manager enforces any dependencies on module definitions it discovers or not." );
+    addField( "EchoInfo", TypeBool, Offset(mEchoInfo, ModuleManager), "Whether the module manager echos extra information to the console or not." );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::onDeleteNotify( SimObject *object )
+{
+    // Cast to a module definition.
+    ModuleDefinition* pModuleDefinition = dynamic_cast<ModuleDefinition*>( object );
+
+    // Ignore if not appropriate.
+    if ( pModuleDefinition == NULL )
+        return;
+
+    // Warn.
+    Con::warnf( "Module Manager::onDeleteNotify() - Notified of a module definition deletion for module Id '%s' of version Id '%d' however this should not happen and can cause module database corruption.",
+        pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::setModuleExtension( const char* pExtension )
+{
+    // Sanity!
+    AssertFatal( pExtension != NULL, "Cannot set module extension with NULL extension." );
+
+    // Did we find an extension period?
+    if ( *pExtension == '.' )
+    {
+        // Yes, so warn.
+        Con::warnf("Module Manager: Failed to set extension as supplied extension contains an initial period: '%s'.", pExtension );
+        return false;
+    }
+
+    // Is the extension too large?
+    if ( dStrlen( pExtension ) > sizeof( mModuleExtension ) )
+    {
+        // Yes, so warn.
+        Con::warnf("Module Manager: Failed to set extension as supplied extension is too large: '%s'.", pExtension );
+        return false;
+    }
+
+    // Set module extension.
+    dStrcpy( mModuleExtension, pExtension );
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::scanModules( const char* pPath, const bool rootOnly )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pPath != NULL, "Cannot scan module with NULL path." );
+
+    // Expand module location.
+    char pathBuffer[1024];
+    Con::expandPath( pathBuffer, sizeof(pathBuffer), pPath );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Started scanning '%s'...", pathBuffer );
+    }
+
+    Vector<StringTableEntry> directories;
+
+    // Find directories.
+    if ( !Platform::dumpDirectories( pathBuffer, directories, 1 ) )
+    {
+        // Failed so warn.
+        Con::warnf( "Module Manager: Failed to scan module directories in path '%s'.", pathBuffer );
+        return false;
+    }
+
+    // Fetch extension length.
+    const U32 extensionLength = dStrlen( mModuleExtension );
+
+    Vector<Platform::FileInfo> files;
+
+    // Iterate directories.
+    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
+    {
+        // Fetch base path.
+        StringTableEntry basePath = *basePathItr;
+
+        // Skip if we're only processing the root and this is not the root.
+        if ( rootOnly && basePathItr != directories.begin() )
+            continue;
+
+        // Find files.
+        files.clear();
+        if ( !Platform::dumpPath( basePath, files, 0 ) )
+        {
+            // Failed so warn.
+            Con::warnf( "Module Manager: Failed to scan modules files in directory '%s'.", basePath );
+            return false;
+        }
+
+        // Iterate files.
+        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
+        {
+            // Fetch file info.
+            Platform::FileInfo* pFileInfo = fileItr;
+
+            // Fetch filename.
+            const char* pFilename = pFileInfo->pFileName;
+
+            // Find filename length.
+            const U32 filenameLength = dStrlen( pFilename );
+
+            // Skip if extension is longer than filename.
+            if ( extensionLength > filenameLength )
+                continue;
+
+            // Skip if extension not found.
+            if ( dStricmp( pFilename + filenameLength - extensionLength, mModuleExtension ) != 0 )
+                continue;
+
+            // Register module.
+            registerModule( basePath, pFileInfo->pFileName );
+        }
+
+        // Stop processing if we're only processing the root.
+        if ( rootOnly )
+            break;
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Finished scanning '%s'.", pathBuffer );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::loadModuleGroup( const char* pModuleGroup )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleGroup != NULL, "Cannot load module group with NULL group name." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Loading group '%s':" ,moduleGroup );
+    }
+
+    // Is the module group already loaded?
+    if ( findGroupLoaded( moduleGroup ) != NULL )
+    {
+        // Yes, so warn.
+        Con::warnf( "Module Manager: Cannot load group '%s' as it is already loaded.", moduleGroup );
+        return false;
+    }
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr == mGroupModules.end() )
+    {
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
+        }
+        
+        return true;
+    }
+
+    // Yes, so fetch the module Ids.
+    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+    // Iterate module groups.
+    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+    {
+        // Fetch module Id.
+        StringTableEntry moduleId = *moduleIdItr;
+
+        // Finish if we could not resolve the dependencies for module Id (of any version Id).
+        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+            return false;
+    }
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is already loaded but at version Id '%d'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        // Info.
+        Con::printf( "Module Manager: Group '%s' and its dependencies is comprised of the following '%d' module(s):", moduleGroup, moduleReadyQueue.size() );
+
+        // Iterate the modules echoing them.
+        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+        {
+            // Fetch the ready entry.
+            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+            // Info.
+            Con::printf( "> module Id '%s' at version Id '%d':", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+        }
+    }
+
+    // Add module group.
+    mGroupsLoaded.push_back( moduleGroup );
+
+    // Reset modules loaded count.
+    U32 modulesLoadedCount = 0;
+
+    // Iterate the modules, executing their script files and call their create function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module already loaded.
+        if ( pLoadedEntry != NULL )
+        {
+            // Yes, so increase load count.
+            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
+
+            // Skip.
+            continue;
+        }
+
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printSeparator();
+            Con::printf( "Module Manager: Loading group '%s' : module Id '%s' at version Id '%d' in group '%s' using the script file '%s'.",
+                moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+        }
+
+        // Is the module deprecated?
+        if ( pLoadReadyModuleDefinition->getDeprecated() )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' in group '%s' is deprecated.  You should use a newer version!",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
+        }
+
+        // Add the path expando for module.
+        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
+
+        // Create a scope set.
+        SimSet* pScopeSet = new SimSet;
+        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
+        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
+
+        // Increase load count.
+        pReadyEntry->mpModuleDefinition->increaseLoadCount();
+
+        // Queue module loaded.
+        mModulesLoaded.push_back( *pReadyEntry );
+
+        // Bump modules loaded count.
+        modulesLoadedCount++;
+
+        // Raise notifications.
+        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
+
+        // Do we have a script file-path specified?
+        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
+        {
+            // Yes, so execute the script file.
+            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
+
+            // Did we execute the script file?
+            if ( scriptFileExecuted )
+            {
+                // Yes, so is the create method available?
+                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
+                {
+                    // Yes, so call the create method.
+                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
+                }
+            }
+            else
+            {
+                // No, so warn.
+                Con::errorf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+            }
+        }
+
+        // Raise notifications.
+        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish loading '%d' module(s) for group '%s'.", modulesLoadedCount, moduleGroup );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::unloadModuleGroup( const char* pModuleGroup )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleGroup != NULL, "Cannot unload module group with NULL group name." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Unloading group '%s':" , moduleGroup );
+    }
+
+    // Find the group loaded iterator.
+    typeGroupVector::iterator groupLoadedItr = findGroupLoaded( moduleGroup );
+
+    // Is the module group already unloaded?
+    if ( groupLoadedItr == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload group '%s' as it is not loaded.", moduleGroup );
+        return false;
+    }
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr == mGroupModules.end() )
+    {
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
+            return true;
+        }
+    }
+
+    // Yes, so fetch the module Ids.
+    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+    // Iterate module groups.
+    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+    {
+        // Fetch module Id.
+        StringTableEntry moduleId = *moduleIdItr;
+
+        // Finish if we could not resolve the dependencies for module Id (of any version Id).
+        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+            return false;
+    }
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot unload module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is loaded but at version Id '%d'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Remove module group.
+    mGroupsLoaded.erase_fast( groupLoadedItr );
+
+    // Reset modules unloaded count.
+    U32 modulesUnloadedCount = 0;
+
+    // Iterate the modules in reverse order calling their destroy function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module loaded.
+        if ( pLoadedEntry == NULL )
+        {
+            // No, so warn.
+            if ( mEchoInfo )
+            {
+                Con::printf( "Module Manager: Unloading group '%s' but could not unload module Id '%s' at version Id '%d'.",
+                    moduleGroup, pLoadedEntry->mpModuleDefinition->getModuleId(), pLoadedEntry->mpModuleDefinition->getVersionId() );
+            }
+            // Skip.
+            continue;
+        }
+
+        // Reduce load count.
+        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
+
+        // Sanity!
+        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
+
+        // Do we need to unload?
+        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
+        {
+            // Yes, so info.
+            if ( mEchoInfo )
+            {
+                Con::printSeparator();
+                Con::printf( "Module Manager: Unload group '%s' with module Id '%s' at version Id '%d' in group '%s'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
+            }
+
+            // Raise notifications.
+            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
+
+            // Fetch the module Id loaded entry.
+            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Sanity!
+            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleGroup() - Cannot find module to unload it." );
+
+            // Dequeue module loaded.
+            mModulesLoaded.erase_fast( moduleLoadedItr );
+
+            // Fetch scope set.
+            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
+
+            // Is the destroy method available?
+            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
+            {
+                // Yes, so call the destroy method.
+                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
+            }
+
+            // Remove scope set.
+            pScopeSet->deleteObjects();
+            pScopeSet->unregisterObject();
+            pLoadReadyModuleDefinition->mScopeSet = 0;
+
+            // Remove path expando for module.
+            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Bump modules unloaded count.
+            modulesUnloadedCount++;
+
+            // Raise notifications.
+            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish unloading '%d' module(s) for group '%s'.", modulesUnloadedCount, moduleGroup );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 versionId )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot load explicit module Id with NULL module Id." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot load explicit module Id '%s' as it does not exist.", moduleId );
+        return false;
+    }
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d':", moduleId, versionId );
+    }
+
+    // Finish if we could not resolve the dependencies for module Id (of any version Id).
+    if ( !resolveModuleDependencies( moduleId, versionId, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+        return false;
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as the module Id is already loaded but at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        // Info.
+        Con::printf( "Module Manager: Explicit load of module Id '%s' at version Id '%d' and its dependencies is comprised of the following '%d' module(s):", moduleId, versionId, moduleReadyQueue.size() );
+
+        // Iterate the modules echoing them.
+        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+        {
+            // Fetch the ready entry.
+            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+            // Info.
+            Con::printf( "> module Id '%s' at version Id '%d'", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+        }
+    }
+
+    // Reset modules loaded count.
+    U32 modulesLoadedCount = 0;
+
+    // Iterate the modules, executing their script files and call their create function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module already loaded.
+        if ( pLoadedEntry != NULL )
+        {
+            // Yes, so increase load count.
+            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
+
+            // Skip.
+            continue;
+        }
+
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printSeparator();
+            Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d' using the script file '%s'.",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+        }
+
+        // Is the module deprecated?
+        if ( pLoadReadyModuleDefinition->getDeprecated() )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' is deprecated,  You should use a newer version!",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+        }
+
+        // Add the path expando for module.
+        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
+
+        // Create a scope set.
+        SimSet* pScopeSet = new SimSet;
+        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
+        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
+
+        // Increase load count.
+        pReadyEntry->mpModuleDefinition->increaseLoadCount();
+
+        // Queue module loaded.
+        mModulesLoaded.push_back( *pReadyEntry );
+
+        // Bump modules loaded count.
+        modulesLoadedCount++;
+
+        // Raise notifications.
+        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
+
+        // Do we have a script file-path specified?
+        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
+        {
+            // Yes, so execute the script file.
+            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
+
+            // Did we execute the script file?
+            if ( scriptFileExecuted )
+            {
+                // Yes, so is the create method available?
+                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
+                {
+                    // Yes, so call the create method.
+                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
+                }
+            }
+            else
+            {
+                // No, so warn.
+                Con::errorf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+            }
+        }
+
+        // Raise notifications.
+        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish loading '%d' explicit module(s).", modulesLoadedCount );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::unloadModuleExplicit( const char* pModuleId )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot unload explicit module Id with NULL module Id." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it does not exist.", moduleId );
+        return false;
+    }
+
+    // Find if the module is actually loaded.
+    ModuleDefinition* pLoadedModule = findLoadedModule( moduleId );
+
+    // Is the module loaded?
+    if ( pLoadedModule == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it is not loaded.", moduleId );
+        return false;
+    }
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Unloading explicit module Id '%s':" , moduleId );
+    }
+
+    // Finish if we could not resolve the dependencies for module Id (of any version Id).
+    if ( !resolveModuleDependencies( moduleId, pLoadedModule->getVersionId(), moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+        return false;
+
+    // Check the modules we want to unload to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' at version Id '%d' as the module Id is loaded but at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Reset modules unloaded count.
+    U32 modulesUnloadedCount = 0;
+
+    // Iterate the modules in reverse order calling their destroy function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module loaded.
+        if ( pLoadedEntry == NULL )
+        {
+            // No, so warn.
+            if ( mEchoInfo )
+            {
+                Con::printf( "Module Manager: Unloading explicit module Id '%s' at version Id '%d' but ignoring as it is not loaded.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+            }
+
+            // Skip.
+            continue;
+        }
+
+        // Reduce load count.
+        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
+
+        // Sanity!
+        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
+
+        // Do we need to unload?
+        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
+        {
+            // Yes, so info.
+            if ( mEchoInfo )
+            {
+                Con::printSeparator();
+                Con::printf( "Module Manager: Unload explicit module Id '%s' at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+            }
+
+            // Raise notifications.
+            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
+
+            // Fetch the module Id loaded entry.
+            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Sanity!
+            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleExplicit() - Cannot find module to unload it." );
+
+            // Dequeue module loaded.
+            mModulesLoaded.erase_fast( moduleLoadedItr );
+
+            // Fetch scope set.
+            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
+
+            // Is the destroy method available?
+            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
+            {
+                // Yes, so call the destroy method.
+                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
+            }
+
+            // Remove scope set.
+            pScopeSet->deleteObjects();
+            pScopeSet->unregisterObject();
+            pLoadReadyModuleDefinition->mScopeSet = 0;
+
+            // Remove path expando for module.
+            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Bump modules unloaded count.
+            modulesUnloadedCount++;
+
+            // Raise notifications.
+            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish unloading '%d' explicit module(s).", modulesUnloadedCount );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleDefinition* ModuleManager::findModule( const char* pModuleId, const U32 versionId )
+{
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
+
+    // Find module definition.
+    ModuleDefinitionEntry::iterator moduleItr = findModuleDefinition( StringTable->insert( pModuleId ), versionId );
+
+     // Finish if module was not found.
+    if ( moduleItr == NULL )
+        return NULL;
+
+    return *moduleItr;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleDefinition* ModuleManager::findLoadedModule( const char* pModuleId )
+{
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Iterate loaded modules.
+    for ( typeModuleLoadEntryVector::iterator loadedModuleItr = mModulesLoaded.begin(); loadedModuleItr != mModulesLoaded.end(); ++loadedModuleItr )
+    {
+        // Skip if not the module.
+        if ( loadedModuleItr->mpModuleDefinition->getModuleId() != moduleId )
+            continue;
+
+        return loadedModuleItr->mpModuleDefinition;
+    }
+
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::findModules( const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
+{
+    // Iterate module Ids.
+    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
+    {
+        // Fetch module definition entry.
+        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
+
+        // Iterate module definitions.
+        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
+
+            // Are we searching for loaded modules only?
+            if ( loadedOnly )
+            {
+                // Yes, so skip if the module is not loaded.
+                if ( pModuleDefinition->getLoadCount() == 0 )
+                    continue;
+
+                // Use module definition.
+                moduleDefinitions.push_back( pModuleDefinition );
+
+                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
+                break;
+            }
+
+            // use module definition.
+            moduleDefinitions.push_back( pModuleDefinition );
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::findModuleTypes( const char* pModuleType, const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
+{
+    // Fetch module type.
+    StringTableEntry moduleType = StringTable->insert( pModuleType );
+
+    // Iterate module Ids.
+    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
+    {
+        // Fetch module definition entry.
+        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
+
+        // Skip if note the module type we're searching for.
+        if ( pModuleDefinitionEntry->mModuleType != moduleType )
+            continue;
+
+        // Iterate module definitions.
+        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
+
+            // Are we searching for loaded modules only?
+            if ( loadedOnly )
+            {
+                // Yes, so skip if the module is not loaded.
+                if ( pModuleDefinition->getLoadCount() == 0 )
+                    continue;
+
+                // Use module definition.
+                moduleDefinitions.push_back( pModuleDefinition );
+
+                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
+                break;
+            }
+
+            // use module definition.
+            moduleDefinitions.push_back( pModuleDefinition );
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+StringTableEntry ModuleManager::copyModule( ModuleDefinition* pSourceModuleDefinition, const char* pTargetModuleId, const char* pTargetPath, const bool useVersionPathing )
+{
+    // Sanity!
+    AssertFatal( pSourceModuleDefinition != NULL, "Cannot copy module using a NULL source module definition." );
+    AssertFatal( pTargetModuleId != NULL, "Cannot copy module using a NULL target module Id." );
+    AssertFatal( pTargetPath != NULL, "Cannot copy module using a NULL target path." );
+
+    // Fetch the source module Id.
+    StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+    // Is the source module definition registered with this module manager?
+    if ( pSourceModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' as it is not registered with this module manager.", sourceModuleId );
+        return StringTable->EmptyString;
+    }
+
+    // Fetch the target module Id.
+    StringTableEntry targetModuleId = StringTable->insert( pTargetModuleId );
+
+    // Extend moduleId/VersionId pathing.
+    char versionPathBuffer[1024];
+
+    // Are we using version pathing?
+    if ( useVersionPathing )
+    {
+        // Yes, so format it.
+        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s/%s/%d",
+            pTargetPath, targetModuleId, pSourceModuleDefinition->getVersionId() );
+    }
+    else
+    {
+        // No, so a straight copy.
+        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s", pTargetPath );
+    }
+
+    // Expand the path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), versionPathBuffer );
+    pTargetPath = targetPathBuffer;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Started copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
+    }
+
+    // Is the target folder a directory?
+    if ( !Platform::isDirectory( pTargetPath ) )
+    {
+        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
+        char createDirectoryBuffer[1024];
+        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetPath, NULL, true );
+
+        // No, so can we create it?
+        if ( !Platform::createPath( createDirectoryBuffer ) )
+        {
+            // No, so warn.
+            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory was not found and could not be created.",
+                sourceModuleId, pTargetPath );
+            return StringTable->EmptyString;
+        }
+    }
+
+    // Copy the source module to the target folder.
+    if ( !Platform::pathCopy( pSourceModuleDefinition->getModulePath(), pTargetPath, false ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory copy failed.",
+            sourceModuleId, pTargetPath );
+        return StringTable->EmptyString;
+    }
+
+    // Format the new source module definition file-path.
+    char newModuleDefinitionSourceFileBuffer[1024];
+    dSprintf( newModuleDefinitionSourceFileBuffer, sizeof(newModuleDefinitionSourceFileBuffer), "%s/%s", pTargetPath, pSourceModuleDefinition->getModuleFile() );
+
+    // Finish if source/target module Ids are identical.
+    if ( sourceModuleId == targetModuleId )
+        return StringTable->insert( newModuleDefinitionSourceFileBuffer );
+
+    // Format the new target module definition file-path.
+    char newModuleDefinitionTargetFileBuffer[1024];
+    dSprintf( newModuleDefinitionTargetFileBuffer, sizeof(newModuleDefinitionTargetFileBuffer), "%s/%s.%s", pTargetPath, targetModuleId, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
+
+    // Rename the module definition.
+    if ( !Platform::fileRename( newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as renaming the module from '%s' to '%s' failed.",
+            sourceModuleId, pTargetPath, newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer );
+        return StringTable->EmptyString;
+    }
+
+    Vector<StringTableEntry> directories;
+
+    // Find directories.
+    if ( !Platform::dumpDirectories( pTargetPath, directories, -1 ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
+            sourceModuleId, pTargetPath );
+        return StringTable->EmptyString;
+    }
+
+    TamlModuleIdUpdateVisitor moduleIdUpdateVisitor;
+    moduleIdUpdateVisitor.setModuleIdFrom( sourceModuleId );
+    moduleIdUpdateVisitor.setModuleIdTo( targetModuleId );
+
+    Vector<Platform::FileInfo> files;
+
+    const char* pExtension = (const char*)"Taml";
+    const U32 extensionLength = dStrlen(pExtension);
+
+    // Iterate directories.
+    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
+    {
+        // Fetch base path.
+        StringTableEntry basePath = *basePathItr;
+
+        // Find files.
+        files.clear();
+        if ( !Platform::dumpPath( basePath, files, 0 ) )
+        {
+            // Warn.
+            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
+                sourceModuleId, pTargetPath );
+            return StringTable->EmptyString;
+        }
+
+        // Iterate files.
+        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
+        {
+            // Fetch file info.
+            Platform::FileInfo* pFileInfo = fileItr;
+
+            // Fetch filename.
+            const char* pFilename = pFileInfo->pFileName;
+
+            // Find filename length.
+            const U32 filenameLength = dStrlen( pFilename );
+
+            // Skip if extension is longer than filename.
+            if ( extensionLength >= filenameLength )
+                continue;
+
+            // Skip if extension not found.
+            if ( dStricmp( pFilename + filenameLength - extensionLength, pExtension ) != 0 )
+                continue;
+
+            char parseFileBuffer[1024];
+            dSprintf( parseFileBuffer, sizeof(parseFileBuffer), "%s/%s", pFileInfo->pFullPath, pFilename );
+
+            // Parse file.
+            if ( !moduleIdUpdateVisitor.parse( parseFileBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Module Manager: Failed to parse file '%s' whilst copying module Id '%s' using target directory '%s'.",
+                    parseFileBuffer, sourceModuleId, pTargetPath );
+                return StringTable->EmptyString;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Finished copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
+    }
+
+    return StringTable->insert( newModuleDefinitionTargetFileBuffer );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::synchronizeDependencies( ModuleDefinition* pRootModuleDefinition, const char* pTargetDependencyPath )
+{
+    // Sanity!
+    AssertFatal( pRootModuleDefinition != NULL, "Cannot synchronize dependencies with NULL root module definition." );
+    AssertFatal( pTargetDependencyPath != NULL, "Cannot synchronize dependencies with NULL target dependency path." );
+
+    // Fetch the root module Id.
+    StringTableEntry rootModuleId = pRootModuleDefinition->getModuleId();
+
+    // Is the root module definition registered with this module manager?
+    if ( pRootModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Cannot synchronize dependencies for module Id '%s' as it is not registered with this module manager.", rootModuleId );
+        return false;
+    }
+
+    // Expand the path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pTargetDependencyPath );
+    pTargetDependencyPath = targetPathBuffer;
+
+    // Is the target dependency folder a directory?
+    if ( !Platform::isDirectory( pTargetDependencyPath ) )
+    {
+        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
+        char createDirectoryBuffer[1024];
+        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetDependencyPath, NULL, true );
+
+        // No, so can we create it?
+        if ( !Platform::createPath( createDirectoryBuffer ) )
+        {
+            // No, so warn.
+            Con::warnf("Cannot synchronize dependencies for module Id '%s' using target directory '%s' as directory was not found and could not be created.",
+                rootModuleId, pTargetDependencyPath );
+            return false;
+        }
+    }
+
+    typeModuleLoadEntryVector       resolvingQueue;
+    typeModuleLoadEntryVector       sourceModulesNeeded;
+
+    // Could we resolve source dependencies?
+    if ( !resolveModuleDependencies( rootModuleId, pRootModuleDefinition->getVersionId(), pRootModuleDefinition->getModuleGroup(), true, resolvingQueue, sourceModulesNeeded ) )
+    {
+        // No, so warn.
+        Con::warnf("Cannot synchronize dependencies for root module Id '%s' as its dependencies could not be resolved.", rootModuleId );
+        return false;
+    }
+
+    // Sanity!
+    AssertFatal( sourceModulesNeeded.size() > 0, "Cannot synchronize dependencies as no modules were returned." );
+
+    // Remove the root module definition.
+    sourceModulesNeeded.pop_back();
+
+    // Initialize the target module manager and scan the target folder for modules.
+    ModuleManager targetModuleManager;
+    targetModuleManager.mEnforceDependencies = true;
+    targetModuleManager.mEchoInfo = false;
+    targetModuleManager.scanModules( pTargetDependencyPath );
+
+    char targetFolderGenerateBuffer[1024];
+
+    // Iterate module definitions.
+    for ( typeModuleLoadEntryVector::iterator sourceModuleItr = sourceModulesNeeded.begin(); sourceModuleItr != sourceModulesNeeded.end(); ++sourceModuleItr )
+    {
+        // Fetch module definition.
+        ModuleDefinition* pSourceModuleDefinition = sourceModuleItr->mpModuleDefinition;
+        
+        // Fetch the source module Id,
+        StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+        // Fetch the source module version Id.
+        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
+
+        // Fetch the source module build Id.
+        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
+
+        // Fetch module definition entry for this module Id in the target.
+        ModuleDefinitionEntry* pDefinitions = targetModuleManager.findModuleId( sourceModuleId );
+
+        // Is the module Id present in the target?
+        if ( pDefinitions == NULL )
+        {
+            // No, so format module Id folder path.
+            dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/", pTargetDependencyPath, sourceModuleId );
+
+            // Create module Id folder.
+            if ( !Platform::createPath( targetFolderGenerateBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Cannot synchronize dependencies for module Id '%s' as the target directory '%s' could not be created.", sourceModuleId, targetFolderGenerateBuffer );
+                return false;
+            }
+        }
+        else
+        {
+            // Yes, so fetch the module definition for this module Id and version Id in the target.
+            ModuleDefinitionEntry::iterator definitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
+
+            // Is the specific module definition present in the target?
+            if ( definitionItr != NULL )
+            {
+                // Yes, so fetch the module definition.
+                ModuleDefinition* pTargetModuleDefinition = *definitionItr;
+
+                // Fetch the target module build Id.
+                const U32 targetBuildId = pTargetModuleDefinition->getBuildId();
+
+                // Fetch the target module path.
+                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
+
+                // Remove the target module definition from the database.
+                targetModuleManager.removeModuleDefinition( pTargetModuleDefinition );
+
+                // Skip if the target definition is the same build Id.
+                if ( targetBuildId == sourceBuildId )
+                    continue;
+
+                // Delete the target module definition folder.
+                if ( !Platform::deleteDirectory( targetModulePath ) )
+                {
+                    // Warn.
+                    Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the old module at '%s' could not be deleted.",
+                        sourceModuleId, sourceVersionId, targetModulePath );
+                    return false;
+                }
+            }
+        }
+
+        // Format source module path.
+        char sourceFolderPath[1024];
+        Con::expandPath( sourceFolderPath, sizeof(sourceFolderPath), pSourceModuleDefinition->getModulePath() );
+
+        // Format target module path.
+        dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/%d", pTargetDependencyPath, sourceModuleId, sourceVersionId );
+
+        // Copy the source module to the target folder.
+        if ( !Platform::pathCopy( sourceFolderPath, targetFolderGenerateBuffer, false ) )
+        {
+            // Warn.
+            Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the module could not be copied to '%s'.",
+                sourceModuleId, sourceVersionId, targetFolderGenerateBuffer );
+            return false;
+        }
+    }
+
+    // Find any target modules left, These are orphaned modules not depended upon by any other module.
+    typeConstModuleDefinitionVector orphanedTargetModules;
+    targetModuleManager.findModules( false, orphanedTargetModules );
+
+    // Iterate module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = orphanedTargetModules.begin(); moduleDefinitionItr != orphanedTargetModules.end(); ++moduleDefinitionItr )
+    {
+        // Fetch orphaned module definition.
+        const ModuleDefinition* pOrphanedModuleDefinition = *moduleDefinitionItr;
+       
+        // Delete the target module definition folder.
+        if ( !Platform::deleteDirectory( pOrphanedModuleDefinition->getModulePath() ) )
+        {
+            // Warn.
+            Con::warnf("Cannot delete orphaned module Id '%s' at version Id '%d' from '%s'.",
+                pOrphanedModuleDefinition->getModuleId(), pOrphanedModuleDefinition->getVersionId(), pOrphanedModuleDefinition->getModulePath() );
+        }
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::canMergeModules( const char* pMergeSourcePath )
+{
+    // Sanity!
+    AssertFatal( pMergeSourcePath != NULL, "Cannot check merge modules with NULL source path." );
+
+    // Expand the source path.
+    char sourcePathBuffer[1024];
+    Con::expandPath( sourcePathBuffer, sizeof(sourcePathBuffer), pMergeSourcePath );
+    pMergeSourcePath = sourcePathBuffer;
+
+    // Is the path a valid directory?
+    if ( !Platform::isDirectory( sourcePathBuffer ) )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot check merge modules as path is invalid '%s'.", sourcePathBuffer );
+        return false;
+    }
+
+    // Initialize the source module manager and scan the source folder for modules.
+    ModuleManager mergeModuleManager;
+    mergeModuleManager.mEnforceDependencies = false;
+    mergeModuleManager.mEchoInfo = false;
+    mergeModuleManager.scanModules( pMergeSourcePath );
+
+    // Find all the merge modules.
+    typeConstModuleDefinitionVector mergeModules;
+    mergeModuleManager.findModules( false, mergeModules );
+
+    // Iterate found merge module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator mergeModuleItr = mergeModules.begin(); mergeModuleItr != mergeModules.end(); ++mergeModuleItr )
+    {
+        // Fetch module definition.
+        const ModuleDefinition* pMergeModuleDefinition = *mergeModuleItr;
+
+        // Fetch module Id.
+        StringTableEntry moduleId = pMergeModuleDefinition->getModuleId();
+
+        // Fetch version Id.
+        const U32 versionId = pMergeModuleDefinition->getVersionId();
+
+        // Fetch module group.
+        StringTableEntry moduleGroup = pMergeModuleDefinition->getModuleGroup();
+
+        // Cannot merge if module already exists.
+        if ( findModuleDefinition( moduleId, versionId ) != NULL )
+            return false;
+
+        // Cannot merge if module is part of a loaded group.
+        if ( findGroupLoaded( moduleGroup ) != NULL )
+            return false;
+    }
+
+    // Can merge modules.
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::mergeModules( const char* pMergeTargetPath, const bool removeMergeDefinition, const bool registerNewModules )
+{
+    // Sanity!
+    AssertFatal( pMergeTargetPath != NULL, "Cannot merge modules with a target path of NULL." );
+
+    // Is a module merge available?
+    if ( !isModuleMergeAvailable() )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot merge modules as a module merge is not available." );
+        return false;
+    }
+
+    // Expand the target path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pMergeTargetPath );
+    pMergeTargetPath = targetPathBuffer;
+
+    // Fetch module merge file path.
+    StringTableEntry moduleMergeFilePath = getModuleMergeFilePath();
+
+    // Read module merge definition.
+    Taml taml;
+    ModuleMergeDefinition* pModuleMergeDefinition = taml.read<ModuleMergeDefinition>( moduleMergeFilePath );
+    
+    // Do we have a module merge definition.
+    if ( pModuleMergeDefinition == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot merge modules as the module merge definition file failed to load '%s'.", moduleMergeFilePath );
+        return false;
+    }
+
+    // Fetch the merge source path.
+    StringTableEntry mergeSourcePath = pModuleMergeDefinition->getModuleMergePath();
+
+    // Remove the module merge definition.
+    pModuleMergeDefinition->deleteObject();
+    pModuleMergeDefinition = NULL;
+
+    // If we cannot merge the modules then we only process modules flagged as critical merge.
+    const bool criticalMergeOnly = !canMergeModules( mergeSourcePath );
+
+    // Initialize the target module manager and scan the target folder for modules.
+    ModuleManager targetModuleManager;
+    targetModuleManager.mEnforceDependencies = false;
+    targetModuleManager.mEchoInfo = false;
+    targetModuleManager.scanModules( pMergeTargetPath );
+
+    // Initialize the source module manager and scan the source folder for modules.
+    ModuleManager sourceModuleManager;
+    sourceModuleManager.mEnforceDependencies = false;
+    sourceModuleManager.mEchoInfo = false;
+    sourceModuleManager.scanModules( mergeSourcePath );
+
+    // Find all the source modules.
+    typeConstModuleDefinitionVector sourceModules;
+    sourceModuleManager.findModules( false, sourceModules );
+
+    // Iterate found merge module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator sourceModuleItr = sourceModules.begin(); sourceModuleItr != sourceModules.end(); ++sourceModuleItr )
+    {
+        // Fetch the source module definition.
+        const ModuleDefinition* pSourceModuleDefinition = *sourceModuleItr;
+
+        // Skip if we're performing a critical merge only and the module is not flagged as critical merge.
+        if ( criticalMergeOnly && pSourceModuleDefinition->getCriticalMerge() )
+            continue;
+
+        // Fetch source module Id.
+        const StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+        // Fetch source version Id.
+        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
+
+        // Fetch source build Id.
+        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
+
+        // Format module Id folder path.
+        char targetModuleIdBuffer[1024];
+        dSprintf( targetModuleIdBuffer, sizeof(targetModuleIdBuffer), "%s/%s/", pMergeTargetPath, sourceModuleId );
+
+        // Flag to indicate if the merged module needs registering.
+        bool shouldRegisterModule;
+
+        // Does the module Id exist?
+        if ( targetModuleManager.findModuleId( sourceModuleId ) == NULL )
+        {
+            // No, so create module Id folder.
+            if ( !Platform::createPath( targetModuleIdBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Cannot merge modules for module '%s' as the path '%s' could not be created.", sourceModuleId, targetModuleIdBuffer );
+                return false;
+            }
+
+            // Module Should be registered.
+            shouldRegisterModule = true;
+        }
+        else
+        {
+            // Yes, so find the target module definition that matches the source module definition.
+            ModuleDefinitionEntry::iterator targetModuleDefinitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
+
+            // Is there an existing target module definition entry?
+            if ( targetModuleDefinitionItr != NULL )
+            {
+                // Yes, so fetch the target module definition.
+                const ModuleDefinition* pTargetModuleDefinition = *targetModuleDefinitionItr;
+
+                // Fetch target module path.
+                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
+
+                // Yes, so we have to remove it first.
+                if ( !Platform::deleteDirectory( targetModulePath ) )
+                {
+                    // Module was not deleted so warn.
+                    Con::warnf( "Failed to remove module folder located at '%s'.  Module will be copied over.", targetModulePath );
+                }
+
+                // Is the build Id being downgraded?
+                if ( sourceBuildId < pTargetModuleDefinition->getBuildId() )
+                {
+                    // Yes, so warn.
+                    Con::warnf( "Encountered a downgraded build Id for module Id '%s' at version Id '%d'.", sourceModuleId, sourceBuildId );
+                }
+
+                // Module should not be registered.
+                shouldRegisterModule = false;
+            }
+            else
+            {
+                // Module Should be registered.
+                shouldRegisterModule = true;
+            }
+        }
+
+        // Fetch source module path.
+        StringTableEntry sourceModulePath = pSourceModuleDefinition->getModulePath();
+
+        // Format target version Id folder path.
+        char targetVersionIdBuffer[1024];
+        dSprintf( targetVersionIdBuffer, sizeof(targetVersionIdBuffer), "%s%d", targetModuleIdBuffer, sourceVersionId );
+
+        // Copy module (allow overwrites as we may have failed to remove the old folder in which case this is likely to fail as well).
+        if ( !Platform::pathCopy( sourceModulePath, targetVersionIdBuffer, false ) )
+        {
+            // Failed to copy module.
+            Con::warnf( "Failed to copy module folder located at '%s' to location '%s'.  The modules may now be corrupted.", sourceModulePath, targetVersionIdBuffer );
+        }
+
+        // Are we registering new modules and the module needs registering?
+        if ( registerNewModules && shouldRegisterModule )
+        {
+            // Yes, so scan module.
+            scanModules( targetVersionIdBuffer, true );
+        }
+
+        // Is the module part of a critical merge?
+        if ( criticalMergeOnly )
+        {
+            // Yes, so we need to remove the source module definition.
+            if ( !Platform::deleteDirectory( sourceModulePath ) )
+            {
+                // Module was not deleted so warn.
+                Con::warnf( "Failed to remove CRITICAL merge module folder located at '%s'.  Module will be copied over.", sourceModulePath );
+            }
+        }
+    }
+
+    // Do we need to remove the module merge definition file?
+    if ( removeMergeDefinition )
+    {
+        // Yes, so remove it.
+        Platform::fileDelete( moduleMergeFilePath );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::addListener( SimObject* pListener )
+{
+    // Sanity!
+    AssertFatal( pListener != NULL, "Cannot add notifications to a NULL object." );
+
+    // Ignore if already added.
+    if ( mNotificationListeners.find( pListener ) != mNotificationListeners.end() )
+        return;
+        
+    // Add as a listener.
+    mNotificationListeners.addObject( pListener );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::removeListener( SimObject* pListener )
+{
+    // Sanity!
+    AssertFatal( pListener != NULL, "Cannot remove notifications from a NULL object." );
+
+    // Remove as a listener.
+    mNotificationListeners.removeObject( pListener );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::clearDatabase( void )
+{
+    // Lock database.
+    AssertFatal( mDatabaseLocks == 0, "Cannot clear database if database is locked." );
+
+    // Iterate groups loaded.
+    while ( mGroupsLoaded.size() > 0 )
+    {
+        // Unload module group.
+        unloadModuleGroup( *mGroupsLoaded.begin() );
+    }
+
+    // Iterate any other explicit modules that are loaded.
+    while ( mModulesLoaded.size() > 0 )
+    {
+        // Fetch module definition.
+        ModuleDefinition* pModuleDefinition = mModulesLoaded.begin()->mpModuleDefinition;
+
+        // Unload explicit module.
+        unloadModuleExplicit( pModuleDefinition->getModuleId() );
+    }
+
+    // Iterate modules to delete module definitions.
+    for ( typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.begin(); moduleItr != mModuleIdDatabase.end(); ++moduleItr )
+    {
+        // Fetch modules definitions.
+        ModuleDefinitionEntry* pDefinitions = moduleItr->value;
+
+        // Iterate module definitions.
+        for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *definitionItr;
+
+            // Remove notification before we delete it.
+            clearNotify( pModuleDefinition );
+
+            // Delete module definition.
+            pModuleDefinition->deleteObject();
+        }
+
+        // Clear definitions.
+        delete pDefinitions;        
+    }
+
+    // Clear database.
+    mModuleIdDatabase.clear();
+
+    // Iterate module groups.
+    for ( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
+    {
+        // Delete module group vector.
+        delete moduleGroupItr->value;
+    }
+
+    // Clear module groups.
+    mGroupModules.clear();
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::removeModuleDefinition( ModuleDefinition* pModuleDefinition )
+{
+    // Sanity!
+    AssertFatal( pModuleDefinition != NULL, "Cannot remove module definition if it is NULL." );
+    
+    // Fetch module Id.
+    StringTableEntry moduleId = pModuleDefinition->getModuleId();
+
+    // Is the module definition registered with this module manager?
+    if ( pModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Cannot remove module definition '%s' as it is not registered with this module manager.", moduleId );
+        return false;
+    }
+
+    // Is the module definition loaded?
+    if ( pModuleDefinition->getLoadCount() > 0 )
+    {
+        // No, so warn.
+        Con::warnf("Cannot remove module definition '%s' as it is loaded.", moduleId );
+        return false;
+    }
+
+    // Find module Id.
+    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
+
+    // Sanity!
+    AssertFatal( moduleItr != mModuleIdDatabase.end(), "Failed to find module definition." );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = moduleItr->value;
+
+    // Fetch version Id.
+    const U32 versionId = pModuleDefinition->getVersionId();
+
+    // Iterate module definitions.
+    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+    {
+        // Skip if this isn't the version Id we're searching for.
+        if ( versionId != (*definitionItr)->getVersionId() )
+            continue;
+
+        // Remove definition entry.
+        pDefinitions->erase( definitionItr ); 
+
+        // Remove notification before we delete it.
+        clearNotify( pModuleDefinition );
+
+        // Delete module definition.
+        pModuleDefinition->deleteObject();
+
+        // Are there any modules left for this module Id?
+        if ( findModuleId( moduleId ) == NULL )
+        {
+            bool moduleIdFound = false;
+
+            // No, so remove from groups.
+            for( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
+            {
+                // Fetch module Ids.
+                typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+                // Iterate module Id.
+                for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+                {
+                    // Skip if this isn't the Id.
+                    if ( *moduleIdItr != moduleId )
+                        continue;
+
+                    // Remove the module Id.
+                    pModuleIds->erase( moduleIdItr );
+
+                    // Flag as found.
+                    moduleIdFound = true;
+
+                    break;
+                }
+
+                // Finish if found.
+                if ( moduleIdFound )
+                    break;
+            }
+        }
+
+        return true;
+    }
+
+    // Sanity!
+    AssertFatal( false, "Failed to find module definition." );
+
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::registerModule( const char* pModulePath, const char* pModuleFile )
+{
+    // Sanity!
+    AssertFatal( pModulePath != NULL, "Cannot scan module with NULL module path." );
+    AssertFatal( pModuleFile != NULL, "Cannot scan module with NULL module file." );
+
+    // Make the module path a full-path.
+    char fullPathBuffer[1024];
+    Platform::makeFullPathName( pModulePath, fullPathBuffer, sizeof(fullPathBuffer) );
+    pModulePath = fullPathBuffer;
+
+
+    char formatBuffer[1024];
+
+    // Fetch module path trail character.
+    char modulePathTrail = pModulePath[dStrlen(pModulePath) - 1];
+
+    // Format module file-path.
+    dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleFile );
+
+    // Read the module file.
+    ModuleDefinition* pModuleDefinition = mTaml.read<ModuleDefinition>( formatBuffer );
+
+    // Did we read a module definition?
+    if ( pModuleDefinition == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Failed to read module definition in file '%s'.", formatBuffer );
+        return false;
+    }
+
+    // Set the module manager.
+    pModuleDefinition->setModuleManager( this );
+
+    // Set module definition path.
+    pModuleDefinition->setModulePath( pModulePath );
+
+    // Set module file.
+    pModuleDefinition->setModuleFile( pModuleFile );
+
+    // Set module file-path.
+    pModuleDefinition->setModuleFilePath( formatBuffer );
+
+    // Fetch module Id.
+    StringTableEntry moduleId = pModuleDefinition->getModuleId();
+
+    // Fetch module version Id.
+    const U32 versionId = pModuleDefinition->getVersionId();
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pModuleDefinition->getModuleGroup();
+
+    // Fetch module type.
+    StringTableEntry moduleType = pModuleDefinition->getModuleType();
+
+    // Is the module enabled?
+    if ( !pModuleDefinition->getEnabled() )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it is disabled.", pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module Id valid?
+    if ( moduleId == StringTable->EmptyString )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it has an unspecified module Id.",
+            pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module version Id valid?
+    if ( versionId == 0 )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found Manager: Registering module: '%s' but it has an invalid Version Id of '0'.",
+            pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module group already loaded?
+    if ( findGroupLoaded( moduleGroup ) != NULL )
+    {
+        // Yes, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it is in a module group '%s' which has already been loaded.",
+            pModuleDefinition->getModuleFilePath(),
+            moduleGroup );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Was a script-file specified?
+    if ( pModuleDefinition->getScriptFile() != StringTable->EmptyString )
+    {
+        // Yes, so format module script file-path.
+        dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleDefinition->getScriptFile() );
+        pModuleDefinition->setModuleScriptFilePath( formatBuffer );
+    }
+
+    // Format module signature,
+    dSprintf( formatBuffer, sizeof(formatBuffer), "%s_%d_%d", moduleId, versionId, pModuleDefinition->getBuildId() );
+    pModuleDefinition->setSignature( formatBuffer );
+
+    // Locked the module definition.
+    pModuleDefinition->setLocked( true );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions != NULL )
+    {
+        // Yes, so find the module definition.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Does this version Id already exist?
+        if ( definitionItr != NULL )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but it is already registered as module Id '%s' at version Id '%d'.",
+                pModuleDefinition->getModuleFilePath(), moduleId, versionId );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+
+        // Is the module group the same as the module definitions we already have?
+        if ( moduleGroup != pDefinitions->mModuleGroup )
+        {
+            // No, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but its module group '%s' is not the same as other module definitions of the same module Id.",
+                pModuleDefinition->getModuleFilePath(), moduleGroup );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+
+        // Is the module type the same as the module definitions we already have?
+        if ( moduleType != pDefinitions->mModuleType )
+        {
+            // No, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but its module type '%s' is not the same as other module definitions of the same module Id.",
+                pModuleDefinition->getModuleFilePath(), moduleGroup );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+    }
+    else
+    {
+        // No, so create a vector of definitions.
+        pDefinitions = new ModuleDefinitionEntry( moduleId, moduleGroup, moduleType );
+
+        // Insert module Id definitions.
+        mModuleIdDatabase.insert( moduleId, pDefinitions );
+    }
+    
+    // Add module definition.
+    pDefinitions->push_back( pModuleDefinition );
+
+    // Sort module definitions by version Id so that higher versions appear first.
+    dQsort( pDefinitions->address(), pDefinitions->size(), sizeof(ModuleDefinition*), moduleDefinitionVersionIdSort );
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr != mGroupModules.end() )
+    {
+        // Yes, so fetch module Ids.
+        typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+        // Is the module Id already present?
+        bool moduleIdFound = false;
+        for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+        {
+            // Skip if this isn't the Id.
+            if ( *moduleIdItr != moduleId )
+                continue;
+
+            // Flag as found.
+            moduleIdFound = true;
+            break;
+        }
+
+        // Add if module Id was not found.
+        if ( !moduleIdFound )
+            pModuleIds->push_back( moduleId );
+    }
+    else
+    {
+        // No, so insert a module Id vector.
+        moduleGroupItr = mGroupModules.insert( pModuleDefinition->getModuleGroup(), new typeModuleIdVector() );
+
+        // Add module Id.
+        moduleGroupItr->value->push_back( moduleId );
+    }
+
+    // Notify if the module definition is destroyed.
+    deleteNotify( pModuleDefinition );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+#if 1
+        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s' ].",
+            pModuleDefinition->getModuleFilePath(),
+            pModuleDefinition->getModuleId(),
+            pModuleDefinition->getVersionId(),
+            pModuleDefinition->getBuildId(),
+            pModuleDefinition->getModuleDescription()
+            );
+#else
+        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s', Group='%s', Dependencies='%s', ScriptFile='%s', CreateFunction='%s', DestroyFunction='%s' ].",
+            pModuleDefinition->getModuleFilePath(),
+            pModuleDefinition->getModuleId(),
+            pModuleDefinition->getVersionId(),
+            pModuleDefinition->getBuildId(),
+            pModuleDefinition->getModuleDescription(),
+            pModuleDefinition->getModuleGroup(),
+            pModuleDefinition->getDataField( StringTable->insert("Dependencies"), NULL ),
+            pModuleDefinition->getScriptFile(),
+            pModuleDefinition->getCreateFunction(),
+            pModuleDefinition->getDestroyFunction()
+            );
+#endif
+    }
+
+    // Emit notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        Con::executef( *notifyItr, 2, "onModuleRegister", pModuleDefinition->getIdString() );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::unregisterModule( const char* pModuleId, const U32 versionId )
+{
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "A module Id cannot be NULL." );
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Find the module definition.
+    ModuleDefinition* pModuleDefinition = findModule( pModuleId, versionId );
+
+    // Did we find the module definition?
+    if ( pModuleDefinition == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unregister module Id '%s' as it is not registered.", moduleId );
+        return false;
+    }
+
+    // Remove the module definition.
+    return removeModuleDefinition( pModuleDefinition );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePreLoadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePreLoad( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePreLoad" ) )
+            Con::executef( pListener, 2, "onModulePreLoad", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePostLoadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePostLoad( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePostLoad" ) )
+            Con::executef( pListener, 2, "onModulePostLoad", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePreUnloadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePreUnload( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePreUnload" ) )
+            Con::executef( pListener, 2, "onModulePreUnload", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePostUnloadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePostUnload( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePostUnload" ) )
+            Con::executef( pListener, 2, "onModulePostUnload", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleDefinitionEntry* ModuleManager::findModuleId( StringTableEntry moduleId )
+{
+    // Sanity!
+    AssertFatal( moduleId != NULL, "A module Id cannot be NULL." );
+
+    // Is the module Id valid?
+    if ( moduleId == StringTable->EmptyString )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Invalid Module Id." );
+        return NULL;
+    }
+
+    // Find module Id.
+    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
+
+    // Return appropriately.
+    return moduleItr != mModuleIdDatabase.end() ? moduleItr->value : NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleDefinitionEntry::iterator ModuleManager::findModuleDefinition( StringTableEntry moduleId, const U32 versionId )
+{
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Finish if no module definitions for the module Id.
+    if ( pDefinitions == NULL )
+        return NULL;
+
+    // Iterate module definitions.
+    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+    {
+        // Skip if this isn't the version Id we're searching for.
+        if ( versionId != (*definitionItr)->getVersionId() )
+            continue;
+
+        // Return module definition iterator.
+        return definitionItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::resolveModuleDependencies( StringTableEntry moduleId, const U32 versionId, StringTableEntry moduleGroup, bool synchronizedOnly, typeModuleLoadEntryVector& moduleResolvingQueue, typeModuleLoadEntryVector& moduleReadyQueue )
+{
+    // Fetch the module Id ready entry.
+    ModuleLoadEntry* pLoadReadyEntry = findModuleReady( moduleId, moduleReadyQueue );
+
+    // Is there a load entry?
+    if ( pLoadReadyEntry )
+    {
+        // Yes, so finish if the version Id is not important,
+        if ( versionId == 0 )
+            return true;
+
+        // Finish if the version Id are compatible.
+        if ( versionId == pLoadReadyEntry->mpModuleDefinition->getVersionId() )
+            return true;
+
+        // Is it a strict version Id?
+        if ( pLoadReadyEntry->mStrictVersionId )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: A module dependency was detected loading module Id '%s' at version Id '%d' in group '%s' but an version Id '%d' is also required.",
+                moduleId, versionId, pLoadReadyEntry->mpModuleDefinition->getVersionId(), moduleGroup );
+            return false;
+        }
+
+        // No, so find the required module version Id.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Did we find the requested module definition.
+        if ( definitionItr == NULL )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Set the new module definition.
+        pLoadReadyEntry->mpModuleDefinition = *definitionItr;
+
+        // Set strict version Id.
+        pLoadReadyEntry->mStrictVersionId = true;
+                
+        return true;
+    }
+
+    // Is the module Id load resolving?
+    if ( findModuleResolving( moduleId, moduleResolvingQueue ) != NULL )
+    {
+        // Yes, so a cycle has been detected so warn.
+        Con::warnf( "Module Manager: A cyclic dependency was detected resolving module Id '%s' at version Id '%d' in group '%s'.",
+            moduleId, versionId, moduleGroup );
+        return false;
+    }
+
+    // Reset selected module definition.
+    ModuleDefinition* pSelectedModuleDefinition = NULL;
+
+    // Do we want the latest version Id?
+    if ( versionId == 0 )
+    {
+        // Yes, so find the module Id.
+        typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.find( moduleId );
+
+        // Did we find the module Id?
+        if ( moduleIdItr == mModuleIdDatabase.end() )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Fetch first module definition which should be the highest version Id.
+        pSelectedModuleDefinition = (*moduleIdItr->value)[0];
+    }
+    else
+    {
+        // No, so find the module Id at the specific version Id.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Did we find the module definition?
+        if ( definitionItr == NULL )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Select the module definition.
+        pSelectedModuleDefinition = *definitionItr;
+    }
+
+    // If we're only resolving synchronized modules and the module is not synchronized then finish.
+    if ( synchronizedOnly && !pSelectedModuleDefinition->getSynchronized() )
+        return true;
+
+    // Create a load entry.
+    ModuleLoadEntry loadEntry( pSelectedModuleDefinition, false );
+
+    // Fetch module dependencies.
+    const ModuleDefinition::typeModuleDependencyVector& moduleDependencies = pSelectedModuleDefinition->getDependencies();
+
+    // Do we have any module dependencies?
+    if ( moduleDependencies.size() > 0 )
+    {
+        // Yes, so queue this module as resolving.
+        moduleResolvingQueue.push_back( loadEntry );
+
+        // Iterate module dependencies.
+        for( ModuleDefinition::typeModuleDependencyVector::const_iterator dependencyItr = moduleDependencies.begin(); dependencyItr != moduleDependencies.end(); ++dependencyItr )
+        {            
+            // Finish if we could not the dependent module Id at the version Id.
+            if ( !resolveModuleDependencies( dependencyItr->mModuleId, dependencyItr->mVersionId, moduleGroup, synchronizedOnly, moduleResolvingQueue, moduleReadyQueue ) )
+                return false;
+        }
+
+        // Remove module as resolving.
+        moduleResolvingQueue.pop_back();
+    }
+
+    // Queue module as ready.
+    moduleReadyQueue.push_back( loadEntry );        
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleLoadEntry* ModuleManager::findModuleResolving( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleResolvingQueue )
+{
+    // Iterate module load resolving queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleResolvingQueue.begin(); loadEntryItr != moduleResolvingQueue.end(); ++loadEntryItr )
+    {
+        // Finish if found.
+        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
+            return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleLoadEntry* ModuleManager::findModuleReady( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleReadyQueue )
+{
+    // Iterate module load ready queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleReadyQueue.begin(); loadEntryItr != moduleReadyQueue.end(); ++loadEntryItr )
+    {
+        // Finish if found.
+        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
+            return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::typeModuleLoadEntryVector::iterator ModuleManager::findModuleLoaded( StringTableEntry moduleId, const U32 versionId )
+{
+    // Iterate module loaded queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = mModulesLoaded.begin(); loadEntryItr != mModulesLoaded.end(); ++loadEntryItr )
+    {
+        // Skip if not the module Id we're looking for.
+        if ( moduleId != loadEntryItr->mpModuleDefinition->getModuleId() )
+            continue;
+
+        // Skip if we are searching for a specific version and it does not match.
+        if ( versionId != 0 && versionId != loadEntryItr->mpModuleDefinition->getVersionId() )
+            continue;
+
+        return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::typeGroupVector::iterator ModuleManager::findGroupLoaded( StringTableEntry moduleGroup )
+{
+    // Iterate groups loaded queue.
+    for( typeGroupVector::iterator groupsLoadedItr = mGroupsLoaded.begin(); groupsLoadedItr != mGroupsLoaded.end(); ++groupsLoadedItr )
+    {
+        // Finish if found.
+        if ( moduleGroup == *groupsLoadedItr )
+            return groupsLoadedItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+StringTableEntry ModuleManager::getModuleMergeFilePath( void ) const
+{
+    // Format merge file path.
+    char filePathBuffer[1024];
+    dSprintf( filePathBuffer, sizeof(filePathBuffer), "%s/%s", Platform::getExecutablePath(), MODULE_MANAGER_MERGE_FILE );
+
+    return StringTable->insert( filePathBuffer );
+}

+ 3 - 0
engine/source/module/moduleManager.h

@@ -152,6 +152,9 @@ public:
     /// Module discovery.
     bool scanModules( const char* pPath, const bool rootOnly = false );
 
+    /// Module unregister.
+    bool unregisterModule( const char* pModuleId, const U32 versionId );
+
     /// Module (un)loading.
     bool loadModuleGroup( const char* pModuleGroup );
     bool unloadModuleGroup( const char* pModuleGroup );

+ 17 - 19
engine/source/module/moduleManager_ScriptBinding.h

@@ -54,6 +54,23 @@ ConsoleMethod(ModuleManager, scanModules, bool, 3, 4,   "(moduleRootPath, [rootO
 
 //-----------------------------------------------------------------------------
 
+ConsoleMethod(ModuleManager, unregisterModule, bool, 4, 4,  "(moduleId, versionId) - Unregister the specified module.\n"
+                                                            "@param moduleId The module Id to unregister.\n"
+                                                            "@param versionId The version Id to unregister.\n"
+                                                            "@return Whether the module was unregister or not.")
+{
+    // Fetch the module Id.
+    const char* pModuleId = argv[2];
+
+    // Fetch the version Id.
+    const U32 versionId = (U32)dAtoi(argv[3]);
+
+    // Unregister the module.
+    return object->unregisterModule( pModuleId, versionId );
+}
+
+//-----------------------------------------------------------------------------
+
 ConsoleMethod(ModuleManager, loadGroup, bool, 3, 3,     "(moduleGroup) - Load the specified module group.\n"
                                                         "@param moduleGroup The module group to load.\n"
                                                         "@return Whether the module group was loaded or not.")
@@ -128,25 +145,6 @@ ConsoleMethod(ModuleManager, findModule, const char*, 4, 4,     "(moduleId, vers
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(ModuleManager, findLoadedModule, const char*, 3, 3,   "(moduleId) - Find the specific loaded module Id.\n"
-                                                                    "@param moduleId The module Id to find.\n"
-                                                                    "@return The module definition object or NULL if the module was not found or loaded.")
-{
-    // Fetch module Id.
-    const char* pModuleId = argv[2];
-
-    // Find loaded module definition.
-    ModuleDefinition* pModuleDefinition = object->findLoadedModule( pModuleId );
-
-    // Return nothing if not found.
-    if ( pModuleDefinition == NULL )
-        return StringTable->EmptyString;
-
-    return pModuleDefinition->getIdString();
-}
-
-//-----------------------------------------------------------------------------
-
 ConsoleMethod(ModuleManager, findModules, const char*, 3, 3,    "(loadedOnly?) - Find all the modules registered with the specified loaded state.\n"
                                                                 "@param loadedOnly Whether to return only modules that are loaded or not.\n"
                                                                 "@return A list of space-separated module definition object Ids.")

+ 2 - 2
engine/source/module/tamlModuleIdUpdateVisitor.h

@@ -110,7 +110,7 @@ public:
     void setModuleIdFrom( const char* pModuleIdFrom )
     {
         // Sanity!
-        AssertFatal( pModuleIdFrom != NULL, "Asset Id from cannot be NULL." );
+        AssertFatal( pModuleIdFrom != NULL, "Module Id from cannot be NULL." );
 
         // Set module Id.
         mModuleIdFrom = StringTable->insert( pModuleIdFrom );
@@ -121,7 +121,7 @@ public:
     void setModuleIdTo( const char* pModuleIdTo )
     {
         // Sanity!
-        AssertFatal( pModuleIdTo != NULL, "Asset Id to cannot be NULL." );
+        AssertFatal( pModuleIdTo != NULL, "Module Id to cannot be NULL." );
 
         // Set module Id.
         mModuleIdTo = StringTable->insert( pModuleIdTo );

+ 59 - 62
engine/source/persistence/taml/taml.cc

@@ -379,7 +379,7 @@ Taml::TamlFormatMode Taml::getFileAutoFormatMode( const char* pFilename )
 
 //-----------------------------------------------------------------------------
 
-TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
+TamlWriteNode* Taml::compileObject( SimObject* pSimObject, const bool forceId )
 {
     // Debug Profiling.
     PROFILE_SCOPE(Taml_CompileObject);
@@ -426,6 +426,13 @@ TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
     TamlWriteNode* pNewNode = new TamlWriteNode();
     pNewNode->set( pSimObject );
 
+    // Is an Id being forced for this object?
+    if ( forceId )
+    {
+        // Yes, so allocate one.
+        pNewNode->mRefId = ++mMasterNodeId;
+    }
+
     // Push new node.
     mCompiledNodes.push_back( pNewNode );
 
@@ -446,8 +453,8 @@ TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
     // Compile children.
     compileChildren( pNewNode );
 
-    // Compile custom properties.
-    compileCustomProperties( pNewNode );
+    // Compile custom state.
+    compileCustomState( pNewNode );
 
     // Are there any Taml callbacks?
     if ( pNewNode->mpTamlCallbacks != NULL )
@@ -661,93 +668,83 @@ void Taml::compileChildren( TamlWriteNode* pTamlWriteNode )
 
 //-----------------------------------------------------------------------------
 
-void Taml::compileCustomProperties( TamlWriteNode* pTamlWriteNode )
+void Taml::compileCustomState( TamlWriteNode* pTamlWriteNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(Taml_CompileCustomProperties);
 
     // Sanity!
-    AssertFatal( pTamlWriteNode != NULL, "Cannot compile custom properties on a NULL node." );
-    AssertFatal( pTamlWriteNode->mpSimObject != NULL, "Cannot compile custom properties on a node with no object." );
+    AssertFatal( pTamlWriteNode != NULL, "Cannot compile custom state on a NULL node." );
+    AssertFatal( pTamlWriteNode->mpSimObject != NULL, "Cannot compile custom state on a node with no object." );
 
-    // Fetch the custom properties on the write node.
-    TamlCustomProperties& customProperties = pTamlWriteNode->mCustomProperties;
+    // Fetch the custom node on the write node.
+    TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
 
     // Are there any Taml callbacks?
     if ( pTamlWriteNode->mpTamlCallbacks != NULL )
     {
         // Yes, so call it.
-        tamlCustomWrite( pTamlWriteNode->mpTamlCallbacks, customProperties );
+        tamlCustomWrite( pTamlWriteNode->mpTamlCallbacks, customNodes );
     }
 
-    // Finish if no custom properties to process.
-    if ( customProperties.size() == 0 )
-        return;
+    // Fetch custom nodes.
+    const TamlCustomNodeVector& nodes = customNodes.getNodes();
 
+    // Finish if no custom nodes to process.
+    if ( nodes.size() == 0 )
+        return;
+    
     // Iterate custom properties.
-    for( TamlCustomPropertyVector::iterator propertyItr = customProperties.begin(); propertyItr != customProperties.end(); ++propertyItr )
+    for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
     {
-        TamlCustomProperty* pCustomProperty = *propertyItr;
-
-        // Iterate alias.
-        for( TamlPropertyAliasVector::iterator typeAliasItr = pCustomProperty->begin(); typeAliasItr != pCustomProperty->end(); ++typeAliasItr )
-        {
-            TamlPropertyAlias* pAlias = *typeAliasItr;
+        // Fetch the custom node.
+        TamlCustomNode* pCustomNode = *customNodesItr;
 
-            // Iterate the fields.
-            for( TamlPropertyFieldVector::iterator fieldItr = pAlias->begin(); fieldItr != pAlias->end(); ++fieldItr )
-            {
-                TamlPropertyField* pPropertyField = *fieldItr;
+        // Compile custom node state.
+        compileCustomNodeState( pCustomNode );
+    }
+}
 
-                // Skip if not an object field.
-                if ( !pPropertyField->isObjectField() )
-                    continue;
+//-----------------------------------------------------------------------------
 
-                // Compile the object.
-                TamlWriteNode* pCompiledWriteNode = compileObject( pPropertyField->getFieldObject() );
+void Taml::compileCustomNodeState( TamlCustomNode* pCustomNode )
+{
+    // Sanity!
+    AssertFatal( pCustomNode != NULL, "Taml: Cannot compile NULL custom node state." );
 
-                // Set reference field.
-                pCompiledWriteNode->mRefField = pPropertyField->getFieldName();
+    // Fetch children.
+    const TamlCustomNodeVector& children = pCustomNode->getChildren();
 
-                // Set the write node for the property field.
-                pPropertyField->setWriteNode( pCompiledWriteNode );
-            }
-        }
-    }
+    // Fetch proxy object.
+    SimObject* pProxyObject = pCustomNode->getProxyObject<SimObject>(false);
 
-#if 0
-    // Iterate the custom properties removing ignored items.
-    for( S32 customPropertyIndex = 0; customPropertyIndex < customProperties.size(); ++customPropertyIndex )
+    // Do we have a proxy object?
+    if ( pProxyObject != NULL )
     {
-        // Fetch the custom property.
-        TamlCustomProperty* pCustomProperty = customProperties.at( customPropertyIndex );
-
-        // Skip if we are not ignoring the custom property if empty.
-        if ( !pCustomProperty->mIgnoreEmpty )
-            continue;
-
-        // Iterate the alias.
-        for ( S32 typeAliasIndex = 0; typeAliasIndex < pCustomProperty->size(); ++typeAliasIndex )
-        {
-            // Fetch the alias.
-            TamlPropertyAlias* pAlias = pCustomProperty->at( typeAliasIndex );
+        // Yes, so sanity!
+        AssertFatal( children.size() == 0, "Taml: Cannot compile a proxy object on a custom node that has children." );
 
-            // Skip If we're not ignoring the alias or the custom property is not empty.
-            if ( !pAlias->mIgnoreEmpty && pAlias->size() != 0 )
-                continue;
+        // Yes, so compile it.
+        // NOTE: We force an Id for custom compiled objects so we guarantee an Id.  The reason for this is fairly
+        // weak in that the XML parser currently has no way of distinguishing between a compiled object node
+        // and a custom node.  If the node has an Id or an Id-Ref then it's obviously an object and should be parsed as such.
+        pCustomNode->setWriteNode( compileObject( pProxyObject, true ) );
+        return;
+    }
 
-            // Remove the alias.
-            pCustomProperty->removeAlias( typeAliasIndex-- );
-        }
+    // Finish if no children.
+    if ( children.size() == 0 )
+        return;
 
-        // Skip if the custom property is not empty.
-        if ( pCustomProperty->size() != 0 )
-            continue;
+    // Iterate children.
+    for( TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr )
+    {
+        // Fetch shape node.
+        TamlCustomNode* pChildNode = *childItr;
 
-        // Remove the custom property.
-        customProperties.removeProperty( customPropertyIndex-- );
+        // Compile the child.
+        compileCustomNodeState( pChildNode );
     }
-#endif
 }
 
 //-----------------------------------------------------------------------------

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.