Jelajahi Sumber

Applied massive patch from weitjong.

Changes:

I. Build environment
1. Added new cmake definition to disable/enable Log subsystem in main's CMakeLists.txt.
2. Added setting in main's CMakeLists.txt to pass the "-D_DEBUG" compiler flags for Debug configuration build on non-MSVC compiler. At least it is needed on XCode to get verbose debug log. I assume MSVC does not have this problem.
3. Added '../Engine. to include directories in Input's CMakeLists.txt to allow Input to reference Engine class for handling of SDL_QUIT event (see V.5 and VII.1).
4. Changed cmake_gcc.sh to sed Doxyfile to exclude Direct3D9 and include OpenGL variants of the classes, assuming gcc is only for Mac OS X and Linux.
5. Changed cmake_ios.sh to sed Doxyfile to exclude Direct3D9 and include OpenGL variants of the classes.
6. Added new cmake_macosx.sh shell script to prepare cmake for Xcode in Mac OS X environment.
7. Updated Docs/Reference.dox to correct the URL to AMD's Compressonator.
8. Updated Docs/ScriptAPI.dox to reflect API changes in UI.
9. Added *.sh scripts in the Bin folder to invoke the respective demos.

II. Audio
1. Minor refactoring on Audio class: removed redundant header include, removed redundant call.
2. Corrected minor documentation typo error in Sound.h.

III. Container
1. HashMap minor optimisation: operator [] implementation would not traverse the container twice before node insertion, refactored Clear() and Sort() implementation, changed to static_cast instead of reinterpret_cast, etc.
2. HashSet minor optimisation: refactored Clear() and Sort() implementation, changed to static_cast instead of reinterpret_cast, etc.
3. List minor optimisation: refactored Clear() implementation and changed to static_cast instead of reinterpret_cast.
4. Corrected documentation error for operator > in Pair class.
5. Added new Contains() methods and renamed Print()/PrintArgs() to AppendWithFormat()/AppendWithFormatArgs (while they are still new :-) in String class. Added ToString() global function in StringUtils class to take advantage of new append methods (useful in constructing a formatted string for logging in one liner; becomes no-ops when logging not enabled).
6. Added new Remove() method in Vector and PODVector classes.

IV. Core
1. Enhanced ProcessUtils class to add native approach to detect number of Physical and Logical CPUs for iOS platform.
   BUG FIX: Previous ParseArguments(int, char**) implementation assumed there were no space in the pathname which causes parsing error when it does. Fixed by enclosed the argument in quotes before appending into command line. With this fix, Ninja War demo is runable in iPhoneSimulator.
2. In StringUtils added new convenient global function to construct a formatted string, see III.6.
3. In Variant added MAX_VAR_TYPES enum for guarding a while loop.

V. Engine
1. Added pragma to suppress LLVM/Clang erroneous warnings on unused functions in APITemplates.h.
2. Minor refactoring on Console class: removed redundant call, renamed current_Row to currentRow_.
3. In CoreAPI refactored code to replace map's Find() method call with the newly added Contains() method, added call to reserve container capacity.
4. Refactored DebugHUD class to use the String's new append method instead of a series of strings concatenation.
5. Enhanced Engine class to make Logging an optional feature that can be turned on/off like Profiler subsystem (see I.1), only unpause the audio when it was pause by the Engine previously, no need to check for uninitialized graphics due to window was closed (see VII.1) as engine's exiting_ flag should be set when SDL quits (even by quitting the process externally).
6. GraphicsAPI minor optimisation: Add call to reserve vector to the required known size.
7. Enhanced IOAPI to add preprocessor directive to no-ops the logging functions when Logging not enabled, see I.1.
8. Enhanced UIAPI to reflect changes in UI: mainly, exposed nonFocusedMouseWheel property (see XV.12).

VI. Graphics
1. AnimatedModel optimisation: refactored ProcessRayQuery() implementation, added call to reserve container capacity, removed copy-pasta comment.
   BUG FIX: Infinite while loop in SetMorphsAttr() as the index was never incremented.
2. Changed Animation constructor to also initialize length_.
3. Refactored AnimationController class: only mark for network update when necessary, corrected minor documentation error, added call to reserve container capacity, prevent out-of-bound index access in SetAnimationAttr() implementation, use the StringHash instead of plain name to find the animation state.
4. Refactored AnimationState to avoid redundant call.
5. Refactored BillboardSet class: removed redundant class forward, changed to use the revised Drawable's constructor (see VI.9), added call to reserve container capacity.
6. Refactored Camera class to remove redundant construct. 
7. Refactored DebugRenderer to reuse code. 
8. Refactored DecalSet class: changed to use the revised Drawable's constructor (see VI.9), added call to reserve container capacity, added new flag to track the event subscription.
9. Refactored Drawable to change its constructor to take additional drawableFlags parameter.
10. Enhanced Light class: changed to use the revised Drawable's constructor (see VI.9), refactored ProcessRayQuery() implementation, added implementation to debug draw the directional light.
    BUG FIX: On mobile devices, the CSM only has two splits. Use preprocessor directive to define the attribute as VAR_VECTOR2 instead of VAR_VECTOR4, accordingly.
    BUG FIX: Previous implementation used local ray against world bounding box for RAY_OBB. Fixed by first transformed the world bounding box to local coordinate.
11. Refactored Material class to set or unset the specular_ flag without traversing the shader parameters each time.
12. Refactored Model class: added call to reserve container capacity, removed redundant call to set the number of vertex buffer to 1 (which is Geometry's default).
13. Modified OcclusionBuffer Clear() method to use post decrement.
14. Refactored Octant and Octree classes: changed to use the new PODVector Remove() method, added index_ instance variable to facilitate child octant deletion, simplified the conditional check in the Release() but still achieving the same result.
15. Refactored OctreeQuery class to reorder the condition and to use post increment.
16. Refactored ParticleEmitter class: removed redundant class forward, changed to use the other ColorFade constructor that inits both color and time, added call to reserve container capacity.
17. Refactored Renderer class: removed unused static constants, amended the documentation text for HandleGraphicsFeatures(), delayed event subscription until object is initialized, moved logic to validate the shadow cascades from getter to setter, changed to use String's Contains() method instead of Find(), and a few more code changes (that make no difference :-).
18. Refactored ShaderParser class to change the method signature of GetCombination() method, removed redundant include.
19. Refactored Skeleton class to add call to reserver container capacity.
20. Refactored StaticModel class: changed to use the revised Drawable's constructor (see VI.9), changed the ProcessRayQuery() implementation.
21. Enhanced Tangent class to use pointer arithmetic instead of array indexing.
22. Refactored Terrain class: reordered instance variable initialization in constructor to keep compiler happy, added call to reserve the container capacity.
23. Refactored TerrainPatch class: changed to use the revised Drawable's constructor (see VI.9), changed ProcessRayQuery() implementation.
24. Refactored View class: removed unused method declarations (did not have implementation), changed to reuse existing Vector3 constants, changed octree query base class to FrustumOctreeQuery instead (no additional penalty as the methods are already virtualized anyway), moved constant to outside the loop in CheckVisibilityWork() implementation, reordered logical statement to take advantage of short circuit evaluation, and more.
25. Refactored Viewport class to use the more common way to insert into container.
26. Refactored Zone class: changed to use the revised Drawable's constructor (see VI.9), reordered logical statement to take advantage of short circuit evaluation.
27. Enhanced OGLGraphics class: added code to prevent unnecessary call to get OGL extensions, changed to use new String's Contains() method, added flag to speed up the Release() method, removed redundant code.
28. Changed OGLGraphicsImpl to remove unused include.
29. Changed OGLIndexBuffer to remove unnecessary override. Probably it was copy pasta from Direct3D9 version.
30. Refactored OGLShader class to use the modified ShaderParser's GetCombination() method, see VI.18.
31. Refactored OGLShaderProgram class to use reuse the index for the second string Find() call.
32. Refactored OGLShaderVariation class to the string Insert() method instead to insert all the defines.
33. Refactored OGLTexture class to use the enum instead of hardcoded value.
34. Refactored OGLTexture2D class to simplify the boolean assignment.
35. Refactored OGLTextureCube class to simplify the boolean assignment.
36. Refactored OGLVertexBuffer class to remove unnecessary override and to simplify the for loop condition check in GetElementOffset() method.
37. Refactored CustomGeometry class: changed to use the revised Drawable's constructor (see VI.9), changed ProcessRayQuery() implementation.

VII. Input
1. Enhanced Input class: added call to Engine::Exit() to properly setting the exiting flag instead of just closing the graphics (see V.5), refactored constructor to initialize the mouse related instance variables, delayed event subscription until object is initialized, and other minor refactoring.

VIII. IO
1. Enhanced FileSystem class: added Mac OS X implementation for SystemOpen() method, changed to use new String's Contains() method instead of Find().
   BUG FIX: On non-Win32 platform, files (including dirs) having name starts with '.' were being returned in the result although SCAN_HIDDEN flag is not set. The fix now excludes them.
2. Enhanced Log class: added preprocessor directive to no-ops the logging macros when Logging not enabled (see I.1), removed "XCODE_DEBUG_CONFIGURATION" as it is not effective (at least on my Xcode). Also replaced "XCODE_DEBUG_CONFIGURATION" with "_DEBUG" in SDL_uikitappdelegate.m.
3. Minor changed in Serializer.cpp to suppress compiler warning.

IX. Math
1. Refactored BoundingBox class to reuse code.
2. Enhanced MathDefs.h: better PI, added new constant M_DEGTORAD_2 which is M_DEGTORAD / 2.f (same as M_PI_2, M_PI_4 convention used in math.h).
3. Refactored Frustum class to use the new M_DEGTORAD_2 constant.
4. Refactored Plane.h to reuse code.
   BUG FIX: Copy constructor did not initialize the absNormal_ properly.
5. Enhanced Quaternion class: added multiply-assign operator with a scalar, added Conjugate() method, refactored to reuse code as much as possible, returned conjugate as inverse for unit quaternion, refactored code to use the new M_DEGTORAD_2 constant.
6. Refactored Rect class to reuse code.
7. Refactored Sphere to use post increment.

X. Network
1. Refactored Connection class: changed to hide the detail of the message processing from the caller, added preprocessor directive to exclude statistics when logging is disabled, changed the code to reflect the change done in Node::CreateComponent() method signature (see XIII.2).
2. Refactored Controls class: removed redundant class forward, changed the way default constructor initialize the instance variables.
3. Refactored Network class: removed redundant class forward, changed to reflect changes done in X.1 to process messages, changed the GetConnection() implementation to avoid traversing of the clientConnections_ unnecessarily.

XI. Physics
1. Enhanced CollisionShape class to add preprocessor directive to no-ops the logging calls when Logging not enabled, see I.1. Refactored the HeightfieldData() method to eliminate if statement in the loop.
2. Minor changed in Constraint.cpp to suppress compiler warning.
3. Refactored PhysicsWorld class: removed redundant call in destructor, changed to use the new PODVector Remove() method.
4. Refactored RigidBody class to use the new PODVector Remove() method.

XII. Resource
1. Minor changed in Image.cpp to suppress compiler warning.
2. Enhanced ResourceCache class: changed code to subscribe to event only when auto reload is enabled, refactored to use HashMap's Find() instead of traversing through container manually, changed to use new String's Contains() method.
3. Minor changed in XMLElement class to define an EMPTY element constant.

XIII. Scene
1. Refactored Component class: removed redundant include, reordered logical evaluation.
2. Refactored Node class: changed the CreateComponent() to accept optional ID parameter (combined previously two methods into one), refactored to reuse code as much as possible, added new method to reset scene called by Scene class, moved protected section to correct place adhering to code convention, added new private convenient method to remove component. 
3. Refactored Scene class: changed destructor and in NodeAdded() method to use the new Node::ResetScene() method (see XIII.2), changed the GetVarNamesAttr() implementation to avoid if in the loop, instead of using XMLElement and String default constructor, changed to reference new EMPTY XMLElement constant (see XII.3) and String's Clear() method, respectively.
4. Enhanced SceneResolver class to add preprocessor directive to no-ops the logging calls when Logging not enabled, see I.1.
   BUG FIX: The missing 'else' statement in Resolve() method might cause component ID not resolved correctly. Fixed by adding the else statement.
5. Enhanced Serializable class: refactored code to suppress compiler warning, added new feature to read/write new optional "enum" XML element.

XIV. Script
1. Enhanced Addons class: removed redundant include, changed code to suppress compiler warning, exposed new String's Contains() methods to script enginei (see III.6).
   BUG FIX: The Find() and FindLast() were erroneously exposed as returning integer value. Changed it to returning unsigned instead.
2. Enhanced Script class: added preprocessor directive to no-ops the logging calls when Logging not enabled (see I.1), changed to use new String's Contains() method instead of Find(), removed redundant call in destructor.
   BUG FIX: The logMode_ instance variable was not initialized properly in the constructor (although documentation says it should be defaulted to immediate mode). Initialized it according to documentation.
3. Refactored ScriptFile class to remove redundant call.
   BUG FIX: Previous implementation in Load() assumed script log mode. The fix saves the old mode and use it to revert back the mode.
4. Enhanced ScriptInstance class: refactored ClearDelayedExecute() method to avoid unnecessary vector traversal, simplified code that tracks the execution of METHOD_DELAYEDSTART, refactored to code reuse.

XV. UI
1. Refactored UIQuad and UIBatch: Added new constructor that initialize instance variables, moved GetInterpolatedColor() method between the two, modified the UIBatch methods to take in const UIElement reference, changed the implementation to code reuse as much as possible.
2. Refactored BorderImage class to use the new UIBatch constructor, see XV.1.
3. Very minor changed in Button.cpp to simplify the logical statement.
4. Refactored DropDownList class: Changed to call Menu::GetBatches() instead of Button::GetBatches(), changed for code reuse.
5. Refactored FileSelector class: added call to reserve container capacity, changed for code reuse.
6. Refacotred Font class: added call to reserve container capacity, removed redundant call. 
   BUG FIX: Corrected a typo in GetKerning() implementation causing the kerning information not being applied correctly between adjacent characters.
7. Refactored LineEdit class: Changed to call BorderImage::ApplyAttributes() instead of UIElement::Attributes(), removed redundant construct, changed to reuse code as much as possible, initialized variable in the setter method instead of during update.
8. Enhanced ListView class: added optional index parameter for RemoveItem() method, subscribed to the defocused event only when it is needed, changed to reuse code as much as possible (page up/down and home/end now support additive multi-select as the positive side effect), removed redundant construct.
9. Very minor change in Menu.cpp to use Variant::EMPTY instead of constructing an empty Variant instance.
10. Refactored ScrollView class to reuse code.
11. Refactored Text class to use the new UIBatch constructor, see XV.1.
12. Enhanced UI class: Added support to enable non-focused mouse wheel behaviour similar to Mac OS X and Linux, delayed post-update and render-update event subscription until object is initialized, removed redundant construct.
13. Enhanced UIElement class: added optional index parameter for RemoveChild() method, refactor the code to use the right constness, renamed method from GetUintColor() to GetDerivedColor(), added new private Detach() method, added mutable keyword as necessary to support const getter, refactored destructor method, added call to reserve container capacity, and more.
14. Minor change in Window.cpp to suppress compiler warning.

XVI. Third Party
1. FreeType: Included zutil.c as source in the CMakeLists.txt and fixed the problem when compiling with _DEBUG set.
2. kNet: Removed std::cout statement in the UDPMessageConnection.cpp.
3. SDL: Changed XCODE_DEBUG_CONFIGURATION to _DEBUG in SDL_uikitappdelegate.m, see VIII.2.
   BUG FIX: The touch focus was always zero causing the touch event from touchpad in Mac OS X platform was not handled correctly by Input class because the GetInputInstance() method returns 0 when windowID is 0. Assign the touch's focus with current focus window ID. NOT SURE THIS IS THE CORRECT FIX THOUGH.

XVII. Demos
1. BUG FIX: TestScene.as and TestSceneOld.as assert in the btAlignedObjectArray.h. It was caused by the script trying to remove the RigidBody and/or CollisionShape while in the middle of physics collision event handling. The Bullet's assert could be observed when build using _DEBUG. Fixed by postponinig the removal to post step.
2. Enhanced the NinjaSnowWar to support no-background-music (nobgm) option.
   BUG FIX: BGM was purposely not played on multiplayer mode assuming testing is done on a same test machine, however, this has caused the BGM not to be played on the genuinue multiplayer mode using different machines. Re-enable BGM on multiplayer mode. Instead, added comments in the demo shell/batch script on how to avoid multiple BGM played on a same test machine.
Lasse Öörni 13 tahun lalu
induk
melakukan
7d8558a51f
100 mengubah file dengan 1067 tambahan dan 1040 penghapusan
  1. 2 0
      Android/jni/Android.mk
  2. 1 0
      Bin/ChatServer.sh
  3. 4 5
      Bin/Data/Scripts/NinjaSnowWar.as
  4. 4 4
      Bin/Data/Scripts/NinjaSnowWar/AIController.as
  5. 21 9
      Bin/Data/Scripts/TestScene.as
  6. 20 9
      Bin/Data/Scripts/TestSceneOld.as
  7. 8 1
      Bin/Data/Scripts/Utilities/Network.as
  8. 1 0
      Bin/Editor.sh
  9. 1 0
      Bin/LightTest.sh
  10. 12 1
      Bin/NinjaSnowWar.bat
  11. 12 0
      Bin/NinjaSnowWar.sh
  12. 1 0
      Bin/Physics.sh
  13. 1 0
      Bin/Terrain.sh
  14. 1 0
      Bin/TestScene.sh
  15. 1 0
      Bin/TestSceneOld.sh
  16. 17 10
      CMakeLists.txt
  17. 1 1
      Docs/Reference.dox
  18. 8 6
      Docs/ScriptAPI.dox
  19. 1 0
      Docs/Urho3D.dox
  20. 2 7
      Engine/Audio/Audio.cpp
  21. 0 2
      Engine/Audio/Audio.h
  22. 1 1
      Engine/Audio/Sound.h
  23. 12 18
      Engine/Container/Allocator.cpp
  24. 48 32
      Engine/Container/HashMap.h
  25. 34 21
      Engine/Container/HashSet.h
  26. 13 4
      Engine/Container/List.h
  27. 1 1
      Engine/Container/Pair.h
  28. 13 5
      Engine/Container/Str.cpp
  29. 7 3
      Engine/Container/Str.h
  30. 31 5
      Engine/Container/Vector.h
  31. 36 9
      Engine/Core/ProcessUtils.cpp
  32. 10 0
      Engine/Core/StringUtils.cpp
  33. 2 0
      Engine/Core/StringUtils.h
  34. 6 11
      Engine/Core/Variant.cpp
  35. 2 1
      Engine/Core/Variant.h
  36. 11 1
      Engine/Engine/APITemplates.h
  37. 6 11
      Engine/Engine/Console.cpp
  38. 1 1
      Engine/Engine/Console.h
  39. 4 6
      Engine/Engine/CoreAPI.cpp
  40. 45 72
      Engine/Engine/DebugHud.cpp
  41. 41 36
      Engine/Engine/Engine.cpp
  42. 2 0
      Engine/Engine/Engine.h
  43. 2 0
      Engine/Engine/GraphicsAPI.cpp
  44. 17 0
      Engine/Engine/IOAPI.cpp
  45. 4 1
      Engine/Engine/UIAPI.cpp
  46. 38 54
      Engine/Graphics/AnimatedModel.cpp
  47. 1 1
      Engine/Graphics/AnimatedModel.h
  48. 4 3
      Engine/Graphics/Animation.cpp
  49. 28 33
      Engine/Graphics/AnimationController.cpp
  50. 1 1
      Engine/Graphics/AnimationController.h
  51. 5 3
      Engine/Graphics/AnimationState.cpp
  52. 6 11
      Engine/Graphics/BillboardSet.cpp
  53. 0 1
      Engine/Graphics/BillboardSet.h
  54. 3 6
      Engine/Graphics/Camera.cpp
  55. 23 38
      Engine/Graphics/CustomGeometry.cpp
  56. 0 3
      Engine/Graphics/CustomGeometry.h
  57. 2 8
      Engine/Graphics/DebugRenderer.cpp
  58. 36 28
      Engine/Graphics/DecalSet.cpp
  59. 2 0
      Engine/Graphics/DecalSet.h
  60. 1 1
      Engine/Graphics/Direct3D9/D3D9Graphics.cpp
  61. 4 4
      Engine/Graphics/Drawable.cpp
  62. 3 3
      Engine/Graphics/Drawable.h
  63. 64 60
      Engine/Graphics/Light.cpp
  64. 14 20
      Engine/Graphics/Material.cpp
  65. 0 2
      Engine/Graphics/Material.h
  66. 9 3
      Engine/Graphics/Model.cpp
  67. 1 4
      Engine/Graphics/OcclusionBuffer.cpp
  68. 17 28
      Engine/Graphics/Octree.cpp
  69. 5 4
      Engine/Graphics/Octree.h
  70. 8 16
      Engine/Graphics/OctreeQuery.cpp
  71. 41 25
      Engine/Graphics/OpenGL/OGLGraphics.cpp
  72. 3 1
      Engine/Graphics/OpenGL/OGLGraphics.h
  73. 0 4
      Engine/Graphics/OpenGL/OGLGraphicsImpl.cpp
  74. 3 5
      Engine/Graphics/OpenGL/OGLIndexBuffer.cpp
  75. 0 2
      Engine/Graphics/OpenGL/OGLIndexBuffer.h
  76. 2 4
      Engine/Graphics/OpenGL/OGLShader.cpp
  77. 1 1
      Engine/Graphics/OpenGL/OGLShaderProgram.cpp
  78. 11 12
      Engine/Graphics/OpenGL/OGLShaderVariation.cpp
  79. 6 3
      Engine/Graphics/OpenGL/OGLTexture.cpp
  80. 1 3
      Engine/Graphics/OpenGL/OGLTexture2D.cpp
  81. 2 4
      Engine/Graphics/OpenGL/OGLTextureCube.cpp
  82. 4 9
      Engine/Graphics/OpenGL/OGLVertexBuffer.cpp
  83. 0 2
      Engine/Graphics/OpenGL/OGLVertexBuffer.h
  84. 5 10
      Engine/Graphics/ParticleEmitter.cpp
  85. 0 1
      Engine/Graphics/ParticleEmitter.h
  86. 49 62
      Engine/Graphics/Renderer.cpp
  87. 2 4
      Engine/Graphics/Renderer.h
  88. 5 6
      Engine/Graphics/ShaderParser.cpp
  89. 1 1
      Engine/Graphics/ShaderParser.h
  90. 2 1
      Engine/Graphics/Skeleton.cpp
  91. 26 44
      Engine/Graphics/StaticModel.cpp
  92. 15 7
      Engine/Graphics/Tangent.cpp
  93. 6 6
      Engine/Graphics/Terrain.cpp
  94. 16 33
      Engine/Graphics/TerrainPatch.cpp
  95. 93 132
      Engine/Graphics/View.cpp
  96. 0 8
      Engine/Graphics/View.h
  97. 1 1
      Engine/Graphics/Viewport.cpp
  98. 7 9
      Engine/Graphics/Zone.cpp
  99. 16 9
      Engine/IO/FileSystem.cpp
  100. 6 2
      Engine/IO/Log.cpp

+ 2 - 0
Android/jni/Android.mk

@@ -261,6 +261,8 @@ LOCAL_SRC_FILES := \
     $(wildcard $(LOCAL_PATH)/Urho3D/*.cpp) \
     $(wildcard $(LOCAL_PATH)/Urho3D/*.cpp) \
     $(wildcard $(LOCAL_PATH)/ThirdParty/SDL/src/main/android/*.cpp))
     $(wildcard $(LOCAL_PATH)/ThirdParty/SDL/src/main/android/*.cpp))
 
 
+LOCAL_CPPFLAGS += -DENABLE_LOGGING
+
 LOCAL_STATIC_LIBRARIES := AngelScript Bullet FreeType kNet PugiXml SDL StanHull STB JO
 LOCAL_STATIC_LIBRARIES := AngelScript Bullet FreeType kNet PugiXml SDL StanHull STB JO
 
 
 LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog
 LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog

+ 1 - 0
Bin/ChatServer.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/Chat.as server -headless $*

+ 4 - 5
Bin/Data/Scripts/NinjaSnowWar.as

@@ -105,14 +105,13 @@ void InitAudio()
     if (engine.headless)
     if (engine.headless)
         return;
         return;
 
 
-    // Lower mastervolumes slightly. On the server, turn sound off completely
-    audio.masterGain[SOUND_MASTER] = runServer ? 0.0 : 0.75;
+    // Lower mastervolumes slightly.
+    audio.masterGain[SOUND_MASTER] = 0.75;
     audio.masterGain[SOUND_MUSIC] = 0.75;
     audio.masterGain[SOUND_MUSIC] = 0.75;
 
 
-    // Start music playback (play in singleplayer only to avoid cacophony if testing with multiple connections)
-    // Note: the non-positional sound source component does not need to be in the scene
-    if (singlePlayer)
+    if (!nobgm)
     {
     {
+        // Note: the non-positional sound source component does not need to be in the scene
         Sound@ musicFile = cache.GetResource("Sound", "Music/Ninja Gods.ogg");
         Sound@ musicFile = cache.GetResource("Sound", "Music/Ninja Gods.ogg");
         musicFile.looped = true;
         musicFile.looped = true;
         musicSource = SoundSource();
         musicSource = SoundSource();

+ 4 - 4
Bin/Data/Scripts/NinjaSnowWar/AIController.as

@@ -49,9 +49,9 @@ class AIController
         {
         {
             Node@ otherNode = nodes[i];
             Node@ otherNode = nodes[i];
             Ninja@ otherNinja = cast<Ninja>(otherNode.scriptObject);
             Ninja@ otherNinja = cast<Ninja>(otherNode.scriptObject);
-            if (otherNinja.side == SIDE_PLAYER)
+            if (otherNinja.side == SIDE_PLAYER && otherNinja.health > 0)
             {
             {
-                float distance = (node.position - otherNode.position).length;
+                float distance = (node.position - otherNode.position).lengthSquared;
                 if (distance < closestDistance)
                 if (distance < closestDistance)
                 {
                 {
                     @targetNode = otherNode;
                     @targetNode = otherNode;
@@ -61,7 +61,7 @@ class AIController
             }
             }
         }
         }
 
 
-        if ((targetNode !is null) && (targetNinja.health > 0))
+        if (targetNode !is null)
         {
         {
             RigidBody@ targetBody = targetNode.GetComponent("RigidBody");
             RigidBody@ targetBody = targetNode.GetComponent("RigidBody");
 
 
@@ -170,4 +170,4 @@ class AIController
                 ninja.controls.yaw += 0.2;
                 ninja.controls.yaw += 0.2;
         }
         }
     }
     }
-}
+}

+ 21 - 9
Bin/Data/Scripts/TestScene.as

@@ -12,6 +12,8 @@ int drawDebug = 0;
 
 
 Text@ downloadsText;
 Text@ downloadsText;
 
 
+Array<Node@> hitObjects;
+
 void Start()
 void Start()
 {
 {
     if (!engine.headless)
     if (!engine.headless)
@@ -34,6 +36,7 @@ void Start()
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("SpawnBox", "HandleSpawnBox");
     SubscribeToEvent("SpawnBox", "HandleSpawnBox");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
+    SubscribeToEvent("PhysicsPostStep", "HandlePhysicsPostStep");
 
 
     network.RegisterRemoteEvent("SpawnBox");
     network.RegisterRemoteEvent("SpawnBox");
 
 
@@ -513,18 +516,27 @@ void HandlePhysicsCollision(StringHash eventType, VariantMap& eventData)
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     if (nodeA.HasComponent("AnimatedModel"))
     if (nodeA.HasComponent("AnimatedModel"))
-    {
-        // Remove the trigger physics shape, and create the ragdoll
-        nodeA.RemoveComponent("RigidBody");
-        nodeA.RemoveComponent("CollisionShape");
-        CreateRagdoll(nodeA.GetComponent("AnimatedModel"));
-    }
+        hitObjects.Push(nodeA);
     else if (nodeB.HasComponent("AnimatedModel"))
     else if (nodeB.HasComponent("AnimatedModel"))
+        hitObjects.Push(nodeB);
+}
+
+void HandlePhysicsPostStep()
+{
+    if (hitObjects.empty)
+        return;
+
+    for (uint i = 0; i < hitObjects.length; ++i)
     {
     {
-        nodeB.RemoveComponent("RigidBody");
-        nodeB.RemoveComponent("CollisionShape");
-        CreateRagdoll(nodeB.GetComponent("AnimatedModel"));
+        Node@ node = hitObjects[i];
+
+        // Remove the trigger physics shape, and create the ragdoll
+        node.RemoveComponent("RigidBody");
+        node.RemoveComponent("CollisionShape");
+        CreateRagdoll(node.GetComponent("AnimatedModel"));
     }
     }
+
+    hitObjects.Clear();
 }
 }
 
 
 void CreateRagdoll(AnimatedModel@ model)
 void CreateRagdoll(AnimatedModel@ model)

+ 20 - 9
Bin/Data/Scripts/TestSceneOld.as

@@ -24,6 +24,7 @@ int drawDebug = 0;
 Array<Node@> animatingObjects;
 Array<Node@> animatingObjects;
 Array<Node@> billboards;
 Array<Node@> billboards;
 Array<Node@> lights;
 Array<Node@> lights;
+Array<Node@> hitObjects;
 
 
 void Start()
 void Start()
 {
 {
@@ -46,6 +47,7 @@ void Start()
     SubscribeToEvent("MouseButtonUp", "HandleMouseButtonUp");
     SubscribeToEvent("MouseButtonUp", "HandleMouseButtonUp");
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
+    SubscribeToEvent("PhysicsPostStep", "HandlePhysicsPostStep");
 }
 }
 
 
 void InitScene()
 void InitScene()
@@ -612,18 +614,27 @@ void HandlePhysicsCollision(StringHash eventType, VariantMap& eventData)
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     if (nodeA.HasComponent("AnimatedModel"))
     if (nodeA.HasComponent("AnimatedModel"))
-    {
-        // Remove the trigger physics shape, and create the ragdoll
-        nodeA.RemoveComponent("RigidBody");
-        nodeA.RemoveComponent("CollisionShape");
-        CreateRagdoll(nodeA.GetComponent("AnimatedModel"));
-    }
+        hitObjects.Push(nodeA);
     else if (nodeB.HasComponent("AnimatedModel"))
     else if (nodeB.HasComponent("AnimatedModel"))
+        hitObjects.Push(nodeB);
+}
+
+void HandlePhysicsPostStep()
+{
+    if (hitObjects.empty)
+        return;
+
+    for (uint i = 0; i < hitObjects.length; ++i)
     {
     {
-        nodeB.RemoveComponent("RigidBody");
-        nodeB.RemoveComponent("CollisionShape");
-        CreateRagdoll(nodeB.GetComponent("AnimatedModel"));
+        Node@ node = hitObjects[i];
+
+        // Remove the trigger physics shape, and create the ragdoll
+        node.RemoveComponent("RigidBody");
+        node.RemoveComponent("CollisionShape");
+        CreateRagdoll(node.GetComponent("AnimatedModel"));
     }
     }
+
+    hitObjects.Clear();
 }
 }
 
 
 void CreateRagdoll(AnimatedModel@ model)
 void CreateRagdoll(AnimatedModel@ model)

+ 8 - 1
Bin/Data/Scripts/Utilities/Network.as

@@ -3,6 +3,7 @@ bool runClient = false;
 String serverAddress;
 String serverAddress;
 uint16 serverPort = 1234;
 uint16 serverPort = 1234;
 String userName;
 String userName;
+bool nobgm = false;
 
 
 void ParseNetworkArguments()
 void ParseNetworkArguments()
 {
 {
@@ -30,5 +31,11 @@ void ParseNetworkArguments()
             
             
             ++index;
             ++index;
         }
         }
+        else
+        {
+            String argument = arguments[i].Substring(1).ToLower();
+            if (argument == "nobgm")
+                nobgm = true;
+        }
     }
     }
-}
+}

+ 1 - 0
Bin/Editor.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/Editor.as $*

+ 1 - 0
Bin/LightTest.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/LightTest.as $*

+ 12 - 1
Bin/NinjaSnowWar.bat

@@ -1 +1,12 @@
-Urho3D.exe Scripts/NinjaSnowWar.as %1 %2 %3 %4 %5 %6 %7 %8
+:: To avoid cacophony of multiple background music when testing with multiple connections on the same test machine,
+:: start all the other non-headless connections with '-nobgm' argument.
+:: E.g. 1 - headless server
+::   Start the server with "NinjaSnowWar -headless server"
+::   Start the first client with "NinjaSnowWar -w <put-your-host-name-here>"
+::   Start the subsequent clients on the same host with "NinjaSnowWar -w -nobgm <put-your-host-name-here>"
+::
+:: E.g. 2 - non-headless server
+::   Start the server with "NinjaSnowWar -w server"
+::   Start the client on the same host with "NinjaSnowWar -w -nobgm <put-your-host-name-here>"
+::
+Urho3D.exe Scripts/NinjaSnowWar.as %1 %2 %3 %4 %5 %6 %7 %8

+ 12 - 0
Bin/NinjaSnowWar.sh

@@ -0,0 +1,12 @@
+# To avoid cacophony of multiple background music when testing with multiple connections on the same test machine,
+# start all the other non-headless connections with '-nobgm' argument.
+# E.g. 1 - headless server
+#   Start the server with "./NinjaSnowWar.sh -headless server"
+#   Start the first client with "./NinjaSnowWar.sh -w `hostname`"
+#   Start the subsequent clients on the same host with "./NinjaSnowWar.sh -w -nobgm `hostname`"
+#
+# E.g. 2 - non-headless server
+#   Start the server with "./NinjaSnowWar.sh -w server"
+#   Start the client on the same host with "./NinjaSnowWar.sh -w -nobgm `hostname`"
+#
+./Urho3D Scripts/NinjaSnowWar.as $*

+ 1 - 0
Bin/Physics.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/Physics.as $*

+ 1 - 0
Bin/Terrain.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/Terrain.as $*

+ 1 - 0
Bin/TestScene.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/TestScene.as $*

+ 1 - 0
Bin/TestSceneOld.sh

@@ -0,0 +1 @@
+./Urho3D Scripts/TestSceneOld.as $*

+ 17 - 10
CMakeLists.txt

@@ -37,6 +37,9 @@ add_definitions (-DENABLE_FILEWATCHER)
 # instantiated.
 # instantiated.
 add_definitions (-DENABLE_PROFILING)
 add_definitions (-DENABLE_PROFILING)
 
 
+# Enable logging. If disabled, LOGXXXX macros become no-ops and the Log subsystem is not instantiated.
+add_definitions (-DENABLE_LOGGING)
+
 # If not on MSVC, enable use of OpenGL instead of Direct3D9 (either not compiling on Windows or
 # If not on MSVC, enable use of OpenGL instead of Direct3D9 (either not compiling on Windows or
 # with a compiler that may not have an up-to-date DirectX SDK). This can also be unconditionally
 # with a compiler that may not have an up-to-date DirectX SDK). This can also be unconditionally
 # set, but Windows graphics card drivers are usually better optimized for Direct3D.
 # set, but Windows graphics card drivers are usually better optimized for Direct3D.
@@ -65,17 +68,21 @@ if (MSVC)
     endif ()
     endif ()
     set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /DEBUG")
     set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /DEBUG")
     set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
     set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
-elseif (NOT IOS)
-    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -m32")
-    if (ENABLE_SSE)
-        set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse")
-        set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse")
-    endif ()
-    if (WIN32)
-        set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
-        set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
+else ()
+    if (NOT IOS)
+        set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
+        set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -m32")
+        if (ENABLE_SSE)
+            set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse")
+            set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse")
+        endif ()
+        if (WIN32)
+            set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
+            set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
+        endif ()
     endif ()
     endif ()
+    set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
+    set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
 endif ()
 endif ()
 
 
 # Macro for precompiled headers
 # Macro for precompiled headers

+ 1 - 1
Docs/Reference.dox

@@ -634,7 +634,7 @@ Default culling mode is counterclockwise. The shadowcull element specifies the c
 
 
 Diffuse maps specify the surface color in the RGB channels. Optionally they can use the alpha channel for blending and alpha testing. They should preferably be compressed to DXT1 (no alpha or 1-bit alpha) or DXT5 (smooth alpha) format.
 Diffuse maps specify the surface color in the RGB channels. Optionally they can use the alpha channel for blending and alpha testing. They should preferably be compressed to DXT1 (no alpha or 1-bit alpha) or DXT5 (smooth alpha) format.
 
 
-Normal maps encode the tangent-space surface normal for normal mapping. They need to be stored as xGxR, ie. Y-component in the green channel, and X-component in the alpha (Z will be reconstructed in the pixel shader.) This encoding lends itself well to DXT5 compression. To convert normal maps to this format, you can use AMD's The Compressonator utility, see http://developer.amd.com/tools/compressonator/pages/default.aspx Before compression, make sure the normal map is oriented correctly: an even surface should have the color value R 0.5 G 0.5 B 1.0.
+Normal maps encode the tangent-space surface normal for normal mapping. They need to be stored as xGxR, ie. Y-component in the green channel, and X-component in the alpha (Z will be reconstructed in the pixel shader.) This encoding lends itself well to DXT5 compression. To convert normal maps to this format, you can use AMD's The Compressonator utility, see http://developer.amd.com/Resources/archive/ArchivedTools/gpu/compressonator/Pages/default.aspx Before compression, make sure the normal map is oriented correctly: an even surface should have the color value R 0.5 G 0.5 B 1.0.
 
 
 Specular maps encode the specular surface color as RGB. Note that deferred rendering is only able to use monochromatic specular intensity from the G channel, while forward and light pre-pass rendering use fully colored specular. DXT1 format should suit these textures well.
 Specular maps encode the specular surface color as RGB. Note that deferred rendering is only able to use monochromatic specular intensity from the G channel, while forward and light pre-pass rendering use fully colored specular. DXT1 format should suit these textures well.
 
 

+ 8 - 6
Docs/ScriptAPI.dox

@@ -260,10 +260,10 @@ Methods:<br>
 - String Replaced(uint8, uint8) const
 - String Replaced(uint8, uint8) const
 - String Replaced(const String&, const String&) const
 - String Replaced(const String&, const String&) const
 - void Resize(uint)
 - void Resize(uint)
-- int Find(const String&, uint arg1 = 0) const
-- int Find(uint8, uint arg1 = 0) const
-- int FindLast(const String&, uint arg1 = 0xffffffff) const
-- int FindLast(uint8, uint arg1 = 0xffffffff) const
+- uint Find(const String&, uint arg1 = 0) const
+- uint Find(uint8, uint arg1 = 0) const
+- uint FindLast(const String&, uint arg1 = 0xffffffff) const
+- uint FindLast(uint8, uint arg1 = 0xffffffff) const
 - bool StartsWith(const String&) const
 - bool StartsWith(const String&) const
 - bool EndsWith(const String&) const
 - bool EndsWith(const String&) const
 - String Substring(uint) const
 - String Substring(uint) const
@@ -280,6 +280,8 @@ Methods:<br>
 - String SubstringUTF8(uint) const
 - String SubstringUTF8(uint) const
 - String SubstringUTF8(uint, uint) const
 - String SubstringUTF8(uint, uint) const
 - int Compare(const String&, bool arg1 = true) const
 - int Compare(const String&, bool arg1 = true) const
+- bool Contains(const String&) const
+- bool Contains(uint8) const
 - String[]@ Split(uint8) const
 - String[]@ Split(uint8) const
 - bool ToBool() const
 - bool ToBool() const
 - float ToFloat() const
 - float ToFloat() const
@@ -2705,7 +2707,6 @@ Properties:<br>
 - bool sm3Support (readonly)
 - bool sm3Support (readonly)
 - bool lightPrepassSupport (readonly)
 - bool lightPrepassSupport (readonly)
 - bool deferredSupport (readonly)
 - bool deferredSupport (readonly)
-- bool hardwareDepthSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool forceSM2
 - bool forceSM2
 - IntVector2[]@ resolutions (readonly)
 - IntVector2[]@ resolutions (readonly)
@@ -3878,7 +3879,7 @@ Methods:<br>
 - void SetScrollBarsVisible(bool, bool)
 - void SetScrollBarsVisible(bool, bool)
 - void AddItem(UIElement@)
 - void AddItem(UIElement@)
 - void InsertItem(uint, UIElement@)
 - void InsertItem(uint, UIElement@)
-- void RemoveItem(UIElement@)
+- void RemoveItem(UIElement@, uint arg1 = 0)
 - void RemoveItem(uint)
 - void RemoveItem(uint)
 - void RemoveAllItems()
 - void RemoveAllItems()
 - void AddSelection(uint)
 - void AddSelection(uint)
@@ -4595,6 +4596,7 @@ Properties:<br>
 - UIElement@ focusElement
 - UIElement@ focusElement
 - UIElement@ frontElement (readonly)
 - UIElement@ frontElement (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
+- bool nonFocusedMouseWheel
 
 
 
 
 Controls
 Controls

+ 1 - 0
Docs/Urho3D.dox

@@ -84,6 +84,7 @@ Contributions and bugfixes from:
 - Alex Fuller
 - Alex Fuller
 - Jason Kinzer
 - Jason Kinzer
 - Miika Santala
 - Miika Santala
+- Wei Tjong Yao
 - Magic.Lixin
 - Magic.Lixin
 - primitivewaste
 - primitivewaste
 - skaiware
 - skaiware

+ 2 - 7
Engine/Audio/Audio.cpp

@@ -25,8 +25,6 @@
 #include "Audio.h"
 #include "Audio.h"
 #include "Context.h"
 #include "Context.h"
 #include "CoreEvents.h"
 #include "CoreEvents.h"
-#include "Graphics.h"
-#include "GraphicsEvents.h"
 #include "Log.h"
 #include "Log.h"
 #include "Mutex.h"
 #include "Mutex.h"
 #include "ProcessUtils.h"
 #include "ProcessUtils.h"
@@ -83,9 +81,7 @@ bool Audio::SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpo
     
     
     desired.freq = mixRate;
     desired.freq = mixRate;
     desired.format = AUDIO_S16SYS;
     desired.format = AUDIO_S16SYS;
-    desired.channels = 1;
-    if (stereo)
-        desired.channels = 2;
+    desired.channels = stereo ? 2 : 1;
     
     
     // For SDL, do not actually use the buffer length, but calculate a suitable power-of-two size from the mixrate
     // For SDL, do not actually use the buffer length, but calculate a suitable power-of-two size from the mixrate
     if (desired.freq <= 11025)
     if (desired.freq <= 11025)
@@ -241,15 +237,14 @@ void Audio::MixOutput(void *dest, unsigned samples)
             clipSamples <<= 1;
             clipSamples <<= 1;
         
         
         // Clear clip buffer
         // Clear clip buffer
-        memset(clipBuffer_.Get(), 0, clipSamples * sizeof(int));
         int* clipPtr = clipBuffer_.Get();
         int* clipPtr = clipBuffer_.Get();
+        memset(clipPtr, 0, clipSamples * sizeof(int));
         
         
         // Mix samples to clip buffer
         // Mix samples to clip buffer
         for (PODVector<SoundSource*>::Iterator i = soundSources_.Begin(); i != soundSources_.End(); ++i)
         for (PODVector<SoundSource*>::Iterator i = soundSources_.Begin(); i != soundSources_.End(); ++i)
             (*i)->Mix(clipPtr, workSamples, mixRate_, stereo_, interpolation_);
             (*i)->Mix(clipPtr, workSamples, mixRate_, stereo_, interpolation_);
         
         
         // Copy output from clip buffer to destination
         // Copy output from clip buffer to destination
-        clipPtr = clipBuffer_.Get();
         short* destPtr = (short*)dest;
         short* destPtr = (short*)dest;
         while (clipSamples--)
         while (clipSamples--)
             *destPtr++ = Clamp(*clipPtr++, -32768, 32767);
             *destPtr++ = Clamp(*clipPtr++, -32768, 32767);

+ 0 - 2
Engine/Audio/Audio.h

@@ -27,8 +27,6 @@
 #include "AudioDefs.h"
 #include "AudioDefs.h"
 #include "Mutex.h"
 #include "Mutex.h"
 #include "Object.h"
 #include "Object.h"
-#include "Quaternion.h"
-#include "Thread.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {

+ 1 - 1
Engine/Audio/Sound.h

@@ -49,7 +49,7 @@ public:
     bool LoadRaw(Deserializer& source);
     bool LoadRaw(Deserializer& source);
     /// Load WAV format sound data.
     /// Load WAV format sound data.
     bool LoadWav(Deserializer& source);
     bool LoadWav(Deserializer& source);
-    /// Load Ogg Vorbis format sound data. Does not decode at load, but will be rather be decoded while playing.
+    /// Load Ogg Vorbis format sound data. Does not decode at load, but will rather be decoded while playing.
     bool LoadOggVorbis(Deserializer& source);
     bool LoadOggVorbis(Deserializer& source);
     /// Set sound size in bytes. Also resets the sound to be uncompressed and one-shot.
     /// Set sound size in bytes. Also resets the sound to be uncompressed and one-shot.
     void SetSize(unsigned dataSize);
     void SetSize(unsigned dataSize);

+ 12 - 18
Engine/Container/Allocator.cpp

@@ -54,17 +54,17 @@ AllocatorBlock* AllocatorReserveBlock(AllocatorBlock* allocator, unsigned nodeSi
     unsigned char* nodePtr = blockPtr + sizeof(AllocatorBlock);
     unsigned char* nodePtr = blockPtr + sizeof(AllocatorBlock);
     AllocatorNode* firstNewNode = reinterpret_cast<AllocatorNode*>(nodePtr);
     AllocatorNode* firstNewNode = reinterpret_cast<AllocatorNode*>(nodePtr);
     
     
-    for (unsigned i = 0; i < capacity; ++i)
+    for (unsigned i = 0; i < capacity - 1; ++i)
     {
     {
         AllocatorNode* newNode = reinterpret_cast<AllocatorNode*>(nodePtr);
         AllocatorNode* newNode = reinterpret_cast<AllocatorNode*>(nodePtr);
-        
-        if (i < capacity - 1)
-            newNode->next_ = reinterpret_cast<AllocatorNode*>(nodePtr + sizeof(AllocatorNode) + nodeSize);
-        else
-            newNode->next_ = allocator->free_;
-        
+        newNode->next_ = reinterpret_cast<AllocatorNode*>(nodePtr + sizeof(AllocatorNode) + nodeSize);
         nodePtr += sizeof(AllocatorNode) + nodeSize;
         nodePtr += sizeof(AllocatorNode) + nodeSize;
     }
     }
+    // i == capacity - 1
+    {
+        AllocatorNode* newNode = reinterpret_cast<AllocatorNode*>(nodePtr);
+        newNode->next_ = 0;
+    }
     
     
     allocator->free_ = firstNewNode;
     allocator->free_ = firstNewNode;
     
     
@@ -92,20 +92,14 @@ void* AllocatorReserve(AllocatorBlock* allocator)
     if (!allocator)
     if (!allocator)
         return 0;
         return 0;
     
     
-    if (allocator->free_)
+    if (!allocator->free_)
     {
     {
-        AllocatorNode* freeNode = allocator->free_;
-        void* ptr = (reinterpret_cast<unsigned char*>(freeNode)) + sizeof(AllocatorNode);
-        allocator->free_ = freeNode->next_;
-        freeNode->next_ = 0;
-        return ptr;
+        // Free nodes have been exhausted. Allocate a new larger block
+        unsigned newCapacity = (allocator->capacity_ + 1) >> 1;
+        AllocatorReserveBlock(allocator, allocator->nodeSize_, newCapacity);
+        allocator->capacity_ += newCapacity;
     }
     }
     
     
-    // Free nodes have been exhausted. Allocate a new larger block
-    unsigned newCapacity = (allocator->capacity_ + 1) >> 1;
-    AllocatorReserveBlock(allocator, allocator->nodeSize_, newCapacity);
-    allocator->capacity_ += newCapacity;
-    
     // We should have new free node(s) chained
     // We should have new free node(s) chained
     AllocatorNode* freeNode = allocator->free_;
     AllocatorNode* freeNode = allocator->free_;
     void* ptr = (reinterpret_cast<unsigned char*>(freeNode)) + sizeof(AllocatorNode);
     void* ptr = (reinterpret_cast<unsigned char*>(freeNode)) + sizeof(AllocatorNode);

+ 48 - 32
Engine/Container/HashMap.h

@@ -242,15 +242,15 @@ public:
     U& operator [] (const T& key)
     U& operator [] (const T& key)
     {
     {
         if (!ptrs_)
         if (!ptrs_)
-            return InsertNode(key, U())->pair_.second_;
+            return InsertNode(key, U(), false)->pair_.second_;
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         
         
         Node* node = FindNode(key, hashKey);
         Node* node = FindNode(key, hashKey);
         if (node)
         if (node)
             return node->pair_.second_;
             return node->pair_.second_;
         else
         else
-            return InsertNode(key, U())->pair_.second_;
+            return InsertNode(key, U(), false)->pair_.second_;
     }
     }
     
     
     /// Insert a pair. Return an iterator to it.
     /// Insert a pair. Return an iterator to it.
@@ -288,7 +288,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return false;
             return false;
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         
         
         Node* previous;
         Node* previous;
         Node* node = FindNode(key, hashKey, previous);
         Node* node = FindNode(key, hashKey, previous);
@@ -310,13 +310,13 @@ public:
         if (!ptrs_ || !it.ptr_)
         if (!ptrs_ || !it.ptr_)
             return End();
             return End();
         
         
-        Node* node = reinterpret_cast<Node*>(it.ptr_);
+        Node* node = static_cast<Node*>(it.ptr_);
         Node* next = node->Next();
         Node* next = node->Next();
         
         
-        unsigned hashKey = MakeHash(node->pair_.first_) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(node->pair_.first_);
         
         
         Node* previous = 0;
         Node* previous = 0;
-        Node* current = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* current = static_cast<Node*>(Ptrs()[hashKey]);
         while (current && current != node)
         while (current && current != node)
         {
         {
             previous = current;
             previous = current;
@@ -337,8 +337,17 @@ public:
     /// Clear the map.
     /// Clear the map.
     void Clear()
     void Clear()
     {
     {
-        while (Size())
-            EraseNode(Head());
+        if (Size())
+        {
+            for (Iterator i = Begin(); i != End(); )
+            {
+                FreeNode(static_cast<Node*>(i++.ptr_));
+                i.ptr_->prev_ = 0;
+            }
+            
+            head_ = tail_;
+            SetSize(0);
+        }
         
         
         ResetPtrs();
         ResetPtrs();
     }
     }
@@ -361,13 +370,14 @@ public:
         
         
         Urho3D::Sort(RandomAccessIterator<Node*>(ptrs), RandomAccessIterator<Node*>(ptrs + numKeys), CompareNodes);
         Urho3D::Sort(RandomAccessIterator<Node*>(ptrs), RandomAccessIterator<Node*>(ptrs + numKeys), CompareNodes);
         
         
-        for (unsigned i = 0; i < numKeys; ++i)
+        head_ = ptrs[0];
+        ptrs[0]->prev_ = 0;
+        for (unsigned i = 1; i < numKeys; ++i)
         {
         {
-            ptrs[i]->next_ = (i < numKeys - 1) ? ptrs[i + 1] : tail_;
-            ptrs[i]->prev_ = (i > 0) ? ptrs[i - 1] : 0;
+            ptrs[i - 1]->next_ = ptrs[i];
+            ptrs[i]->prev_ = ptrs[i - 1];
         }
         }
-        
-        head_ = ptrs[0];
+        ptrs[numKeys - 1]->next_ = tail_;
         tail_->prev_ = ptrs[numKeys - 1];
         tail_->prev_ = ptrs[numKeys - 1];
         
         
         delete[] ptrs;
         delete[] ptrs;
@@ -399,7 +409,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return End();
             return End();
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         Node* node = FindNode(key, hashKey);
         Node* node = FindNode(key, hashKey);
         if (node)
         if (node)
             return Iterator(node);
             return Iterator(node);
@@ -413,7 +423,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return End();
             return End();
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         Node* node = FindNode(key, hashKey);
         Node* node = FindNode(key, hashKey);
         if (node)
         if (node)
             return ConstIterator(node);
             return ConstIterator(node);
@@ -427,7 +437,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return false;
             return false;
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         return FindNode(key, hashKey) != 0;
         return FindNode(key, hashKey) != 0;
     }
     }
     
     
@@ -446,14 +456,14 @@ public:
     
     
 private:
 private:
     /// Return the head node.
     /// Return the head node.
-    Node* Head() const { return reinterpret_cast<Node*>(head_); }
+    Node* Head() const { return static_cast<Node*>(head_); }
     /// Return the tail node.
     /// Return the tail node.
-    Node* Tail() const { return reinterpret_cast<Node*>(tail_); }
+    Node* Tail() const { return static_cast<Node*>(tail_); }
     
     
     /// Find a node from the buckets. Do not call if the buckets have not been allocated.
     /// Find a node from the buckets. Do not call if the buckets have not been allocated.
     Node* FindNode(const T& key, unsigned hashKey) const
     Node* FindNode(const T& key, unsigned hashKey) const
     {
     {
-        Node* node = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* node = static_cast<Node*>(Ptrs()[hashKey]);
         while (node)
         while (node)
         {
         {
             if (node->pair_.first_ == key)
             if (node->pair_.first_ == key)
@@ -469,7 +479,7 @@ private:
     {
     {
         previous = 0;
         previous = 0;
         
         
-        Node* node = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* node = static_cast<Node*>(Ptrs()[hashKey]);
         while (node)
         while (node)
         {
         {
             if (node->pair_.first_ == key)
             if (node->pair_.first_ == key)
@@ -482,7 +492,7 @@ private:
     }
     }
     
     
     /// Insert a key and value and return either the new or existing node.
     /// Insert a key and value and return either the new or existing node.
-    Node* InsertNode(const T& key, const U& value)
+    Node* InsertNode(const T& key, const U& value, bool findExisting = true)
     {
     {
         // If no pointers yet, allocate with minimum bucket count
         // If no pointers yet, allocate with minimum bucket count
         if (!ptrs_)
         if (!ptrs_)
@@ -491,14 +501,17 @@ private:
             Rehash();
             Rehash();
         }
         }
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         
         
-        // If exists, just change the value
-        Node* existing = FindNode(key, hashKey);
-        if (existing)
+        if (findExisting)
         {
         {
-            existing->pair_.second_ = value;
-            return existing;
+            // If exists, just change the value
+            Node* existing = FindNode(key, hashKey);
+            if (existing)
+            {
+                existing->pair_.second_ = value;
+                return existing;
+            }
         }
         }
         
         
         Node* newNode = InsertNode(Tail(), key, value);
         Node* newNode = InsertNode(Tail(), key, value);
@@ -564,6 +577,7 @@ private:
     /// Reserve a node.
     /// Reserve a node.
     Node* ReserveNode()
     Node* ReserveNode()
     {
     {
+        assert(allocator_);
         Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
         Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
         new(newNode) Node();
         new(newNode) Node();
         return newNode;
         return newNode;
@@ -572,8 +586,7 @@ private:
     /// Reserve a node with specified key and value.
     /// Reserve a node with specified key and value.
     Node* ReserveNode(const T& key, const U& value)
     Node* ReserveNode(const T& key, const U& value)
     {
     {
-        if (!allocator_)
-            allocator_ = AllocatorInitialize(sizeof(Node));
+        assert(allocator_);
         Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
         Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
         new(newNode) Node(key, value);
         new(newNode) Node(key, value);
         return newNode;
         return newNode;
@@ -591,8 +604,8 @@ private:
     {
     {
         for (Iterator i = Begin(); i != End(); ++i)
         for (Iterator i = Begin(); i != End(); ++i)
         {
         {
-            Node* node = reinterpret_cast<Node*>(i.ptr_);
-            unsigned hashKey = MakeHash(i->first_) & (NumBuckets() - 1);
+            Node* node = static_cast<Node*>(i.ptr_);
+            unsigned hashKey = Hash(i->first_);
             node->down_ = Ptrs()[hashKey];
             node->down_ = Ptrs()[hashKey];
             Ptrs()[hashKey] = node;
             Ptrs()[hashKey] = node;
         }
         }
@@ -600,6 +613,9 @@ private:
     
     
     /// Compare two nodes.
     /// Compare two nodes.
     static bool CompareNodes(Node*& lhs, Node*& rhs) { return lhs->pair_.first_ < rhs->pair_.first_; }
     static bool CompareNodes(Node*& lhs, Node*& rhs) { return lhs->pair_.first_ < rhs->pair_.first_; }
+    
+    /// Compute a hash based on the key and the bucket size
+    unsigned Hash(const T& key) const { return MakeHash(key) & (NumBuckets() - 1); }
 };
 };
 
 
 }
 }

+ 34 - 21
Engine/Container/HashSet.h

@@ -217,7 +217,7 @@ public:
             Rehash();
             Rehash();
         }
         }
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         
         
         Node* existing = FindNode(key, hashKey);
         Node* existing = FindNode(key, hashKey);
         if (existing)
         if (existing)
@@ -258,7 +258,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return false;
             return false;
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         
         
         Node* previous;
         Node* previous;
         Node* node = FindNode(key, hashKey, previous);
         Node* node = FindNode(key, hashKey, previous);
@@ -280,13 +280,13 @@ public:
         if (!ptrs_ || !it.ptr_)
         if (!ptrs_ || !it.ptr_)
             return End();
             return End();
         
         
-        Node* node = reinterpret_cast<Node*>(it.ptr_);
+        Node* node = static_cast<Node*>(it.ptr_);
         Node* next = node->Next();
         Node* next = node->Next();
         
         
-        unsigned hashKey = MakeHash(node->key_) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(node->key_);
         
         
         Node* previous = 0;
         Node* previous = 0;
-        Node* current = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* current = static_cast<Node*>(Ptrs()[hashKey]);
         while (current && current != node)
         while (current && current != node)
         {
         {
             previous = current;
             previous = current;
@@ -307,8 +307,17 @@ public:
     /// Clear the set.
     /// Clear the set.
     void Clear()
     void Clear()
     {
     {
-        while (Size())
-            EraseNode(Head());
+        if (Size())
+        {
+            for (Iterator i = Begin(); i != End(); )
+            {
+                FreeNode(static_cast<Node*>(i++.ptr_));
+                i.ptr_->prev_ = 0;
+            }
+            
+            head_ = tail_;
+            SetSize(0);
+        }
         
         
         ResetPtrs();
         ResetPtrs();
     }
     }
@@ -331,13 +340,14 @@ public:
         
         
         Urho3D::Sort(RandomAccessIterator<Node*>(ptrs), RandomAccessIterator<Node*>(ptrs + numKeys), CompareNodes);
         Urho3D::Sort(RandomAccessIterator<Node*>(ptrs), RandomAccessIterator<Node*>(ptrs + numKeys), CompareNodes);
         
         
-        for (unsigned i = 0; i < numKeys; ++i)
+        head_ = ptrs[0];
+        ptrs[0]->prev_ = 0;
+        for (unsigned i = 1; i < numKeys; ++i)
         {
         {
-            ptrs[i]->next_ = (i < numKeys - 1) ? ptrs[i + 1] : tail_;
-            ptrs[i]->prev_ = (i > 0) ? ptrs[i - 1] : 0;
+            ptrs[i - 1]->next_ = ptrs[i];
+            ptrs[i]->prev_ = ptrs[i - 1];
         }
         }
-        
-        head_ = ptrs[0];
+        ptrs[numKeys - 1]->next_ = tail_;
         tail_->prev_ = ptrs[numKeys - 1];
         tail_->prev_ = ptrs[numKeys - 1];
         
         
         delete[] ptrs;
         delete[] ptrs;
@@ -369,7 +379,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return End();
             return End();
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         Node* node = FindNode(key, hashKey);
         Node* node = FindNode(key, hashKey);
         if (node)
         if (node)
             return Iterator(node);
             return Iterator(node);
@@ -383,7 +393,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return End();
             return End();
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         Node* node = FindNode(key, hashKey);
         Node* node = FindNode(key, hashKey);
         if (node)
         if (node)
             return ConstIterator(node);
             return ConstIterator(node);
@@ -397,7 +407,7 @@ public:
         if (!ptrs_)
         if (!ptrs_)
             return false;
             return false;
         
         
-        unsigned hashKey = MakeHash(key) & (NumBuckets() - 1);
+        unsigned hashKey = Hash(key);
         return FindNode(key, hashKey) != 0;
         return FindNode(key, hashKey) != 0;
     }
     }
     
     
@@ -416,14 +426,14 @@ public:
     
     
 private:
 private:
     /// Return the head node.
     /// Return the head node.
-    Node* Head() const { return reinterpret_cast<Node*>(head_); }
+    Node* Head() const { return static_cast<Node*>(head_); }
     /// Return the tail node.
     /// Return the tail node.
-    Node* Tail() const { return reinterpret_cast<Node*>(tail_); }
+    Node* Tail() const { return static_cast<Node*>(tail_); }
     
     
     /// Find a node from the buckets. Do not call if the buckets have not been allocated.
     /// Find a node from the buckets. Do not call if the buckets have not been allocated.
     Node* FindNode(const T& key, unsigned hashKey) const
     Node* FindNode(const T& key, unsigned hashKey) const
     {
     {
-        Node* node = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* node = static_cast<Node*>(Ptrs()[hashKey]);
         while (node)
         while (node)
         {
         {
             if (node->key_ == key)
             if (node->key_ == key)
@@ -439,7 +449,7 @@ private:
     {
     {
         previous = 0;
         previous = 0;
         
         
-        Node* node = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        Node* node = static_cast<Node*>(Ptrs()[hashKey]);
         while (node)
         while (node)
         {
         {
             if (node->key_ == key)
             if (node->key_ == key)
@@ -527,8 +537,8 @@ private:
     {
     {
         for (Iterator it = Begin(); it != End(); ++it)
         for (Iterator it = Begin(); it != End(); ++it)
         {
         {
-            Node* node = reinterpret_cast<Node*>(it.ptr_);
-            unsigned hashKey = MakeHash(*it) & (NumBuckets() - 1);
+            Node* node = static_cast<Node*>(it.ptr_);
+            unsigned hashKey = Hash(*it);
             node->down_ = Ptrs()[hashKey];
             node->down_ = Ptrs()[hashKey];
             Ptrs()[hashKey] = node;
             Ptrs()[hashKey] = node;
         }
         }
@@ -536,6 +546,9 @@ private:
     
     
     /// Compare two nodes.
     /// Compare two nodes.
     static bool CompareNodes(Node*& lhs, Node*& rhs) { return lhs->key_ < rhs->key_; }
     static bool CompareNodes(Node*& lhs, Node*& rhs) { return lhs->key_ < rhs->key_; }
+
+    /// Compute a hash based on the key and the bucket size
+    unsigned Hash(const T& key) const { return MakeHash(key) & (NumBuckets() - 1); }
 };
 };
 
 
 }
 }

+ 13 - 4
Engine/Container/List.h

@@ -273,8 +273,17 @@ public:
     /// Clear the list.
     /// Clear the list.
     void Clear()
     void Clear()
     {
     {
-        while (size_)
-            EraseNode(Head());
+        if (Size())
+        {
+            for (Iterator i = Begin(); i != End(); )
+            {
+                FreeNode(static_cast<Node*>(i++.ptr_));
+                i.ptr_->prev_ = 0;
+            }
+            
+            head_ = tail_;
+            size_ = 0;
+        }
     }
     }
     
     
     /// Resize the list by removing or adding items at the end.
     /// Resize the list by removing or adding items at the end.
@@ -330,9 +339,9 @@ public:
     
     
 private:
 private:
     /// Return the head node.
     /// Return the head node.
-    Node* Head() const { return reinterpret_cast<Node*>(head_); }
+    Node* Head() const { return static_cast<Node*>(head_); }
     /// Return the tail node.
     /// Return the tail node.
-    Node* Tail() const { return reinterpret_cast<Node*>(tail_); }
+    Node* Tail() const { return static_cast<Node*>(tail_); }
     
     
     /// Allocate and insert a node into the list.
     /// Allocate and insert a node into the list.
     void InsertNode(Node* dest, const T& value)
     void InsertNode(Node* dest, const T& value)

+ 1 - 1
Engine/Container/Pair.h

@@ -59,7 +59,7 @@ public:
         return second_ < rhs.second_;
         return second_ < rhs.second_;
     }
     }
     
     
-    /// Test for less than with another pair.
+    /// Test for greater than with another pair.
     bool operator > (const Pair<T, U>& rhs) const
     bool operator > (const Pair<T, U>& rhs) const
     {
     {
         if (first_ > rhs.first_)
         if (first_ > rhs.first_)

+ 13 - 5
Engine/Container/Str.cpp

@@ -939,15 +939,15 @@ Vector<String> String::Split(const char* str, char separator)
     return ret;
     return ret;
 }
 }
 
 
-void String::Print(const char *formatString, ... )
+void String::AppendWithFormat(const char* formatString, ... )
 {
 {
     va_list args;
     va_list args;
     va_start(args, formatString);
     va_start(args, formatString);
-    PrintArgs(formatString, args);
+    AppendWithFormatArgs(formatString, args);
     va_end(args);
     va_end(args);
 }
 }
 
 
-void String::PrintArgs(const char *formatString, va_list args)
+void String::AppendWithFormatArgs(const char* formatString, va_list args)
 {
 {
     int pos = 0, lastPos = 0;
     int pos = 0, lastPos = 0;
     int length = strlen(formatString);
     int length = strlen(formatString);
@@ -975,6 +975,14 @@ void String::PrintArgs(const char *formatString, va_list args)
                 break;
                 break;
             }
             }
             
             
+        // Unsigned
+        case 'u':
+            {
+                unsigned arg = va_arg(args, unsigned);
+                Append(String(arg));
+                break;
+            }
+            
         // Real
         // Real
         case 'f':
         case 'f':
             {
             {
@@ -986,7 +994,7 @@ void String::PrintArgs(const char *formatString, va_list args)
         // Character
         // Character
         case 'c':
         case 'c':
             {
             {
-                char arg = va_arg(args, char);
+                int arg = va_arg(args, int);
                 Append(arg);
                 Append(arg);
                 break;
                 break;
             }
             }
@@ -1014,7 +1022,7 @@ void String::PrintArgs(const char *formatString, va_list args)
             {
             {
                 char buf[CONVERSION_BUFFER_LENGTH];
                 char buf[CONVERSION_BUFFER_LENGTH];
                 int arg = va_arg(args, int);
                 int arg = va_arg(args, int);
-                int arglen = ::sprintf(buf, "%p", arg);
+                int arglen = ::sprintf(buf, "%p", reinterpret_cast<void*>(arg));
                 Append(buf, arglen);
                 Append(buf, arglen);
                 break;
                 break;
             }
             }

+ 7 - 3
Engine/Container/Str.h

@@ -361,7 +361,11 @@ public:
     int Compare(const String& str, bool caseSensitive = true) const;
     int Compare(const String& str, bool caseSensitive = true) const;
     /// Return comparision result with a C string.
     /// Return comparision result with a C string.
     int Compare(const char* str, bool caseSensitive = true) const;
     int Compare(const char* str, bool caseSensitive = true) const;
-    
+    /// Return whether contains a specific occurences of string.
+    bool Contains(const String& str) const { return Find(str) != NPOS; }
+    /// Return whether contains a specific character.
+    bool Contains(char c) const { return Find(c) != NPOS; }
+
     /// Construct UTF8 content from Latin1.
     /// Construct UTF8 content from Latin1.
     void SetUTF8FromLatin1(const char* str);
     void SetUTF8FromLatin1(const char* str);
     /// Construct UTF8 content from wide characters.
     /// Construct UTF8 content from wide characters.
@@ -426,9 +430,9 @@ public:
     }
     }
     
     
     /// Append to string using formatting.
     /// Append to string using formatting.
-    void Print(const char *formatString, ... );
+    void AppendWithFormat(const char* formatString, ... );
     /// Append to string using variable arguments.
     /// Append to string using variable arguments.
-    void PrintArgs(const char *formatString, va_list args);
+    void AppendWithFormatArgs(const char* formatString, va_list args);
     
     
     /// Compare two C strings.
     /// Compare two C strings.
     static int Compare(const char* str1, const char* str2, bool caseSensitive);
     static int Compare(const char* str1, const char* str2, bool caseSensitive);

+ 31 - 5
Engine/Container/Vector.h

@@ -246,7 +246,7 @@ public:
     void Erase(unsigned pos, unsigned length = 1)
     void Erase(unsigned pos, unsigned length = 1)
     {
     {
         // Return if the range is illegal
         // Return if the range is illegal
-        if (!length || pos + length > size_)
+        if (pos + length > size_ || !length)
             return;
             return;
         
         
         MoveRange(pos, pos + length, size_ - pos - length);
         MoveRange(pos, pos + length, size_ - pos - length);
@@ -276,6 +276,19 @@ public:
         return Begin() + pos;
         return Begin() + pos;
     }
     }
     
     
+    /// Erase an element if found.
+    bool Remove(const T& value)
+    {
+        Iterator i = Find(value);
+        if (i != End())
+        {
+            Erase(i);
+            return true;
+        }
+        else
+            return false;
+    }
+    
     /// Clear the vector.
     /// Clear the vector.
     void Clear() { Resize(0); }
     void Clear() { Resize(0); }
     /// Resize the vector.
     /// Resize the vector.
@@ -343,9 +356,9 @@ public:
     /// Return const first element.
     /// Return const first element.
     const T& Front() const { return Buffer()[0]; }
     const T& Front() const { return Buffer()[0]; }
     /// Return last element.
     /// Return last element.
-    T& Back() { return Buffer()[size_ - 1]; }
+    T& Back() { assert(size_); return Buffer()[size_ - 1]; }
     /// Return const last element.
     /// Return const last element.
-    const T& Back() const { return Buffer()[size_ - 1]; }
+    const T& Back() const { assert(size_); return Buffer()[size_ - 1]; }
     /// Return size of vector.
     /// Return size of vector.
     unsigned Size() const { return size_; }
     unsigned Size() const { return size_; }
     /// Return capacity of vector.
     /// Return capacity of vector.
@@ -693,6 +706,19 @@ public:
         return Begin() + pos;
         return Begin() + pos;
     }
     }
     
     
+    /// Erase an element if found.
+    bool Remove(const T& value)
+    {
+        Iterator i = Find(value);
+        if (i != End())
+        {
+            Erase(i);
+            return true;
+        }
+        else
+            return false;
+    }
+    
     /// Clear the vector.
     /// Clear the vector.
     void Clear() { Resize(0); }
     void Clear() { Resize(0); }
     
     
@@ -782,9 +808,9 @@ public:
     /// Return const first element.
     /// Return const first element.
     const T& Front() const { return Buffer()[0]; }
     const T& Front() const { return Buffer()[0]; }
     /// Return last element.
     /// Return last element.
-    T& Back() { return Buffer()[size_ - 1]; }
+    T& Back() { assert(size_); return Buffer()[size_ - 1]; }
     /// Return const last element.
     /// Return const last element.
-    const T& Back() const { return Buffer()[size_ - 1]; }
+    const T& Back() const { assert(size_); return Buffer()[size_ - 1]; }
     /// Return number of elements.
     /// Return number of elements.
     unsigned Size() const { return size_; }
     unsigned Size() const { return size_; }
     /// Return capacity of vector.
     /// Return capacity of vector.

+ 36 - 9
Engine/Core/ProcessUtils.cpp

@@ -24,12 +24,19 @@
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "Mutex.h"
 #include "Mutex.h"
 #include "ProcessUtils.h"
 #include "ProcessUtils.h"
+#include "MathDefs.h"
 
 
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <fcntl.h>
 #include <fcntl.h>
 
 
-#if !defined(ANDROID) && !defined(IOS)
+#ifdef __APPLE__
+#include "TargetConditionals.h"
+#endif
+
+#if defined(IOS)
+#include <mach/mach_host.h>
+#elif !defined(ANDROID)
 #include <libcpuid.h>
 #include <libcpuid.h>
 #endif
 #endif
 
 
@@ -80,7 +87,14 @@ static String currentLine;
 static Vector<String> arguments;
 static Vector<String> arguments;
 static Mutex staticMutex;
 static Mutex staticMutex;
 
 
-#if !defined(ANDROID) && !defined(IOS)
+#if defined(IOS)
+void GetCPUData(host_basic_info_data_t* data)
+{
+    mach_msg_type_number_t infoCount;
+    infoCount = HOST_BASIC_INFO_COUNT;
+    host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)data, &infoCount);
+}
+#elif !defined(ANDROID)
 void GetCPUData(struct cpu_id_t* data)
 void GetCPUData(struct cpu_id_t* data)
 {
 {
     if (cpu_identify(0, data) < 0)
     if (cpu_identify(0, data) < 0)
@@ -242,11 +256,7 @@ const Vector<String>& ParseArguments(int argc, char** argv)
     String cmdLine;
     String cmdLine;
     
     
     for (int i = 0; i < argc; ++i)
     for (int i = 0; i < argc; ++i)
-    {
-        cmdLine += (const char*)argv[i];
-        if (i < argc - 1)
-            cmdLine += ' ';
-    }
+        cmdLine.AppendWithFormat("\"%s\" ", (const char*)argv[i]);
     
     
     return ParseArguments(cmdLine);
     return ParseArguments(cmdLine);
 }
 }
@@ -344,7 +354,16 @@ String GetPlatform()
 
 
 unsigned GetNumPhysicalCPUs()
 unsigned GetNumPhysicalCPUs()
 {
 {
-    #if !defined(ANDROID) && !defined(IOS)
+    #if defined(IOS)
+    host_basic_info_data_t data;
+    GetCPUData(&data);
+    #if defined(TARGET_IPHONE_SIMULATOR)
+    // Hardcoded to dual-core on simulator mode even if the host has more
+    return Min(2, data.physical_cpu);
+    #else
+    return data.physical_cpu;
+    #endif
+    #elif !defined(ANDROID)
     struct cpu_id_t data;
     struct cpu_id_t data;
     GetCPUData(&data);
     GetCPUData(&data);
     return data.num_cores;
     return data.num_cores;
@@ -356,7 +375,15 @@ unsigned GetNumPhysicalCPUs()
 
 
 unsigned GetNumLogicalCPUs()
 unsigned GetNumLogicalCPUs()
 {
 {
-    #if !defined(ANDROID) && !defined(IOS)
+    #if defined(IOS)
+    host_basic_info_data_t data;
+    GetCPUData(&data);
+    #if defined(TARGET_IPHONE_SIMULATOR)
+    return Min(2, data.logical_cpu);
+    #else
+    return data.logical_cpu;
+    #endif
+    #elif !defined(ANDROID)
     struct cpu_id_t data;
     struct cpu_id_t data;
     GetCPUData(&data);
     GetCPUData(&data);
     return data.num_logical_cpus;
     return data.num_logical_cpus;

+ 10 - 0
Engine/Core/StringUtils.cpp

@@ -390,4 +390,14 @@ unsigned GetStringListIndex(const char* value, const char** strings, unsigned de
     return defaultIndex;
     return defaultIndex;
 }
 }
 
 
+String ToString(const char* formatString, ...)
+{
+    String ret;
+    va_list args;
+    va_start(args, formatString);
+    ret.AppendWithFormatArgs(formatString, args);
+    va_end(args);
+    return ret;
+}
+
 }
 }

+ 2 - 0
Engine/Core/StringUtils.h

@@ -90,5 +90,7 @@ unsigned GetStringListIndex(const String& value, const String* strings, unsigned
 unsigned GetStringListIndex(const char* value, const String* strings, unsigned defaultIndex, bool caseSensitive = false);
 unsigned GetStringListIndex(const char* value, const String* strings, unsigned defaultIndex, bool caseSensitive = false);
 /// Return an index to a C string list corresponding to the given C string, or a default value if not found. The string list must be empty-terminated.
 /// Return an index to a C string list corresponding to the given C string, or a default value if not found. The string list must be empty-terminated.
 unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive = false);
 unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive = false);
+/// Return a formatted string.
+String ToString(const char* formatString, ...);
 
 
 }
 }

+ 6 - 11
Engine/Core/Variant.cpp

@@ -56,7 +56,7 @@ static const String typeNames[] =
     "VariantVector",
     "VariantVector",
     "VariantMap",
     "VariantMap",
     "IntRect",
     "IntRect",
-    "IntVector2"
+    "IntVector2",
     ""
     ""
 };
 };
 
 
@@ -334,14 +334,6 @@ String Variant::ToString() const
         // Pointer serialization not supported (convert to null)
         // Pointer serialization not supported (convert to null)
         return String(0);
         return String(0);
         
         
-    case VAR_RESOURCEREF:
-    case VAR_RESOURCEREFLIST:
-    case VAR_VARIANTVECTOR:
-    case VAR_VARIANTMAP:
-        // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
-        // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
-        return String();
-        
     case VAR_INTRECT:
     case VAR_INTRECT:
         return (reinterpret_cast<const IntRect*>(&value_))->ToString();
         return (reinterpret_cast<const IntRect*>(&value_))->ToString();
         
         
@@ -349,7 +341,10 @@ String Variant::ToString() const
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
         
         
     default:
     default:
-        return String();
+        // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_VARIANTMAP
+        // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
+        // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
+        return String::EMPTY;
     }
     }
 }
 }
 
 
@@ -579,7 +574,7 @@ VariantType Variant::GetTypeFromName(const String& typeName)
 VariantType Variant::GetTypeFromName(const char* typeName)
 VariantType Variant::GetTypeFromName(const char* typeName)
 {
 {
     unsigned index = 0;
     unsigned index = 0;
-    while (!typeNames[index].Empty())
+    while (index < MAX_VAR_TYPES)
     {
     {
         if (!typeNames[index].Compare(typeName, false))
         if (!typeNames[index].Compare(typeName, false))
             return (VariantType)index;
             return (VariantType)index;

+ 2 - 1
Engine/Core/Variant.h

@@ -53,7 +53,8 @@ enum VariantType
     VAR_VARIANTVECTOR,
     VAR_VARIANTVECTOR,
     VAR_VARIANTMAP,
     VAR_VARIANTMAP,
     VAR_INTRECT,
     VAR_INTRECT,
-    VAR_INTVECTOR2
+    VAR_INTVECTOR2,
+    MAX_VAR_TYPES
 };
 };
 
 
 /// Union for the possible variant values. Also stores non-POD objects such as String which must not exceed 16 bytes in size.
 /// Union for the possible variant values. Also stores non-POD objects such as String which must not exceed 16 bytes in size.

+ 11 - 1
Engine/Engine/APITemplates.h

@@ -314,6 +314,12 @@ template <class T> void RegisterNamedObjectConstructor(asIScriptEngine* engine,
 
 
 static const AttributeInfo noAttributeInfo;
 static const AttributeInfo noAttributeInfo;
 
 
+// To keep Xcode LLVM/Clang happy - it erroneosly warns on unused functions defined below which are actually being referenced in the code
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
 static const AttributeInfo& SerializableGetAttributeInfo(unsigned index, Serializable* ptr)
 static const AttributeInfo& SerializableGetAttributeInfo(unsigned index, Serializable* ptr)
 {
 {
     const Vector<AttributeInfo>* attributes = ptr->GetAttributes();
     const Vector<AttributeInfo>* attributes = ptr->GetAttributes();
@@ -474,7 +480,7 @@ static CScriptArray* NodeGetChildrenWithClassName(const String& className, bool
     ptr->GetChildrenWithComponent<ScriptInstance>(nodes, recursive);
     ptr->GetChildrenWithComponent<ScriptInstance>(nodes, recursive);
     for (PODVector<Node*>::Iterator i = nodes.Begin(); i != nodes.End(); ++i)
     for (PODVector<Node*>::Iterator i = nodes.Begin(); i != nodes.End(); ++i)
     {
     {
-        Node* node = (*i);
+        Node* node = *i;
         const Vector<SharedPtr<Component> >& components = node->GetComponents();
         const Vector<SharedPtr<Component> >& components = node->GetComponents();
         for (Vector<SharedPtr<Component> >::ConstIterator j = components.Begin(); j != components.End(); ++j)
         for (Vector<SharedPtr<Component> >::ConstIterator j = components.Begin(); j != components.End(); ++j)
         {
         {
@@ -744,6 +750,10 @@ static VariantMap& UIElementGetVars(UIElement* ptr)
     return const_cast<VariantMap&>(ptr->GetVars());
     return const_cast<VariantMap&>(ptr->GetVars());
 }
 }
 
 
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
 /// Template function for registering a class derived from UIElement.
 /// Template function for registering a class derived from UIElement.
 template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className)
 template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className)
 {
 {

+ 6 - 11
Engine/Engine/Console.cpp

@@ -76,7 +76,6 @@ Console::Console(Context* context) :
     uiRoot->AddChild(background_);
     uiRoot->AddChild(background_);
     
     
     SetNumRows(DEFAULT_CONSOLE_ROWS);
     SetNumRows(DEFAULT_CONSOLE_ROWS);
-    UpdateElements();
     
     
     SubscribeToEvent(lineEdit_, E_TEXTFINISHED, HANDLER(Console, HandleTextFinished));
     SubscribeToEvent(lineEdit_, E_TEXTFINISHED, HANDLER(Console, HandleTextFinished));
     SubscribeToEvent(lineEdit_, E_UNHANDLEDKEY, HANDLER(Console, HandleLineEditKey));
     SubscribeToEvent(lineEdit_, E_UNHANDLEDKEY, HANDLER(Console, HandleLineEditKey));
@@ -86,9 +85,7 @@ Console::Console(Context* context) :
 
 
 Console::~Console()
 Console::~Console()
 {
 {
-    UI* ui = GetSubsystem<UI>();
-    if (ui)
-        ui->GetRoot()->RemoveChild(background_);
+    background_->Remove();
 }
 }
 
 
 void Console::SetStyle(XMLFile* style)
 void Console::SetStyle(XMLFile* style)
@@ -162,9 +159,7 @@ void Console::UpdateElements()
 
 
 bool Console::IsVisible() const
 bool Console::IsVisible() const
 {
 {
-    if (!background_)
-        return false;
-    return background_->IsVisible();
+    return background_ ? background_->IsVisible() : false;
 }
 }
 
 
 const String& Console::GetHistoryRow(unsigned index) const
 const String& Console::GetHistoryRow(unsigned index) const
@@ -189,8 +184,8 @@ void Console::HandleTextFinished(StringHash eventType, VariantMap& eventData)
             history_.Erase(history_.Begin());
             history_.Erase(history_.Begin());
         historyPosition_ = history_.Size();
         historyPosition_ = history_.Size();
         
         
-        current_Row.Clear();
-        lineEdit_->SetText(current_Row);
+        currentRow_.Clear();
+        lineEdit_->SetText(currentRow_);
     }
     }
 }
 }
 
 
@@ -209,7 +204,7 @@ void Console::HandleLineEditKey(StringHash eventType, VariantMap& eventData)
         if (historyPosition_ > 0)
         if (historyPosition_ > 0)
         {
         {
             if (historyPosition_ == history_.Size())
             if (historyPosition_ == history_.Size())
-                current_Row = lineEdit_->GetText();
+                currentRow_ = lineEdit_->GetText();
             --historyPosition_;
             --historyPosition_;
             changed = true;
             changed = true;
         }
         }
@@ -229,7 +224,7 @@ void Console::HandleLineEditKey(StringHash eventType, VariantMap& eventData)
         if (historyPosition_ < history_.Size())
         if (historyPosition_ < history_.Size())
             lineEdit_->SetText(history_[historyPosition_]);
             lineEdit_->SetText(history_[historyPosition_]);
         else
         else
-            lineEdit_->SetText(current_Row);
+            lineEdit_->SetText(currentRow_);
     }
     }
 }
 }
 
 

+ 1 - 1
Engine/Engine/Console.h

@@ -100,7 +100,7 @@ private:
     /// Command history.
     /// Command history.
     Vector<String> history_;
     Vector<String> history_;
     /// Current row being edited.
     /// Current row being edited.
-    String current_Row;
+    String currentRow_;
     /// Command history maximum rows.
     /// Command history maximum rows.
     unsigned historyRows_;
     unsigned historyRows_;
     /// Command history current position.
     /// Command history current position.

+ 4 - 6
Engine/Engine/CoreAPI.cpp

@@ -362,7 +362,7 @@ static Variant& VariantMapAtHash(ShortStringHash key, VariantMap& map)
 
 
 static bool VariantMapContains(const String& key, VariantMap& map)
 static bool VariantMapContains(const String& key, VariantMap& map)
 {
 {
-    return map.Find(ShortStringHash(key)) != map.End();
+    return map.Contains(ShortStringHash(key));
 }
 }
 
 
 static void VariantMapErase(const String& key, VariantMap& map)
 static void VariantMapErase(const String& key, VariantMap& map)
@@ -372,7 +372,7 @@ static void VariantMapErase(const String& key, VariantMap& map)
 
 
 static bool VariantMapContainsHash(ShortStringHash key, VariantMap& map)
 static bool VariantMapContainsHash(ShortStringHash key, VariantMap& map)
 {
 {
-    return map.Find(key) != map.End();
+    return map.Contains(key);
 }
 }
 
 
 static void VariantMapEraseHash(ShortStringHash key, VariantMap& map)
 static void VariantMapEraseHash(ShortStringHash key, VariantMap& map)
@@ -383,6 +383,7 @@ static void VariantMapEraseHash(ShortStringHash key, VariantMap& map)
 static CScriptArray* VariantMapGetKeys(const VariantMap& map)
 static CScriptArray* VariantMapGetKeys(const VariantMap& map)
 {
 {
     Vector<ShortStringHash> result;
     Vector<ShortStringHash> result;
+    result.Reserve(map.Size());
     for (VariantMap::ConstIterator i = map.Begin(); i != map.End(); ++i)
     for (VariantMap::ConstIterator i = map.Begin(); i != map.End(); ++i)
         result.Push(i->first_);
         result.Push(i->first_);
     return VectorToArray<ShortStringHash>(result, "Array<StringHash>");
     return VectorToArray<ShortStringHash>(result, "Array<StringHash>");
@@ -622,10 +623,7 @@ static CScriptArray* AttributeInfoGetEnumNames(AttributeInfo* ptr)
     Vector<String> enumNames;
     Vector<String> enumNames;
     const char** enumNamePtrs = ptr->enumNames_;
     const char** enumNamePtrs = ptr->enumNames_;
     while (enumNamePtrs && *enumNamePtrs)
     while (enumNamePtrs && *enumNamePtrs)
-    {
-        enumNames.Push(*enumNamePtrs);
-        ++enumNamePtrs;
-    }
+        enumNames.Push(*enumNamePtrs++);
     return VectorToArray<String>(enumNames, "Array<String>");
     return VectorToArray<String>(enumNames, "Array<String>");
 }
 }
 
 

+ 45 - 72
Engine/Engine/DebugHud.cpp

@@ -38,19 +38,22 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-static const String renderModeTexts[] = {
+static const char* renderModeTexts[] =
+{
     "Forward",
     "Forward",
     "Prepass",
     "Prepass",
     "Deferred"
     "Deferred"
 };
 };
 
 
-static const String qualityTexts[] = {
+static const char* qualityTexts[] =
+{
     "Low",
     "Low",
     "Med",
     "Med",
     "High"
     "High"
 };
 };
 
 
-static const String shadowQualityTexts[] = {
+static const char* shadowQualityTexts[] =
+{
     "16bit Low",
     "16bit Low",
     "24bit Low",
     "24bit Low",
     "16bit High",
     "16bit High",
@@ -91,14 +94,9 @@ DebugHud::DebugHud(Context* context) :
 
 
 DebugHud::~DebugHud()
 DebugHud::~DebugHud()
 {
 {
-    UI* ui = GetSubsystem<UI>();
-    if (ui)
-    {
-        UIElement* uiRoot = ui->GetRoot();
-        uiRoot->RemoveChild(statsText_);
-        uiRoot->RemoveChild(modeText_);
-        uiRoot->RemoveChild(profilerText_);
-    }
+    statsText_->Remove();
+    modeText_->Remove();
+    profilerText_->Remove();
 }
 }
 
 
 void DebugHud::Update()
 void DebugHud::Update()
@@ -108,27 +106,28 @@ void DebugHud::Update()
     if (!renderer || !graphics)
     if (!renderer || !graphics)
         return;
         return;
     
     
-    unsigned primitives, batches;
-    if (!useRendererStats_)
-    {
-        primitives = graphics->GetNumPrimitives();
-        batches = graphics->GetNumBatches();
-    }
-    else
-    {
-        primitives = renderer->GetNumPrimitives();
-        batches = renderer->GetNumBatches();
-    }
-    
     if (statsText_->IsVisible())
     if (statsText_->IsVisible())
     {
     {
-        String stats = 
-            "Triangles " + String(graphics->GetNumPrimitives()) +
-            "\nBatches " + String(graphics->GetNumBatches()) +
-            "\nViews " + String(renderer->GetNumViews()) + 
-            "\nLights " + String(renderer->GetNumLights(true)) +
-            "\nShadowmaps " + String(renderer->GetNumShadowMaps(true)) +
-            "\nOccluders " + String(renderer->GetNumOccluders(true));
+        unsigned primitives, batches;
+        if (!useRendererStats_)
+        {
+            primitives = graphics->GetNumPrimitives();
+            batches = graphics->GetNumBatches();
+        }
+        else
+        {
+            primitives = renderer->GetNumPrimitives();
+            batches = renderer->GetNumBatches();
+        }
+        
+        String stats;
+        stats.AppendWithFormat("Triangles %u\nBatches %u\nViews %u\nLights %u\nShadowmaps %u\nOccluders %u",
+            primitives,
+            batches,
+            renderer->GetNumViews(),
+            renderer->GetNumLights(true),
+            renderer->GetNumShadowMaps(true),
+            renderer->GetNumOccluders(true));
         
         
         statsText_->SetText(stats);
         statsText_->SetText(stats);
     }
     }
@@ -136,47 +135,21 @@ void DebugHud::Update()
     if (modeText_->IsVisible())
     if (modeText_->IsVisible())
     {
     {
         String mode;
         String mode;
-        
-        mode += renderModeTexts[renderer->GetRenderMode()];
-        mode += " Tex: " + qualityTexts[renderer->GetTextureQuality()];
-        mode += " Mat: " + qualityTexts[renderer->GetMaterialQuality()];
-        
-        mode += " Spec:";
-        if (renderer->GetSpecularLighting())
-            mode += "On";
-        else
-            mode += "Off";
-        
-        mode += " Shadows:";
-        if (renderer->GetDrawShadows())
-            mode += "On";
-        else
-            mode += "Off";
-        
-        mode += " Size:" + String(renderer->GetShadowMapSize());
-        mode += " Quality:" + shadowQualityTexts[renderer->GetShadowQuality()];
-        
-        mode += " Occlusion:";
-        if (renderer->GetMaxOccluderTriangles() > 0)
-            mode += "On";
-        else
-            mode += "Off";
-        
-        mode += " Instancing:";
-        if (renderer->GetDynamicInstancing())
-            mode += "On";
-        else
-            mode += "Off";
-        
-        mode += " Mode:";
-        #ifdef USE_OPENGL
-        mode += "OGL";
-        #else
-        if (graphics->GetSM3Support())
-            mode += "SM3";
-        else
-            mode += "SM2";
-        #endif
+        mode.AppendWithFormat("Render:%s Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s Mode:%s",
+            renderModeTexts[renderer->GetRenderMode()],
+            qualityTexts[renderer->GetTextureQuality()],
+            qualityTexts[renderer->GetMaterialQuality()],
+            renderer->GetSpecularLighting() ? "On" : "Off",
+            renderer->GetDrawShadows() ? "On" : "Off",
+            renderer->GetShadowMapSize(),
+            shadowQualityTexts[renderer->GetShadowQuality()],
+            renderer->GetMaxOccluderTriangles() > 0 ? "On" : "Off",
+            renderer->GetDynamicInstancing() ? "On" : "Off",
+            #ifdef USE_OPENGL
+            "OGL");
+            #else
+            graphics->GetSM3Support() ? "SM3" : "SM2");
+            #endif
         
         
         modeText_->SetText(mode);
         modeText_->SetText(mode);
     }
     }
@@ -239,7 +212,7 @@ void DebugHud::Toggle(unsigned mode)
 
 
 void DebugHud::ToggleAll()
 void DebugHud::ToggleAll()
 {
 {
-    Toggle(DEBUGHUD_SHOW_STATS | DEBUGHUD_SHOW_MODE | DEBUGHUD_SHOW_PROFILER);
+    Toggle(DEBUGHUD_SHOW_ALL);
 }
 }
 
 
 unsigned DebugHud::GetMode() const
 unsigned DebugHud::GetMode() const

+ 41 - 36
Engine/Engine/Engine.cpp

@@ -86,7 +86,8 @@ Engine::Engine(Context* context) :
     #endif
     #endif
     initialized_(false),
     initialized_(false),
     exiting_(false),
     exiting_(false),
-    headless_(false)
+    headless_(false),
+   audioPaused_(false)
 {
 {
 }
 }
 
 
@@ -155,31 +156,30 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
                 forceSM2 = true;
                 forceSM2 = true;
             else
             else
             {
             {
+                int value;
+                if (argument.Length() > 1)
+                    value = ToInt(argument.Substring(1));
+                
                 switch (tolower(argument[0]))
                 switch (tolower(argument[0]))
                 {
                 {
                 case 'x':
                 case 'x':
-                    if (arguments[i].Length() > 1)
-                        width = ToInt(argument.Substring(1));
+                    width = value;
                     break;
                     break;
                     
                     
                 case 'y':
                 case 'y':
-                    if (arguments[i].Length() > 1)
-                        height = ToInt(argument.Substring(1));
+                    height = value;
                     break;
                     break;
                 
                 
                 case 'm':
                 case 'm':
-                    if (arguments[i].Length() > 1)
-                        multiSample = ToInt(argument.Substring(1));
+                    multiSample = value;
                     break;
                     break;
                     
                     
                 case 'b':
                 case 'b':
-                    if (arguments[i].Length() > 1)
-                        buffer = ToInt(argument.Substring(1));
+                    buffer = value;
                     break;
                     break;
                     
                     
                 case 'r':
                 case 'r':
-                    if (arguments[i].Length() > 1)
-                        mixRate = ToInt(argument.Substring(1));
+                    mixRate = value;
                     break;
                     break;
                     
                     
                 case 'v':
                 case 'v':
@@ -208,24 +208,24 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     
     
     // Start logging
     // Start logging
     Log* log = GetSubsystem<Log>();
     Log* log = GetSubsystem<Log>();
-    log->Open(logName);
-    if (logDebug)
-        log->SetLevel(LOG_DEBUG);
+    if (log)
+    {
+        log->Open(logName);
+        if (logDebug)
+            log->SetLevel(LOG_DEBUG);
+    }
     
     
     // Set maximally accurate low res timer
     // Set maximally accurate low res timer
     GetSubsystem<Time>()->SetTimerPeriod(1);
     GetSubsystem<Time>()->SetTimerPeriod(1);
     
     
     // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
     // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
-    int numThreads = threads ? GetNumPhysicalCPUs() - 1 : 0;
-    if (numThreads > 0)
+    unsigned numThreads = threads ? GetNumPhysicalCPUs() - 1 : 0;
+    if (numThreads)
     {
     {
         GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
         GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
         
         
-        String workerThreadString = "Created " + String(numThreads) + " worker thread";
-        if (numThreads > 1)
-            workerThreadString += "s";
-        LOGINFO(workerThreadString);
+        LOGINFO(ToString("Created %u worker thread%s", numThreads, numThreads > 1 ? "s" : ""));
     }
     }
     
     
     // Add default resource paths: CoreData package or directory, Data package or directory
     // Add default resource paths: CoreData package or directory, Data package or directory
@@ -318,15 +318,7 @@ bool Engine::InitializeScripting()
 
 
 void Engine::RunFrame()
 void Engine::RunFrame()
 {
 {
-    if (!initialized_ || exiting_)
-        return;
-    
-    // Set exit flag if the window was closed
-    if (!headless_ && !GetSubsystem<Graphics>()->IsInitialized())
-    {
-        exiting_ = true;
-        return;
-    }
+    assert(initialized_ && !exiting_);
     
     
     Time* time = GetSubsystem<Time>();
     Time* time = GetSubsystem<Time>();
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
@@ -335,17 +327,24 @@ void Engine::RunFrame()
     time->BeginFrame(timeStep_);
     time->BeginFrame(timeStep_);
     
     
     // If pause when minimized -mode is in use, stop updates and audio as necessary
     // If pause when minimized -mode is in use, stop updates and audio as necessary
-    if (!input || !input->IsMinimized() || !pauseMinimized_)
+    if (pauseMinimized_ && input->IsMinimized())
     {
     {
-        if (pauseMinimized_ && audio && audio->IsInitialized() && !audio->IsPlaying())
-            audio->Play();
-        
-        Update();
+        if (audio->IsPlaying())
+        {
+            audio->Stop();
+            audioPaused_ = true;
+        }
     }
     }
     else
     else
     {
     {
-        if (audio && audio->IsInitialized() && audio->IsPlaying())
-            audio->Stop();
+        // Only unpause when it was paused by the engine
+        if (audioPaused_)
+        {
+            audio->Play();
+            audioPaused_ = false;
+        }
+        
+        Update();
     }
     }
     
     
     Render();
     Render();
@@ -408,6 +407,7 @@ void Engine::DumpProfilingData()
 
 
 void Engine::DumpResources()
 void Engine::DumpResources()
 {
 {
+    #ifdef ENABLE_LOGGING
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     const HashMap<ShortStringHash, ResourceGroup>& resourceGroups = cache->GetAllResources();
     const HashMap<ShortStringHash, ResourceGroup>& resourceGroups = cache->GetAllResources();
     LOGRAW("\n");
     LOGRAW("\n");
@@ -426,10 +426,12 @@ void Engine::DumpResources()
     }
     }
     
     
     LOGRAW("Total memory use of all resources " + String(cache->GetTotalMemoryUse()) + "\n\n");
     LOGRAW("Total memory use of all resources " + String(cache->GetTotalMemoryUse()) + "\n\n");
+    #endif
 }
 }
 
 
 void Engine::DumpMemory()
 void Engine::DumpMemory()
 {
 {
+    #ifdef ENABLE_LOGGING
     #if defined(_MSC_VER) && defined(_DEBUG)
     #if defined(_MSC_VER) && defined(_DEBUG)
     _CrtMemState state;
     _CrtMemState state;
     _CrtMemCheckpoint(&state);
     _CrtMemCheckpoint(&state);
@@ -464,6 +466,7 @@ void Engine::DumpMemory()
     #else
     #else
     LOGRAW("DumpMemory() supported on MSVC debug mode only\n\n");
     LOGRAW("DumpMemory() supported on MSVC debug mode only\n\n");
     #endif
     #endif
+    #endif
 }
 }
 
 
 void Engine::Update()
 void Engine::Update()
@@ -570,7 +573,9 @@ void Engine::RegisterSubsystems()
     #ifdef ENABLE_PROFILING
     #ifdef ENABLE_PROFILING
     context_->RegisterSubsystem(new Profiler(context_));
     context_->RegisterSubsystem(new Profiler(context_));
     #endif
     #endif
+    #ifdef ENABLE_LOGGING
     context_->RegisterSubsystem(new Log(context_));
     context_->RegisterSubsystem(new Log(context_));
+    #endif
     context_->RegisterSubsystem(new FileSystem(context_));
     context_->RegisterSubsystem(new FileSystem(context_));
     context_->RegisterSubsystem(new ResourceCache(context_));
     context_->RegisterSubsystem(new ResourceCache(context_));
     context_->RegisterSubsystem(new Network(context_));
     context_->RegisterSubsystem(new Network(context_));

+ 2 - 0
Engine/Engine/Engine.h

@@ -116,6 +116,8 @@ private:
     bool exiting_;
     bool exiting_;
     /// Headless mode flag.
     /// Headless mode flag.
     bool headless_;
     bool headless_;
+    /// Audio paused flag.
+    bool audioPaused_;
 };
 };
 
 
 }
 }

+ 2 - 0
Engine/Engine/GraphicsAPI.cpp

@@ -166,6 +166,7 @@ static Viewport* ConstructViewportSceneCameraPostProcesses(Scene* scene, Camera*
     Vector<SharedPtr<PostProcess> > vec;
     Vector<SharedPtr<PostProcess> > vec;
     if (arr)
     if (arr)
     {
     {
+        vec.Reserve(arr->GetSize());
         for (unsigned i = 0; i < arr->GetSize(); ++i)
         for (unsigned i = 0; i < arr->GetSize(); ++i)
             vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
             vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
     }
     }
@@ -178,6 +179,7 @@ static Viewport* ConstructViewportSceneCameraRectPostProcesses(Scene* scene, Cam
     Vector<SharedPtr<PostProcess> > vec;
     Vector<SharedPtr<PostProcess> > vec;
     if (arr)
     if (arr)
     {
     {
+        vec.Reserve(arr->GetSize());
         for (unsigned i = 0; i < arr->GetSize(); ++i)
         for (unsigned i = 0; i < arr->GetSize(); ++i)
             vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
             vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
     }
     }

+ 17 - 0
Engine/Engine/IOAPI.cpp

@@ -45,6 +45,8 @@ static Log* GetLog()
     return GetScriptContext()->GetSubsystem<Log>();
     return GetScriptContext()->GetSubsystem<Log>();
 }
 }
 
 
+#ifdef ENABLE_LOGGING
+
 static void Print(const String& value)
 static void Print(const String& value)
 {
 {
     GetLog()->WriteRaw(value + "\n");
     GetLog()->WriteRaw(value + "\n");
@@ -95,6 +97,21 @@ static void LogError(const String& str, Log* ptr)
     ptr->Write(LOG_ERROR, str);
     ptr->Write(LOG_ERROR, str);
 }
 }
 
 
+#else
+
+static void Print(const String& value) {}
+static void Print(int value) {}
+static void Print(unsigned value) {}
+static void Print(float value) {}
+static void Print(bool value) {}
+static void LogWrite(const String& str, Log* ptr) {}
+static void LogDebug(const String& str, Log* ptr) {}
+static void LogInfo(const String& str, Log* ptr) {}
+static void LogWarning(const String& str, Log* ptr) {}
+static void LogError(const String& str, Log* ptr) {}
+
+#endif
+
 static void RegisterLog(asIScriptEngine* engine)
 static void RegisterLog(asIScriptEngine* engine)
 {
 {
     engine->RegisterGlobalProperty("const int LOG_DEBUG", (void*)&LOG_DEBUG);
     engine->RegisterGlobalProperty("const int LOG_DEBUG", (void*)&LOG_DEBUG);

+ 4 - 1
Engine/Engine/UIAPI.cpp

@@ -182,6 +182,7 @@ void ListViewSetSelections(CScriptArray* selections, ListView* ptr)
 {
 {
     unsigned numItems = selections->GetSize();
     unsigned numItems = selections->GetSize();
     PODVector<unsigned> dest;
     PODVector<unsigned> dest;
+    dest.Reserve(numItems);
     
     
     for (unsigned i = 0; i < numItems; ++i)
     for (unsigned i = 0; i < numItems; ++i)
         dest.Push(*((unsigned*)selections->At(i)));
         dest.Push(*((unsigned*)selections->At(i)));
@@ -218,7 +219,7 @@ static void RegisterListView(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ListView", "void SetScrollBarsVisible(bool, bool)", asMETHOD(ListView, SetScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void SetScrollBarsVisible(bool, bool)", asMETHOD(ListView, SetScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddItem(UIElement@+)", asMETHOD(ListView, AddItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddItem(UIElement@+)", asMETHOD(ListView, AddItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void InsertItem(uint, UIElement@+)", asMETHOD(ListView, InsertItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void InsertItem(uint, UIElement@+)", asMETHOD(ListView, InsertItem), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ListView", "void RemoveItem(UIElement@+)", asMETHODPR(ListView, RemoveItem, (UIElement*), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ListView", "void RemoveItem(UIElement@+, uint index = 0)", asMETHODPR(ListView, RemoveItem, (UIElement*, unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(uint)", asMETHODPR(ListView, RemoveItem, (unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(uint)", asMETHODPR(ListView, RemoveItem, (unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveAllItems()", asMETHOD(ListView, RemoveAllItems), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveAllItems()", asMETHOD(ListView, RemoveAllItems), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddSelection(uint)", asMETHOD(ListView, AddSelection), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddSelection(uint)", asMETHOD(ListView, AddSelection), asCALL_THISCALL);
@@ -486,6 +487,8 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
+    engine->RegisterObjectMethod("UI", "void set_nonFocusedMouseWheel(bool)", asMETHOD(UI, SetNonFocusedMouseWheel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "bool get_nonFocusedMouseWheel() const", asMETHOD(UI, IsNonFocusedMouseWheel), asCALL_THISCALL);
 }
 }
 
 
 void RegisterUIAPI(asIScriptEngine* engine)
 void RegisterUIAPI(asIScriptEngine* engine)

+ 38 - 54
Engine/Graphics/AnimatedModel.cpp

@@ -126,7 +126,8 @@ void AnimatedModel::ApplyAttributes()
 void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 {
 {
     // If no bones or no bone-level testing, use the Drawable test
     // If no bones or no bone-level testing, use the Drawable test
-    if (query.level_ < RAY_AABB || !skeleton_.GetRootBone() || !skeleton_.GetRootBone()->node_)
+    RayQueryLevel level = query.level_;
+    if (level < RAY_AABB || !skeleton_.GetRootBone() || !skeleton_.GetRootBone()->node_)
     {
     {
         Drawable::ProcessRayQuery(query, results);
         Drawable::ProcessRayQuery(query, results);
         return;
         return;
@@ -138,7 +139,6 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
     
     
     const Vector<Bone>& bones = skeleton_.GetBones();
     const Vector<Bone>& bones = skeleton_.GetBones();
     Sphere boneSphere;
     Sphere boneSphere;
-    RayQueryLevel level = query.level_;
     
     
     for (unsigned i = 0; i < bones.Size(); ++i)
     for (unsigned i = 0; i < bones.Size(); ++i)
     {
     {
@@ -146,57 +146,45 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
         if (!bone.node_)
         if (!bone.node_)
             continue;
             continue;
         
         
+        float distance;
+
         // Use hitbox if available
         // Use hitbox if available
         if (bone.collisionMask_ & BONECOLLISION_BOX)
         if (bone.collisionMask_ & BONECOLLISION_BOX)
         {
         {
             // Do an initial crude test using the bone's AABB
             // Do an initial crude test using the bone's AABB
             const BoundingBox& box = bone.boundingBox_;
             const BoundingBox& box = bone.boundingBox_;
             const Matrix3x4& transform = bone.node_->GetWorldTransform();
             const Matrix3x4& transform = bone.node_->GetWorldTransform();
-            float distance = query.ray_.HitDistance(box.Transformed(transform));
-            if (distance <= query.maxDistance_)
+            distance = query.ray_.HitDistance(box.Transformed(transform));
+            if (distance > query.maxDistance_)
+                continue;
+            if (level != RAY_AABB)
             {
             {
-                if (level == RAY_AABB)
-                {
-                    RayQueryResult result;
-                    result.drawable_ = this;
-                    result.node_ = GetNode();
-                    result.distance_ = distance;
-                    result.subObject_ = i;
-                    results.Push(result);
-                }
-                else
-                {
-                    // Follow with an OBB test if required
-                    Matrix3x4 inverse = transform.Inverse();
-                    Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-                    distance = localRay.HitDistance(box);
-                    if (distance <= query.maxDistance_)
-                    {
-                        RayQueryResult result;
-                        result.drawable_ = this;
-                        result.node_ = GetNode();
-                        result.distance_ = distance;
-                        result.subObject_ = i;
-                        results.Push(result);
-                    }
-                }
+                // Follow with an OBB test if required
+                Matrix3x4 inverse = transform.Inverse();
+                Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+                distance = localRay.HitDistance(box);
+                if (distance > query.maxDistance_)
+                    continue;
             }
             }
         }
         }
         else if (bone.collisionMask_ & BONECOLLISION_SPHERE)
         else if (bone.collisionMask_ & BONECOLLISION_SPHERE)
         {
         {
             boneSphere.center_ = bone.node_->GetWorldPosition();
             boneSphere.center_ = bone.node_->GetWorldPosition();
             boneSphere.radius_ = bone.radius_;
             boneSphere.radius_ = bone.radius_;
-            float distance = query.ray_.HitDistance(boneSphere);
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.subObject_ = i;
-                result.distance_ = distance;
-                results.Push(result);
-            }
+            distance = query.ray_.HitDistance(boneSphere);
+            if (distance > query.maxDistance_)
+                continue;
         }
         }
+        else
+            continue;
+
+        // If the code reaches here then we have a hit
+        RayQueryResult result;
+        result.drawable_ = this;
+        result.node_ = node_;
+        result.distance_ = distance;
+        result.subObject_ = i;
+        results.Push(result);
     }
     }
 }
 }
 
 
@@ -317,6 +305,7 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
     // Copy geometry bone mappings
     // Copy geometry bone mappings
     const Vector<PODVector<unsigned> >& geometryBoneMappings = model->GetGeometryBoneMappings();
     const Vector<PODVector<unsigned> >& geometryBoneMappings = model->GetGeometryBoneMappings();
     geometryBoneMappings_.Clear();
     geometryBoneMappings_.Clear();
+    geometryBoneMappings_.Reserve(geometryBoneMappings.Size());
     for (unsigned i = 0; i < geometryBoneMappings.Size(); ++i)
     for (unsigned i = 0; i < geometryBoneMappings.Size(); ++i)
         geometryBoneMappings_.Push(geometryBoneMappings[i]);
         geometryBoneMappings_.Push(geometryBoneMappings[i]);
     
     
@@ -324,6 +313,7 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
     morphVertexBuffers_.Clear();
     morphVertexBuffers_.Clear();
     morphs_.Clear();
     morphs_.Clear();
     const Vector<ModelMorph>& morphs = model->GetMorphs();
     const Vector<ModelMorph>& morphs = model->GetMorphs();
+    morphs_.Reserve(morphs.Size());
     morphElementMask_ = 0;
     morphElementMask_ = 0;
     for (unsigned i = 0; i < morphs.Size(); ++i)
     for (unsigned i = 0; i < morphs.Size(); ++i)
     {
     {
@@ -551,7 +541,6 @@ void AnimatedModel::ResetMorphWeights()
         PODVector<AnimatedModel*> models;
         PODVector<AnimatedModel*> models;
         GetComponents<AnimatedModel>(models);
         GetComponents<AnimatedModel>(models);
         
         
-        // Indexing might not be the same, so use the name hash instead
         for (unsigned i = 1; i < models.Size(); ++i)
         for (unsigned i = 1; i < models.Size(); ++i)
         {
         {
             if (!models[i]->isMaster_)
             if (!models[i]->isMaster_)
@@ -773,7 +762,8 @@ void AnimatedModel::SetAnimationStatesAttr(VariantVector value)
     RemoveAllAnimationStates();
     RemoveAllAnimationStates();
     unsigned index = 0;
     unsigned index = 0;
     unsigned numStates = index < value.Size() ? value[index++].GetUInt() : 0;
     unsigned numStates = index < value.Size() ? value[index++].GetUInt() : 0;
-    while (numStates)
+    animationStates_.Reserve(numStates);
+    while (numStates--)
     {
     {
         if (index + 5 < value.Size())
         if (index + 5 < value.Size())
         {
         {
@@ -794,8 +784,6 @@ void AnimatedModel::SetAnimationStatesAttr(VariantVector value)
             SharedPtr<AnimationState> newState(new AnimationState(this, 0));
             SharedPtr<AnimationState> newState(new AnimationState(this, 0));
             animationStates_.Push(newState);
             animationStates_.Push(newState);
         }
         }
-        
-        --numStates;
     }
     }
     
     
     MarkAnimationOrderDirty();
     MarkAnimationOrderDirty();
@@ -803,8 +791,7 @@ void AnimatedModel::SetAnimationStatesAttr(VariantVector value)
 
 
 void AnimatedModel::SetMorphsAttr(const PODVector<unsigned char>& value)
 void AnimatedModel::SetMorphsAttr(const PODVector<unsigned char>& value)
 {
 {
-    unsigned index = 0;
-    while (index < value.Size())
+    for (unsigned index = 0; index < value.Size(); ++index)
         SetMorphWeight(index, (float)value[index] / 255.0f);
         SetMorphWeight(index, (float)value[index] / 255.0f);
 }
 }
 
 
@@ -817,6 +804,7 @@ VariantVector AnimatedModel::GetBonesEnabledAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
     const Vector<Bone>& bones = skeleton_.GetBones();
     const Vector<Bone>& bones = skeleton_.GetBones();
+    ret.Reserve(bones.Size());
     for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
     for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
         ret.Push(i->animated_);
         ret.Push(i->animated_);
     return ret;
     return ret;
@@ -825,6 +813,7 @@ VariantVector AnimatedModel::GetBonesEnabledAttr() const
 VariantVector AnimatedModel::GetAnimationStatesAttr() const
 VariantVector AnimatedModel::GetAnimationStatesAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
+    ret.Reserve(animationStates_.Size() * 6 + 1);
     ret.Push(animationStates_.Size());
     ret.Push(animationStates_.Size());
     for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
     for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
     {
     {
@@ -1008,16 +997,11 @@ void AnimatedModel::CloneGeometries()
                 if (clonedVertexBuffers.Contains(originalBuffer))
                 if (clonedVertexBuffers.Contains(originalBuffer))
                 {
                 {
                     VertexBuffer* clonedBuffer = clonedVertexBuffers[originalBuffer];
                     VertexBuffer* clonedBuffer = clonedVertexBuffers[originalBuffer];
-                    clone->SetVertexBuffer(l, originalBuffer, originalMask & ~clonedBuffer->GetElementMask());
-                    ++l;
-                    clone->SetVertexBuffer(l, clonedBuffer, originalMask & clonedBuffer->GetElementMask());
-                    ++l;
+                    clone->SetVertexBuffer(l++, originalBuffer, originalMask & ~clonedBuffer->GetElementMask());
+                    clone->SetVertexBuffer(l++, clonedBuffer, originalMask & clonedBuffer->GetElementMask());
                 }
                 }
                 else
                 else
-                {
-                    clone->SetVertexBuffer(l, originalBuffer, originalMask);
-                    ++l;
-                }
+                    clone->SetVertexBuffer(l++, originalBuffer, originalMask);
             }
             }
             
             
             clone->SetIndexBuffer(original->GetIndexBuffer());
             clone->SetIndexBuffer(original->GetIndexBuffer());
@@ -1272,7 +1256,7 @@ void AnimatedModel::ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsig
 void AnimatedModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
 void AnimatedModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
 {
 {
     Model* currentModel = model_;
     Model* currentModel = model_;
-    model_ = 0; // Set null to allow to be re-set
+    model_.Reset(); // Set null to allow to be re-set
     SetModel(currentModel);
     SetModel(currentModel);
 }
 }
 
 

+ 1 - 1
Engine/Graphics/AnimatedModel.h

@@ -175,7 +175,7 @@ private:
     void CloneGeometries();
     void CloneGeometries();
     /// Copy morph vertices.
     /// Copy morph vertices.
     void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
     void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
-    /// Recalculate animations. Called from UpdateNode().
+    /// Recalculate animations. Called from Update().
     void UpdateAnimation(const FrameInfo& frame);
     void UpdateAnimation(const FrameInfo& frame);
     /// Recalculate skinning.
     /// Recalculate skinning.
     void UpdateSkinning();
     void UpdateSkinning();

+ 4 - 3
Engine/Graphics/Animation.cpp

@@ -52,17 +52,18 @@ void AnimationTrack::GetKeyFrameIndex(float time, unsigned& index) const
     
     
     // Check for being too far ahead
     // Check for being too far ahead
     while (index && time < keyFrames_[index].time_)
     while (index && time < keyFrames_[index].time_)
-        index--;
+        --index;
     
     
     // Check for being too far behind
     // Check for being too far behind
     while (index < keyFrames_.Size() - 1 && time >= keyFrames_[index + 1].time_)
     while (index < keyFrames_.Size() - 1 && time >= keyFrames_[index + 1].time_)
-        index++;
+        ++index;
 }
 }
 
 
 OBJECTTYPESTATIC(Animation);
 OBJECTTYPESTATIC(Animation);
 
 
 Animation::Animation(Context* context) :
 Animation::Animation(Context* context) :
-    Resource(context)
+    Resource(context),
+    length_(0.f)
 {
 {
 }
 }
 
 

+ 28 - 33
Engine/Graphics/AnimationController.cpp

@@ -39,8 +39,6 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-static String noBoneName;
-
 static const unsigned char CTRL_LOOPED = 0x1;
 static const unsigned char CTRL_LOOPED = 0x1;
 static const unsigned char CTRL_STARTBONE = 0x2;
 static const unsigned char CTRL_STARTBONE = 0x2;
 static const unsigned char CTRL_AUTOFADE = 0x4;
 static const unsigned char CTRL_AUTOFADE = 0x4;
@@ -205,6 +203,7 @@ void AnimationController::StopLayer(unsigned char layer, float fadeOutTime)
     if (!model)
     if (!model)
         return;
         return;
     
     
+    bool needUpdate = false;
     for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
     for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
     {
     {
         AnimationState* state = model->GetAnimationState(i->hash_);
         AnimationState* state = model->GetAnimationState(i->hash_);
@@ -212,10 +211,12 @@ void AnimationController::StopLayer(unsigned char layer, float fadeOutTime)
         {
         {
             i->targetWeight_ = 0.0f;
             i->targetWeight_ = 0.0f;
             i->fadeTime_ = fadeOutTime;
             i->fadeTime_ = fadeOutTime;
+            needUpdate = true;
         }
         }
     }
     }
     
     
-    MarkNetworkUpdate();
+    if (needUpdate)
+        MarkNetworkUpdate();
 }
 }
 
 
 void AnimationController::StopAll(float fadeOutTime)
 void AnimationController::StopAll(float fadeOutTime)
@@ -224,13 +225,16 @@ void AnimationController::StopAll(float fadeOutTime)
     if (!model)
     if (!model)
         return;
         return;
     
     
-    for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
+    if (animations_.Size())
     {
     {
-        i->targetWeight_ = 0.0f;
-        i->fadeTime_ = fadeOutTime;
+        for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
+        {
+            i->targetWeight_ = 0.0f;
+            i->fadeTime_ = fadeOutTime;
+        }
+        
+        MarkNetworkUpdate();
     }
     }
-    
-    MarkNetworkUpdate();
 }
 }
 
 
 bool AnimationController::Fade(const String& name, float targetWeight, float fadeTime)
 bool AnimationController::Fade(const String& name, float targetWeight, float fadeTime)
@@ -258,6 +262,7 @@ bool AnimationController::FadeOthers(const String& name, float targetWeight, flo
     AnimatedModel* model = GetComponent<AnimatedModel>();
     AnimatedModel* model = GetComponent<AnimatedModel>();
     unsigned char layer = state->GetLayer();
     unsigned char layer = state->GetLayer();
     
     
+    bool needUpdate = false;
     for (unsigned i = 0; i < animations_.Size(); ++i)
     for (unsigned i = 0; i < animations_.Size(); ++i)
     {
     {
         if (i != index)
         if (i != index)
@@ -268,11 +273,13 @@ bool AnimationController::FadeOthers(const String& name, float targetWeight, flo
             {
             {
                 control.targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f);
                 control.targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f);
                 control.fadeTime_ = fadeTime;
                 control.fadeTime_ = fadeTime;
+                needUpdate = true;
             }
             }
         }
         }
     }
     }
     
     
-    MarkNetworkUpdate();
+    if (needUpdate)
+        MarkNetworkUpdate();
     return true;
     return true;
 }
 }
 
 
@@ -407,23 +414,19 @@ bool AnimationController::IsFadingOut(const String& name) const
 unsigned char AnimationController::GetLayer(const String& name) const
 unsigned char AnimationController::GetLayer(const String& name) const
 {
 {
     AnimationState* state = FindAnimationState(name);
     AnimationState* state = FindAnimationState(name);
-    if (!state)
-        return 0;
-    return state->GetLayer();
+    return state ? state->GetLayer() : 0;
 }
 }
 
 
 Bone* AnimationController::GetStartBone(const String& name) const
 Bone* AnimationController::GetStartBone(const String& name) const
 {
 {
     AnimationState* state = FindAnimationState(name);
     AnimationState* state = FindAnimationState(name);
-    if (!state)
-        return 0;
-    return state->GetStartBone();
+    return state ? state->GetStartBone() : 0;
 }
 }
 
 
 const String& AnimationController::GetStartBoneName(const String& name) const
 const String& AnimationController::GetStartBoneName(const String& name) const
 {
 {
     Bone* bone = GetStartBone(name);
     Bone* bone = GetStartBone(name);
-    return bone ? bone->name_ : noBoneName;
+    return bone ? bone->name_ : String::EMPTY;
 }
 }
 
 
 float AnimationController::GetTime(const String& name) const
 float AnimationController::GetTime(const String& name) const
@@ -455,9 +458,7 @@ float AnimationController::GetSpeed(const String& name) const
     unsigned index;
     unsigned index;
     AnimationState* state;
     AnimationState* state;
     FindAnimation(name, index, state);
     FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return 0.0f;
-    return animations_[index].speed_;
+    return index != M_MAX_UNSIGNED ? animations_[index].speed_ : 0.0f;
 }
 }
 
 
 float AnimationController::GetFadeTarget(const String& name) const
 float AnimationController::GetFadeTarget(const String& name) const
@@ -465,9 +466,7 @@ float AnimationController::GetFadeTarget(const String& name) const
     unsigned index;
     unsigned index;
     AnimationState* state;
     AnimationState* state;
     FindAnimation(name, index, state);
     FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return 0.0f;
-    return animations_[index].targetWeight_;
+    return index != M_MAX_UNSIGNED ? animations_[index].targetWeight_ : 0.0f;
 }
 }
 
 
 float AnimationController::GetFadeTime(const String& name) const
 float AnimationController::GetFadeTime(const String& name) const
@@ -475,9 +474,7 @@ float AnimationController::GetFadeTime(const String& name) const
     unsigned index;
     unsigned index;
     AnimationState* state;
     AnimationState* state;
     FindAnimation(name, index, state);
     FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return 0.0f;
-    return animations_[index].targetWeight_;
+    return index != M_MAX_UNSIGNED ? animations_[index].targetWeight_ : 0.0f;
 }
 }
 
 
 float AnimationController::GetAutoFade(const String& name) const
 float AnimationController::GetAutoFade(const String& name) const
@@ -485,16 +482,15 @@ float AnimationController::GetAutoFade(const String& name) const
     unsigned index;
     unsigned index;
     AnimationState* state;
     AnimationState* state;
     FindAnimation(name, index, state);
     FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return 0.0f;
-    return animations_[index].autoFadeTime_;
+    return index != M_MAX_UNSIGNED ? animations_[index].autoFadeTime_ : 0.0f;
 }
 }
 
 
 void AnimationController::SetAnimationsAttr(VariantVector value)
 void AnimationController::SetAnimationsAttr(VariantVector value)
 {
 {
     animations_.Clear();
     animations_.Clear();
+    animations_.Reserve(value.Size() / 5);  // Incomplete data is discarded
     unsigned index = 0;
     unsigned index = 0;
-    while (index < value.Size())
+    while (index + 4 < value.Size())    // Prevent out-of-bound index access
     {
     {
         AnimationControl newControl;
         AnimationControl newControl;
         newControl.hash_ = value[index++].GetStringHash();
         newControl.hash_ = value[index++].GetStringHash();
@@ -519,10 +515,8 @@ void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& v
     HashSet<StringHash> processedAnimations;
     HashSet<StringHash> processedAnimations;
     
     
     unsigned numAnimations = buf.ReadVLE();
     unsigned numAnimations = buf.ReadVLE();
-    while (numAnimations)
+    while (numAnimations--)
     {
     {
-        --numAnimations;
-        
         StringHash animHash = buf.ReadStringHash();
         StringHash animHash = buf.ReadStringHash();
         processedAnimations.Insert(animHash);
         processedAnimations.Insert(animHash);
         
         
@@ -604,6 +598,7 @@ void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& v
 VariantVector AnimationController::GetAnimationsAttr() const
 VariantVector AnimationController::GetAnimationsAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
+    ret.Reserve(animations_.Size() * 5);
     for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
     for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
     {
     {
         ret.Push(i->hash_);
         ret.Push(i->hash_);
@@ -716,7 +711,7 @@ void AnimationController::FindAnimation(const String& name, unsigned& index, Ani
 AnimationState* AnimationController::FindAnimationState(const String& name) const
 AnimationState* AnimationController::FindAnimationState(const String& name) const
 {
 {
     AnimatedModel* model = GetComponent<AnimatedModel>();
     AnimatedModel* model = GetComponent<AnimatedModel>();
-    return model ? model->GetAnimationState(name) : 0;
+    return model ? model->GetAnimationState(StringHash(name)) : 0;
 }
 }
 
 
 void AnimationController::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
 void AnimationController::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)

+ 1 - 1
Engine/Graphics/AnimationController.h

@@ -130,7 +130,7 @@ public:
     unsigned char GetLayer(const String& name) const;
     unsigned char GetLayer(const String& name) const;
     /// Return animation start bone, or null if no such animation.
     /// Return animation start bone, or null if no such animation.
     Bone* GetStartBone(const String& name) const;
     Bone* GetStartBone(const String& name) const;
-    /// Return animation start bone name, or null if no such animation.
+    /// Return animation start bone name, or empty string if no such animation.
     const String& GetStartBoneName(const String& name) const;
     const String& GetStartBoneName(const String& name) const;
     /// Return animation time position.
     /// Return animation time position.
     float GetTime(const String& name) const;
     float GetTime(const String& name) const;

+ 5 - 3
Engine/Graphics/AnimationState.cpp

@@ -65,11 +65,13 @@ void AnimationState::SetStartBone(Bone* startBone)
         return;
         return;
     
     
     Skeleton& skeleton = model_->GetSkeleton();
     Skeleton& skeleton = model_->GetSkeleton();
-    Bone* rootBone = skeleton.GetRootBone();
-    if (!rootBone)
-        return;
     if (!startBone)
     if (!startBone)
+    {
+        Bone* rootBone = skeleton.GetRootBone();
+        if (!rootBone)
+            return;
         startBone = rootBone;
         startBone = rootBone;
+    }
     
     
     // Do not reassign if the start bone did not actually change, and we already have valid bone nodes
     // Do not reassign if the start bone did not actually change, and we already have valid bone nodes
     if (startBone == startBone_ && !trackToBoneMap_.Empty())
     if (startBone == startBone_ && !trackToBoneMap_.Empty())

+ 6 - 11
Engine/Graphics/BillboardSet.cpp

@@ -52,7 +52,7 @@ inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 OBJECTTYPESTATIC(BillboardSet);
 OBJECTTYPESTATIC(BillboardSet);
 
 
 BillboardSet::BillboardSet(Context* context) :
 BillboardSet::BillboardSet(Context* context) :
-    Drawable(context),
+    Drawable(context, DRAWABLE_GEOMETRY),
     animationLodBias_(1.0f),
     animationLodBias_(1.0f),
     animationLodTimer_(0.0f),
     animationLodTimer_(0.0f),
     relative_(true),
     relative_(true),
@@ -67,8 +67,6 @@ BillboardSet::BillboardSet(Context* context) :
     sortFrameNumber_(0),
     sortFrameNumber_(0),
     previousOffset_(Vector3::ZERO)
     previousOffset_(Vector3::ZERO)
 {
 {
-    drawableFlags_ = DRAWABLE_GEOMETRY;
-    
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2);
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2);
     geometry_->SetIndexBuffer(indexBuffer_);
     geometry_->SetIndexBuffer(indexBuffer_);
     
     
@@ -176,29 +174,25 @@ void BillboardSet::SetNumBillboards(unsigned num)
     }
     }
     
     
     bufferSizeDirty_ = true;
     bufferSizeDirty_ = true;
-    MarkPositionsDirty();
-    MarkNetworkUpdate();
+    Updated();
 }
 }
 
 
 void BillboardSet::SetRelative(bool enable)
 void BillboardSet::SetRelative(bool enable)
 {
 {
     relative_ = enable;
     relative_ = enable;
-    MarkPositionsDirty();
-    MarkNetworkUpdate();
+    Updated();
 }
 }
 
 
 void BillboardSet::SetScaled(bool enable)
 void BillboardSet::SetScaled(bool enable)
 {
 {
     scaled_ = enable;
     scaled_ = enable;
-    MarkPositionsDirty();
-    MarkNetworkUpdate();
+    Updated();
 }
 }
 
 
 void BillboardSet::SetSorted(bool enable)
 void BillboardSet::SetSorted(bool enable)
 {
 {
     sorted_ = enable;
     sorted_ = enable;
-    MarkPositionsDirty();
-    MarkNetworkUpdate();
+    Updated();
 }
 }
 
 
 void BillboardSet::SetAnimationLodBias(float bias)
 void BillboardSet::SetAnimationLodBias(float bias)
@@ -276,6 +270,7 @@ ResourceRef BillboardSet::GetMaterialAttr() const
 VariantVector BillboardSet::GetBillboardsAttr() const
 VariantVector BillboardSet::GetBillboardsAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
+    ret.Reserve(billboards_.Size() * 6 + 1);
     ret.Push(billboards_.Size());
     ret.Push(billboards_.Size());
     
     
     for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)

+ 0 - 1
Engine/Graphics/BillboardSet.h

@@ -32,7 +32,6 @@ namespace Urho3D
 {
 {
 
 
 class IndexBuffer;
 class IndexBuffer;
-class Graphics;
 class VertexBuffer;
 class VertexBuffer;
 
 
 /// One billboard in the billboard set.
 /// One billboard in the billboard set.

+ 3 - 6
Engine/Graphics/Camera.cpp

@@ -428,20 +428,17 @@ float Camera::GetHalfViewSize() const
 
 
 Vector3 Camera::GetForwardVector()
 Vector3 Camera::GetForwardVector()
 {
 {
-    Matrix3 worldRotation = node_ ? node_->GetWorldTransform().RotationMatrix() : Matrix3::IDENTITY;
-    return worldRotation * Vector3::FORWARD;
+    return node_ ? node_->GetWorldDirection() : Vector3::FORWARD;
 }
 }
 
 
 Vector3 Camera::GetRightVector()
 Vector3 Camera::GetRightVector()
 {
 {
-    Matrix3 worldRotation = node_ ? node_->GetWorldTransform().RotationMatrix() : Matrix3::IDENTITY;
-    return worldRotation * Vector3::RIGHT;
+    return node_ ? node_->GetWorldTransform().RotationMatrix() * Vector3::RIGHT : Vector3::RIGHT;
 }
 }
 
 
 Vector3 Camera::GetUpVector()
 Vector3 Camera::GetUpVector()
 {
 {
-    Matrix3 worldRotation = node_ ? node_->GetWorldTransform().RotationMatrix() : Matrix3::IDENTITY;
-    return worldRotation * Vector3::UP;
+    return node_ ? node_->GetWorldTransform().RotationMatrix() * Vector3::UP : Vector3::UP;
 }
 }
 
 
 float Camera::GetDistance(const Vector3& worldPos) const
 float Camera::GetDistance(const Vector3& worldPos) const

+ 23 - 38
Engine/Graphics/CustomGeometry.cpp

@@ -43,13 +43,11 @@ namespace Urho3D
 OBJECTTYPESTATIC(CustomGeometry);
 OBJECTTYPESTATIC(CustomGeometry);
 
 
 CustomGeometry::CustomGeometry(Context* context) :
 CustomGeometry::CustomGeometry(Context* context) :
-    Drawable(context),
-    geometryIndex_(0),
-    elementMask_(MASK_POSITION)
+    Drawable(context, DRAWABLE_GEOMETRY),
+    vertexBuffer_(new VertexBuffer(context)),
+    elementMask_(MASK_POSITION),
+    geometryIndex_(0)
 {
 {
-    drawableFlags_ = DRAWABLE_GEOMETRY;
-    
-    vertexBuffer_ = new VertexBuffer(context_);
     vertexBuffer_->SetShadowed(true);
     vertexBuffer_->SetShadowed(true);
     SetNumGeometries(1);
     SetNumGeometries(1);
 }
 }
@@ -84,50 +82,37 @@ void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQ
         break;
         break;
         
         
     case RAY_OBB:
     case RAY_OBB:
-        {
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
-        }
-        break;
-        
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
+        Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
+        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        float distance = localRay.HitDistance(boundingBox_);
+        if (distance <= query.maxDistance_)
         {
         {
-            // Do a pretest using the OBB
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
+            if (level == RAY_TRIANGLE)
             {
             {
-                // Then the actual test using triangle geometry
-                for (unsigned i = 0; i < batches_.Size(); ++i)
+                // After a pretest using the OBB, do the actual test using triangle geometry
+                unsigned i = 0;
+                while (i < batches_.Size())
                 {
                 {
-                    Geometry* geometry = batches_[i].geometry_;
+                    Geometry* geometry = batches_[i++].geometry_;
                     if (geometry)
                     if (geometry)
                     {
                     {
                         distance = geometry->GetHitDistance(localRay);
                         distance = geometry->GetHitDistance(localRay);
                         if (distance <= query.maxDistance_)
                         if (distance <= query.maxDistance_)
-                        {
-                            RayQueryResult result;
-                            result.drawable_ = this;
-                            result.node_ = GetNode();
-                            result.distance_ = distance;
-                            result.subObject_ = M_MAX_UNSIGNED;
-                            results.Push(result);
                             break;
                             break;
-                        }
                     }
                     }
                 }
                 }
+                if (i == batches_.Size())
+                    break;
             }
             }
+            
+            // If the code reaches here then we have a hit
+            RayQueryResult result;
+            result.drawable_ = this;
+            result.node_ = node_;
+            result.distance_ = distance;
+            result.subObject_ = M_MAX_UNSIGNED;
+            results.Push(result);
         }
         }
         break;
         break;
     }
     }

+ 0 - 3
Engine/Graphics/CustomGeometry.h

@@ -100,9 +100,6 @@ protected:
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();
     
     
 private:
 private:
-    /// Recalculate the local-space bounding box.
-    void CalculateBoundingBox();
-    
     /// Local-space bounding box.
     /// Local-space bounding box.
     BoundingBox boundingBox_;
     BoundingBox boundingBox_;
     /// Primitive type per geometry.
     /// Primitive type per geometry.

+ 2 - 8
Engine/Graphics/DebugRenderer.cpp

@@ -39,10 +39,10 @@
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 
-// Cap the amount of lines to prevent crash when eg. debug rendering large heightfields
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+// Cap the amount of lines to prevent crash when eg. debug rendering large heightfields
 static const unsigned MAX_LINES = 1000000;
 static const unsigned MAX_LINES = 1000000;
 
 
 OBJECTTYPESTATIC(DebugRenderer);
 OBJECTTYPESTATIC(DebugRenderer);
@@ -76,13 +76,7 @@ void DebugRenderer::SetView(Camera* camera)
 
 
 void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, const Color& color, bool depthTest)
 void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, const Color& color, bool depthTest)
 {
 {
-    if (lines_.Size() + noDepthLines_.Size() >= MAX_LINES)
-        return;
-    
-    if (depthTest)
-        lines_.Push(DebugLine(start, end, color.ToUInt()));
-    else
-        noDepthLines_.Push(DebugLine(start, end, color.ToUInt()));
+    AddLine(start, end, color.ToUInt(), depthTest);
 }
 }
 
 
 void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, unsigned color, bool depthTest)
 void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, unsigned color, bool depthTest)

+ 36 - 28
Engine/Graphics/DecalSet.cpp

@@ -150,7 +150,7 @@ void Decal::CalculateBoundingBox()
 OBJECTTYPESTATIC(DecalSet);
 OBJECTTYPESTATIC(DecalSet);
 
 
 DecalSet::DecalSet(Context* context) :
 DecalSet::DecalSet(Context* context) :
-    Drawable(context),
+    Drawable(context, DRAWABLE_GEOMETRY),
     geometry_(new Geometry(context)),
     geometry_(new Geometry(context)),
     vertexBuffer_(new VertexBuffer(context_)),
     vertexBuffer_(new VertexBuffer(context_)),
     indexBuffer_(new IndexBuffer(context_)),
     indexBuffer_(new IndexBuffer(context_)),
@@ -163,10 +163,9 @@ DecalSet::DecalSet(Context* context) :
     bufferDirty_(true),
     bufferDirty_(true),
     boundingBoxDirty_(true),
     boundingBoxDirty_(true),
     skinningDirty_(false),
     skinningDirty_(false),
-    assignBonesPending_(false)
+    assignBonesPending_(false),
+    subscribed_(false)
 {
 {
-    drawableFlags_ = DRAWABLE_GEOMETRY;
-    
     geometry_->SetIndexBuffer(indexBuffer_);
     geometry_->SetIndexBuffer(indexBuffer_);
     
     
     batches_.Resize(1);
     batches_.Resize(1);
@@ -312,7 +311,7 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     {
     {
         Skeleton& skeleton = animatedModel->GetSkeleton();
         Skeleton& skeleton = animatedModel->GetSkeleton();
         unsigned numBones = skeleton.GetNumBones();
         unsigned numBones = skeleton.GetNumBones();
-        unsigned bestIndex = M_MAX_UNSIGNED;
+        Bone* bestBone = 0;
         float bestSize = 0.0f;
         float bestSize = 0.0f;
         
         
         for (unsigned i = 0; i < numBones; ++i)
         for (unsigned i = 0; i < numBones; ++i)
@@ -328,9 +327,9 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
             if (bone->collisionMask_ & BONECOLLISION_BOX)
             if (bone->collisionMask_ & BONECOLLISION_BOX)
             {
             {
                 float size = bone->boundingBox_.HalfSize().Length();
                 float size = bone->boundingBox_.HalfSize().Length();
-                if (bone->boundingBox_.IsInside(decalSphere) != OUTSIDE && size > bestSize)
+                if (bone->boundingBox_.IsInside(decalSphere) && size > bestSize)
                 {
                 {
-                    bestIndex = i;
+                    bestBone = bone;
                     bestSize = size;
                     bestSize = size;
                 }
                 }
             }
             }
@@ -338,19 +337,16 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
             {
             {
                 Sphere boneSphere(Vector3::ZERO, bone->radius_);
                 Sphere boneSphere(Vector3::ZERO, bone->radius_);
                 float size = bone->radius_;
                 float size = bone->radius_;
-                if (boneSphere.IsInside(decalSphere) != OUTSIDE && size > bestSize)
+                if (boneSphere.IsInside(decalSphere) && size > bestSize)
                 {
                 {
-                    bestIndex = i;
+                    bestBone = bone;
                     bestSize = size;
                     bestSize = size;
                 }
                 }
             }
             }
         }
         }
         
         
-        if (bestIndex < numBones)
-        {
-            Bone* bone = skeleton.GetBone(bestIndex);
-            targetTransform = (bone->node_->GetWorldTransform() * bone->offsetMatrix_).Inverse();
-        }
+        if (bestBone)
+            targetTransform = (bestBone->node_->GetWorldTransform() * bestBone->offsetMatrix_).Inverse();
     }
     }
     
     
     // Build the decal frustum
     // Build the decal frustum
@@ -451,8 +447,11 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     
     
     // Subscribe to scene post-update if defined a time-limited decal
     // Subscribe to scene post-update if defined a time-limited decal
     Scene* scene = GetScene();
     Scene* scene = GetScene();
-    if (newDecal.timeToLive_ > 0.0f && scene && !HasSubscribedToEvent(scene, E_SCENEPOSTUPDATE))
+    if (!subscribed_ && newDecal.timeToLive_ > 0.0f && scene)
+    {
         SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(DecalSet, HandleScenePostUpdate));
         SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(DecalSet, HandleScenePostUpdate));
+        subscribed_ = true;
+    }
     
     
     // Remove oldest decals if total vertices exceeded
     // Remove oldest decals if total vertices exceeded
     while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_))
     while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_))
@@ -466,11 +465,8 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
 
 
 void DecalSet::RemoveDecals(unsigned num)
 void DecalSet::RemoveDecals(unsigned num)
 {
 {
-    while (decals_.Size() && num)
-    {
+    while (num-- && decals_.Size())
         RemoveDecal(decals_.Begin());
         RemoveDecal(decals_.Begin());
-        --num;
-    }
 }
 }
 
 
 void DecalSet::RemoveAllDecals()
 void DecalSet::RemoveAllDecals()
@@ -515,6 +511,8 @@ void DecalSet::SetDecalsAttr(VariantVector value)
     
     
     skinned_ = value[index++].GetBool();
     skinned_ = value[index++].GetBool();
     unsigned numDecals = value[index++].GetInt();
     unsigned numDecals = value[index++].GetInt();
+
+    bool hasTimeLimitedDecal = false;
     
     
     while (numDecals--)
     while (numDecals--)
     {
     {
@@ -549,20 +547,26 @@ void DecalSet::SetDecalsAttr(VariantVector value)
         numVertices_ += newDecal.vertices_.Size();
         numVertices_ += newDecal.vertices_.Size();
         numIndices_ += newDecal.indices_.Size();
         numIndices_ += newDecal.indices_.Size();
         
         
-        // Subscribe to scene post-update if defined a time-limited decal
-        if (newDecal.timeToLive_ > 0.0f && scene && !HasSubscribedToEvent(scene, E_SCENEPOSTUPDATE))
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(DecalSet, HandleScenePostUpdate));
+        if (!hasTimeLimitedDecal && newDecal.timeToLive_ > 0.0f)
+            hasTimeLimitedDecal = true;
+    }
+
+    // Subscribe to scene post-update if defined a time-limited decal
+    if (!subscribed_ && hasTimeLimitedDecal && scene)
+    {
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(DecalSet, HandleScenePostUpdate));
+        subscribed_ = true;
     }
     }
     
     
     if (skinned_)
     if (skinned_)
     {
     {
         unsigned numBones = value[index++].GetInt();
         unsigned numBones = value[index++].GetInt();
         skinMatrices_.Resize(numBones);
         skinMatrices_.Resize(numBones);
+        bones_.Resize(numBones);
         
         
-        while (numBones--)
+        for (unsigned i = 0; i < numBones; ++i)
         {
         {
-            bones_.Resize(bones_.Size() + 1);
-            Bone& newBone = bones_.Back();
+            Bone& newBone = bones_[i];
             
             
             newBone.name_ = value[index++].GetString();
             newBone.name_ = value[index++].GetString();
             MemoryBuffer boneData(value[index++].GetBuffer());
             MemoryBuffer boneData(value[index++].GetBuffer());
@@ -721,18 +725,20 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target
         
         
         unsigned elementMask = geometry->GetVertexElementMask(i);
         unsigned elementMask = geometry->GetVertexElementMask(i);
         unsigned char* data = vb->GetShadowData();
         unsigned char* data = vb->GetShadowData();
+        if (!data)
+            continue;
         
         
-        if ((elementMask & MASK_POSITION) && data)
+        if (elementMask & MASK_POSITION)
         {
         {
             positionData = data;
             positionData = data;
             positionStride = vb->GetVertexSize();
             positionStride = vb->GetVertexSize();
         }
         }
-        if ((elementMask & MASK_NORMAL) && data)
+        if (elementMask & MASK_NORMAL)
         {
         {
             normalData = data + vb->GetElementOffset(ELEMENT_NORMAL);
             normalData = data + vb->GetElementOffset(ELEMENT_NORMAL);
             normalStride = vb->GetVertexSize();
             normalStride = vb->GetVertexSize();
         }
         }
-        if ((elementMask & MASK_BLENDWEIGHTS) && data)
+        if (elementMask & MASK_BLENDWEIGHTS)
         {
         {
             skinningData = data + vb->GetElementOffset(ELEMENT_BLENDWEIGHTS);
             skinningData = data + vb->GetElementOffset(ELEMENT_BLENDWEIGHTS);
             skinningStride = vb->GetVertexSize();
             skinningStride = vb->GetVertexSize();
@@ -844,6 +850,7 @@ void DecalSet::GetFace(Vector<PODVector<DecalVertex> >& faces, Drawable* target,
     PODVector<DecalVertex>& face = faces.Back();
     PODVector<DecalVertex>& face = faces.Back();
     if (!hasSkinning)
     if (!hasSkinning)
     {
     {
+        face.Reserve(3);
         face.Push(DecalVertex(v0, n0));
         face.Push(DecalVertex(v0, n0));
         face.Push(DecalVertex(v1, n1));
         face.Push(DecalVertex(v1, n1));
         face.Push(DecalVertex(v2, n2));
         face.Push(DecalVertex(v2, n2));
@@ -865,6 +872,7 @@ void DecalSet::GetFace(Vector<PODVector<DecalVertex> >& faces, Drawable* target,
             !GetBones(target, batchIndex, bw2, bi2, nbi2))
             !GetBones(target, batchIndex, bw2, bi2, nbi2))
             return;
             return;
         
         
+        face.Reserve(3);
         face.Push(DecalVertex(v0, n0, bw0, nbi0));
         face.Push(DecalVertex(v0, n0, bw0, nbi0));
         face.Push(DecalVertex(v1, n1, bw1, nbi1));
         face.Push(DecalVertex(v1, n1, bw1, nbi1));
         face.Push(DecalVertex(v2, n2, bw2, nbi2));
         face.Push(DecalVertex(v2, n2, bw2, nbi2));

+ 2 - 0
Engine/Graphics/DecalSet.h

@@ -228,6 +228,8 @@ private:
     bool skinningDirty_;
     bool skinningDirty_;
     /// Bone nodes assignment pending flag.
     /// Bone nodes assignment pending flag.
     bool assignBonesPending_;
     bool assignBonesPending_;
+    /// Subscribed to scene post update event flag.
+    bool subscribed_;
 };
 };
 
 
 }
 }

+ 1 - 1
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -1818,7 +1818,7 @@ void Graphics::AddGPUObject(GPUObject* object)
 
 
 void Graphics::RemoveGPUObject(GPUObject* object)
 void Graphics::RemoveGPUObject(GPUObject* object)
 {
 {
-    gpuObjects_.Erase(gpuObjects_.Find(object));
+    gpuObjects_.Remove(object);
 }
 }
 
 
 void* Graphics::ReserveScratchBuffer(unsigned size)
 void* Graphics::ReserveScratchBuffer(unsigned size)

+ 4 - 4
Engine/Graphics/Drawable.cpp

@@ -53,9 +53,9 @@ SourceBatch::~SourceBatch()
 
 
 OBJECTTYPESTATIC(Drawable);
 OBJECTTYPESTATIC(Drawable);
 
 
-Drawable::Drawable(Context* context) :
+Drawable::Drawable(Context* context, unsigned char drawableFlags) :
     Component(context),
     Component(context),
-    drawableFlags_(0),
+    drawableFlags_(drawableFlags),
     worldBoundingBoxDirty_(true),
     worldBoundingBoxDirty_(true),
     visible_(true),
     visible_(true),
     castShadows_(false),
     castShadows_(false),
@@ -229,7 +229,7 @@ void Drawable::SetOccludee(bool enable)
 
 
 void Drawable::MarkForUpdate()
 void Drawable::MarkForUpdate()
 {
 {
-    if (octant_ && !updateQueued_)
+    if (!updateQueued_ && octant_)
         octant_->GetRoot()->QueueUpdate(this);
         octant_->GetRoot()->QueueUpdate(this);
 }
 }
 
 
@@ -361,7 +361,7 @@ void Drawable::OnNodeSet(Node* node)
 void Drawable::OnMarkedDirty(Node* node)
 void Drawable::OnMarkedDirty(Node* node)
 {
 {
     worldBoundingBoxDirty_ = true;
     worldBoundingBoxDirty_ = true;
-    if (octant_ && !reinsertionQueued_)
+    if (!reinsertionQueued_ && octant_)
         octant_->GetRoot()->QueueReinsertion(this);
         octant_->GetRoot()->QueueReinsertion(this);
     
     
     if (node == node_)
     if (node == node_)

+ 3 - 3
Engine/Graphics/Drawable.h

@@ -90,9 +90,9 @@ struct SourceBatch
     SharedPtr<Material> material_;
     SharedPtr<Material> material_;
     /// %Object's world transform.
     /// %Object's world transform.
     const Matrix3x4* worldTransform_;
     const Matrix3x4* worldTransform_;
-    /// Vertex shader data.
+    /// Vertex shader data in floats.
     const float* shaderData_;
     const float* shaderData_;
-    /// Vertex shader data size in floats.
+    /// Vertex shader data size.
     unsigned shaderDataSize_;
     unsigned shaderDataSize_;
     /// %Geometry type.
     /// %Geometry type.
     GeometryType geometryType_;
     GeometryType geometryType_;
@@ -111,7 +111,7 @@ class Drawable : public Component
     
     
 public:
 public:
     /// Construct.
     /// Construct.
-    Drawable(Context* context);
+    Drawable(Context* context, unsigned char drawableFlags);
     /// Destruct.
     /// Destruct.
     virtual ~Drawable();
     virtual ~Drawable();
     /// Register object attributes. Drawable must be registered first.
     /// Register object attributes. Drawable must be registered first.

+ 64 - 60
Engine/Graphics/Light.cpp

@@ -84,7 +84,7 @@ template<> LightType Variant::Get<LightType>() const
 OBJECTTYPESTATIC(Light);
 OBJECTTYPESTATIC(Light);
 
 
 Light::Light(Context* context) :
 Light::Light(Context* context) :
-    Drawable(context),
+    Drawable(context, DRAWABLE_LIGHT),
     lightType_(DEFAULT_LIGHTTYPE),
     lightType_(DEFAULT_LIGHTTYPE),
     shadowBias_(BiasParameters(DEFAULT_CONSTANTBIAS, DEFAULT_SLOPESCALEDBIAS)),
     shadowBias_(BiasParameters(DEFAULT_CONSTANTBIAS, DEFAULT_SLOPESCALEDBIAS)),
     shadowCascade_(CascadeParameters(M_LARGE_VALUE, 0.0f, 0.0f, 0.0f, DEFAULT_SHADOWFADESTART)),
     shadowCascade_(CascadeParameters(M_LARGE_VALUE, 0.0f, 0.0f, 0.0f, DEFAULT_SHADOWFADESTART)),
@@ -101,7 +101,6 @@ Light::Light(Context* context) :
     shadowNearFarRatio_(DEFAULT_SHADOWNEARFARRATIO),
     shadowNearFarRatio_(DEFAULT_SHADOWNEARFARRATIO),
     perVertex_(false)
     perVertex_(false)
 {
 {
-    drawableFlags_ =  DRAWABLE_LIGHT;
 }
 }
 
 
 Light::~Light()
 Light::~Light()
@@ -133,7 +132,11 @@ void Light::RegisterObject(Context* context)
     ATTRIBUTE(Light, VAR_BOOL, "Focus To Scene", shadowFocus_.focus_, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Focus To Scene", shadowFocus_.focus_, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Non-uniform View", shadowFocus_.nonUniform_, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Non-uniform View", shadowFocus_.nonUniform_, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Auto-Reduce Size", shadowFocus_.autoSize_, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Auto-Reduce Size", shadowFocus_.autoSize_, true, AM_DEFAULT);
+    #if !defined(ANDROID) && !defined(IOS)
     ATTRIBUTE(Light, VAR_VECTOR4, "CSM Splits", shadowCascade_.splits_, Vector4(M_LARGE_VALUE, 0.0f, 0.0f, 0.0f), AM_DEFAULT);
     ATTRIBUTE(Light, VAR_VECTOR4, "CSM Splits", shadowCascade_.splits_, Vector4(M_LARGE_VALUE, 0.0f, 0.0f, 0.0f), AM_DEFAULT);
+    #else
+    ATTRIBUTE(Light, VAR_VECTOR2, "CSM Splits", shadowCascade_.splits_, Vector2(M_LARGE_VALUE, 0.0f), AM_DEFAULT);
+    #endif
     ATTRIBUTE(Light, VAR_FLOAT, "CSM Fade Start", shadowCascade_.fadeStart_, DEFAULT_SHADOWFADESTART, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_FLOAT, "CSM Fade Start", shadowCascade_.fadeStart_, DEFAULT_SHADOWFADESTART, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_FLOAT, "View Size Quantize", shadowFocus_.quantize_, DEFAULT_SHADOWQUANTIZE, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_FLOAT, "View Size Quantize", shadowFocus_.quantize_, DEFAULT_SHADOWQUANTIZE, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_FLOAT, "View Size Minimum", shadowFocus_.minView_, DEFAULT_SHADOWMINVIEW, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_FLOAT, "View Size Minimum", shadowFocus_.minView_, DEFAULT_SHADOWMINVIEW, AM_DEFAULT);
@@ -159,64 +162,51 @@ void Light::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 
 
 void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 {
 {
-    RayQueryLevel level = query.level_;
+    // Do not record a raycast result for a directional light, as they would overwhelm all other results
+    if (lightType_ == LIGHT_DIRECTIONAL)
+        return;
     
     
-    switch (level)
+    float distance;
+    switch (query.level_)
     {
     {
     case RAY_AABB_NOSUBOBJECTS:
     case RAY_AABB_NOSUBOBJECTS:
     case RAY_AABB:
     case RAY_AABB:
-        // Do not record a raycast result for a directional light, as they would overwhelm all other results
-        if (lightType_ != LIGHT_DIRECTIONAL)
-            Drawable::ProcessRayQuery(query, results);
-        break;
+        Drawable::ProcessRayQuery(query, results);
+        return;
         
         
     case RAY_OBB:
     case RAY_OBB:
-        if (lightType_ != LIGHT_DIRECTIONAL)
         {
         {
             Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
             Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
             Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
             Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(GetWorldBoundingBox());
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
+            distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse));
+            if (distance > query.maxDistance_)
+                return;
         }
         }
         break;
         break;
         
         
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
         if (lightType_ == LIGHT_SPOT)
         if (lightType_ == LIGHT_SPOT)
         {
         {
-            float distance = query.ray_.HitDistance(GetFrustum());
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
+            distance = query.ray_.HitDistance(GetFrustum());
+            if (distance > query.maxDistance_)
+                return;
         }
         }
-        if (lightType_ == LIGHT_POINT)
+        else // if (lightType_ == LIGHT_POINT)
         {
         {
-            float distance = query.ray_.HitDistance(Sphere(node_->GetWorldPosition(), range_));
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
+            distance = query.ray_.HitDistance(Sphere(node_->GetWorldPosition(), range_));
+            if (distance > query.maxDistance_)
+                return;
         }
         }
         break;
         break;
     }
     }
+
+    // If the code reaches here then we have a hit
+    RayQueryResult result;
+    result.drawable_ = this;
+    result.node_ = node_;
+    result.distance_ = distance;
+    result.subObject_ = M_MAX_UNSIGNED;
+    results.Push(result);
 }
 }
 
 
 void Light::UpdateBatches(const FrameInfo& frame)
 void Light::UpdateBatches(const FrameInfo& frame)
@@ -240,12 +230,26 @@ void Light::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     {
     {
         switch (lightType_)
         switch (lightType_)
         {
         {
+        case LIGHT_DIRECTIONAL:
+            {
+                Vector3 start = node_->GetWorldPosition();
+                Vector3 end = start + node_->GetWorldDirection() * 10.f;
+                for (short i = -1; i < 2; ++i)
+                    for (short j = -1; j < 2; ++j)
+                    {
+                        Vector3 offset = Vector3::UP * (5.f * i) + Vector3::RIGHT * (5.f * j);
+                        debug->AddSphere(Sphere(start + offset, 0.1f), color_, depthTest);
+                        debug->AddLine(start + offset, end + offset, color_, depthTest);
+                    }
+            }
+            break;
+            
         case LIGHT_SPOT:
         case LIGHT_SPOT:
             debug->AddFrustum(GetFrustum(), color_, depthTest);
             debug->AddFrustum(GetFrustum(), color_, depthTest);
             break;
             break;
             
             
         case LIGHT_POINT:
         case LIGHT_POINT:
-            debug->AddSphere(Sphere(node_->GetWorldPosition(), range_), GetColor(), depthTest);
+            debug->AddSphere(Sphere(node_->GetWorldPosition(), range_), color_, depthTest);
             break;
             break;
         }
         }
     }
     }
@@ -450,6 +454,11 @@ void Light::OnWorldBoundingBoxUpdate()
         // Directional light always sets humongous bounding box not affected by transform
         // Directional light always sets humongous bounding box not affected by transform
         worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
         worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
         break;
         break;
+            
+    case LIGHT_SPOT:
+        // Frustum is already transformed into world space
+        worldBoundingBox_.Define(GetFrustum());
+        break;
         
         
     case LIGHT_POINT:
     case LIGHT_POINT:
         {
         {
@@ -458,11 +467,6 @@ void Light::OnWorldBoundingBoxUpdate()
             worldBoundingBox_.Define(center - edge, center + edge);
             worldBoundingBox_.Define(center - edge, center + edge);
         }
         }
         break;
         break;
-        
-    case LIGHT_SPOT:
-        // Frustum is already transformed into world space
-        worldBoundingBox_.Define(GetFrustum());
-        break;
     }
     }
 }
 }
 
 
@@ -488,24 +492,11 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
         sortValue_ = 1.0f / (color_.Intensity() + M_EPSILON);
         sortValue_ = 1.0f / (color_.Intensity() + M_EPSILON);
         break;
         break;
         
         
-    case LIGHT_POINT:
-        {
-            Vector3 centerPos = box.Center();
-            Vector3 lightPos = node_->GetWorldPosition();
-            Vector3 lightDir = (centerPos - lightPos).Normalized();
-            Ray lightRay(lightPos, lightDir);
-            float distance = lightRay.HitDistance(box);
-            float normDistance = distance / range_;
-            float att = Max(1.0f - normDistance * normDistance, M_EPSILON);
-            sortValue_ = 1.0f / (color_.Intensity() * att + M_EPSILON);
-        }
-        break;
-        
     case LIGHT_SPOT:
     case LIGHT_SPOT:
         {
         {
             Vector3 centerPos = box.Center();
             Vector3 centerPos = box.Center();
             Vector3 lightPos = node_->GetWorldPosition();
             Vector3 lightPos = node_->GetWorldPosition();
-            Vector3 lightDir = node_->GetWorldRotation() * Vector3::FORWARD;
+            Vector3 lightDir = node_->GetWorldDirection();
             Ray lightRay(lightPos, lightDir);
             Ray lightRay(lightPos, lightDir);
             
             
             Vector3 centerProj = lightRay.Project(centerPos);
             Vector3 centerProj = lightRay.Project(centerPos);
@@ -528,6 +519,19 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
             sortValue_ = 1.0f / (color_.Intensity() * att + M_EPSILON);
             sortValue_ = 1.0f / (color_.Intensity() * att + M_EPSILON);
         }
         }
         break;
         break;
+            
+    case LIGHT_POINT:
+        {
+            Vector3 centerPos = box.Center();
+            Vector3 lightPos = node_->GetWorldPosition();
+            Vector3 lightDir = (centerPos - lightPos).Normalized();
+            Ray lightRay(lightPos, lightDir);
+            float distance = lightRay.HitDistance(box);
+            float normDistance = distance / range_;
+            float att = Max(1.0f - normDistance * normDistance, M_EPSILON);
+            sortValue_ = 1.0f / (color_.Intensity() * att + M_EPSILON);
+        }
+        break;
     }
     }
 }
 }
 
 

+ 14 - 20
Engine/Graphics/Material.cpp

@@ -64,11 +64,11 @@ TextureUnit ParseTextureUnitName(const String& name)
     TextureUnit unit = (TextureUnit)GetStringListIndex(name, textureUnitNames, MAX_MATERIAL_TEXTURE_UNITS);
     TextureUnit unit = (TextureUnit)GetStringListIndex(name, textureUnitNames, MAX_MATERIAL_TEXTURE_UNITS);
     if (name == "diff")
     if (name == "diff")
         unit = TU_DIFFUSE;
         unit = TU_DIFFUSE;
-    if (name == "norm")
+    else if (name == "norm")
         unit = TU_NORMAL;
         unit = TU_NORMAL;
-    if (name == "spec")
+    else if (name == "spec")
         unit = TU_SPECULAR;
         unit = TU_SPECULAR;
-    if (name == "env")
+    else if (name == "env")
         unit = TU_ENVIRONMENT;
         unit = TU_ENVIRONMENT;
     
     
     return unit;
     return unit;
@@ -101,7 +101,8 @@ Material::Material(Context* context) :
     shadowCullMode_(CULL_CCW),
     shadowCullMode_(CULL_CCW),
     depthBias_(BiasParameters(0.0f, 0.0f)),
     depthBias_(BiasParameters(0.0f, 0.0f)),
     auxViewFrameNumber_(0),
     auxViewFrameNumber_(0),
-    occlusion_(true)
+    occlusion_(true),
+    specular_(false)
 {
 {
     SetNumTechniques(1);
     SetNumTechniques(1);
     
     
@@ -112,8 +113,6 @@ Material::Material(Context* context) :
     SetShaderParameter("MatEmissiveColor", Vector4::ZERO);
     SetShaderParameter("MatEmissiveColor", Vector4::ZERO);
     SetShaderParameter("MatEnvMapColor", Vector4::ONE);
     SetShaderParameter("MatEnvMapColor", Vector4::ONE);
     SetShaderParameter("MatSpecColor", Vector4(0.0f, 0.0f, 0.0f, 1.0f));
     SetShaderParameter("MatSpecColor", Vector4(0.0f, 0.0f, 0.0f, 1.0f));
-    
-    CheckSpecular();
 }
 }
 
 
 Material::~Material()
 Material::~Material()
@@ -218,7 +217,6 @@ bool Material::Load(Deserializer& source)
     
     
     SetMemoryUse(memoryUse);
     SetMemoryUse(memoryUse);
     CheckOcclusion();
     CheckOcclusion();
-    CheckSpecular();
     return true;
     return true;
 }
 }
 
 
@@ -297,9 +295,11 @@ void Material::SetShaderParameter(const String& name, const Vector4& value)
     MaterialShaderParameter newParam;
     MaterialShaderParameter newParam;
     newParam.name_ = name;
     newParam.name_ = name;
     newParam.value_ = value;
     newParam.value_ = value;
-    shaderParameters_[StringHash(name)] = newParam;
+    StringHash stringHash(name);
+    shaderParameters_[stringHash] = newParam;
     
     
-    CheckSpecular();
+    if (stringHash == PSP_MATSPECCOLOR)
+        specular_ = value.x_ > 0.0f || value.y_ > 0.0f || value.z_ > 0.0f;
 }
 }
 
 
 void Material::SetTexture(TextureUnit unit, Texture* texture)
 void Material::SetTexture(TextureUnit unit, Texture* texture)
@@ -360,8 +360,11 @@ void Material::SetDepthBias(const BiasParameters& parameters)
 
 
 void Material::RemoveShaderParameter(const String& name)
 void Material::RemoveShaderParameter(const String& name)
 {
 {
-    shaderParameters_.Erase(StringHash(name));
-    CheckSpecular();
+    StringHash stringHash(name);
+    shaderParameters_.Erase(stringHash);
+
+    if (stringHash == PSP_MATSPECCOLOR)
+        specular_ = false;
 }
 }
 
 
 void Material::ReleaseShaders()
 void Material::ReleaseShaders()
@@ -443,13 +446,4 @@ void Material::CheckOcclusion()
     }
     }
 }
 }
 
 
-void Material::CheckSpecular()
-{
-    HashMap<StringHash, MaterialShaderParameter>::ConstIterator i = shaderParameters_.Find(PSP_MATSPECCOLOR);
-    if (i != shaderParameters_.End())
-        specular_ = i->second_.value_.x_ > 0.0f || i->second_.value_.y_ > 0.0f || i->second_.value_.z_ > 0.0f;
-    else
-        specular_ = false;
-}
-
 }
 }

+ 0 - 2
Engine/Graphics/Material.h

@@ -146,8 +146,6 @@ public:
 private:
 private:
     /// Re-evaluate occlusion rendering.
     /// Re-evaluate occlusion rendering.
     void CheckOcclusion();
     void CheckOcclusion();
-    /// Re-evaluate specular lighting.
-    void CheckSpecular();
     
     
     /// Techniques.
     /// Techniques.
     Vector<TechniqueEntry> techniques_;
     Vector<TechniqueEntry> techniques_;

+ 9 - 3
Engine/Graphics/Model.cpp

@@ -98,6 +98,7 @@ bool Model::Load(Deserializer& source)
     
     
     // Read vertex buffers
     // Read vertex buffers
     unsigned numVertexBuffers = source.ReadUInt();
     unsigned numVertexBuffers = source.ReadUInt();
+    vertexBuffers_.Reserve(numVertexBuffers);
     morphRangeStarts_.Resize(numVertexBuffers);
     morphRangeStarts_.Resize(numVertexBuffers);
     morphRangeCounts_.Resize(numVertexBuffers);
     morphRangeCounts_.Resize(numVertexBuffers);
     for (unsigned i = 0; i < numVertexBuffers; ++i)
     for (unsigned i = 0; i < numVertexBuffers; ++i)
@@ -122,6 +123,7 @@ bool Model::Load(Deserializer& source)
 
 
     // Read index buffers
     // Read index buffers
     unsigned numIndexBuffers = source.ReadUInt();
     unsigned numIndexBuffers = source.ReadUInt();
+    indexBuffers_.Reserve(numIndexBuffers);
     for (unsigned i = 0; i < numIndexBuffers; ++i)
     for (unsigned i = 0; i < numIndexBuffers; ++i)
     {
     {
         unsigned indexCount = source.ReadUInt();
         unsigned indexCount = source.ReadUInt();
@@ -141,17 +143,21 @@ bool Model::Load(Deserializer& source)
     
     
     // Read geometries
     // Read geometries
     unsigned numGeometries = source.ReadUInt();
     unsigned numGeometries = source.ReadUInt();
+    geometries_.Reserve(numGeometries);
+    geometryBoneMappings_.Reserve(numGeometries);
+    geometryCenters_.Reserve(numGeometries);
     for (unsigned i = 0; i < numGeometries; ++i)
     for (unsigned i = 0; i < numGeometries; ++i)
     {
     {
         // Read bone mappings
         // Read bone mappings
         unsigned boneMappingCount = source.ReadUInt();
         unsigned boneMappingCount = source.ReadUInt();
-        PODVector<unsigned> boneMapping;
+        PODVector<unsigned> boneMapping(boneMappingCount);
         for (unsigned j = 0; j < boneMappingCount; ++j)
         for (unsigned j = 0; j < boneMappingCount; ++j)
-            boneMapping.Push(source.ReadUInt());
+            boneMapping[j] = source.ReadUInt();
         geometryBoneMappings_.Push(boneMapping);
         geometryBoneMappings_.Push(boneMapping);
         
         
         unsigned numLodLevels = source.ReadUInt();
         unsigned numLodLevels = source.ReadUInt();
         Vector<SharedPtr<Geometry> > geometryLodLevels;
         Vector<SharedPtr<Geometry> > geometryLodLevels;
+        geometryLodLevels.Reserve(numLodLevels);
         
         
         for (unsigned j = 0; j < numLodLevels; ++j)
         for (unsigned j = 0; j < numLodLevels; ++j)
         {
         {
@@ -175,7 +181,6 @@ bool Model::Load(Deserializer& source)
             }
             }
             
             
             SharedPtr<Geometry> geometry(new Geometry(context_));
             SharedPtr<Geometry> geometry(new Geometry(context_));
-            geometry->SetNumVertexBuffers(1);
             geometry->SetVertexBuffer(0, vertexBuffers_[vertexBufferRef]);
             geometry->SetVertexBuffer(0, vertexBuffers_[vertexBufferRef]);
             geometry->SetIndexBuffer(indexBuffers_[indexBufferRef]);
             geometry->SetIndexBuffer(indexBuffers_[indexBufferRef]);
             geometry->SetDrawRange(type, indexStart, indexCount);
             geometry->SetDrawRange(type, indexStart, indexCount);
@@ -190,6 +195,7 @@ bool Model::Load(Deserializer& source)
     
     
     // Read morphs
     // Read morphs
     unsigned numMorphs = source.ReadUInt();
     unsigned numMorphs = source.ReadUInt();
+    morphs_.Reserve(numMorphs);
     for (unsigned i = 0; i < numMorphs; ++i)
     for (unsigned i = 0; i < numMorphs; ++i)
     {
     {
         ModelMorph newMorph;
         ModelMorph newMorph;

+ 1 - 4
Engine/Graphics/OcclusionBuffer.cpp

@@ -143,11 +143,8 @@ void OcclusionBuffer::Clear()
     int* dest = buffer_;
     int* dest = buffer_;
     int count = width_ * height_;
     int count = width_ * height_;
     
     
-    while (count)
-    {
+    while (count--)
         *dest++ = 0x7fffffff;
         *dest++ = 0x7fffffff;
-        --count;
-    }
     
     
     depthHierarchyDirty_ = true;
     depthHierarchyDirty_ = true;
 }
 }

+ 17 - 28
Engine/Graphics/Octree.cpp

@@ -83,12 +83,13 @@ inline bool CompareRayQueryResults(const RayQueryResult& lhs, const RayQueryResu
     return lhs.distance_ < rhs.distance_;
     return lhs.distance_ < rhs.distance_;
 }
 }
 
 
-Octant::Octant(const BoundingBox& box, unsigned level, Octant* parent, Octree* root) :
+Octant::Octant(const BoundingBox& box, unsigned level, Octant* parent, Octree* root, unsigned index) :
     worldBoundingBox_(box),
     worldBoundingBox_(box),
     level_(level),
     level_(level),
     numDrawables_(0),
     numDrawables_(0),
     parent_(parent),
     parent_(parent),
-    root_(root)
+    root_(root),
+    index_(index)
 {
 {
     center_ = worldBoundingBox_.Center();
     center_ = worldBoundingBox_.Center();
     halfSize_ = worldBoundingBox_.Size() * 0.5f;
     halfSize_ = worldBoundingBox_.Size() * 0.5f;
@@ -127,7 +128,7 @@ Octant* Octant::GetOrCreateChild(unsigned index)
     else
     else
         newMax.z_ = oldCenter.z_;
         newMax.z_ = oldCenter.z_;
     
     
-    children_[index] = new Octant(BoundingBox(newMin, newMax), level_ + 1, this, root_);
+    children_[index] = new Octant(BoundingBox(newMin, newMax), level_ + 1, this, root_, index);
     return children_[index];
     return children_[index];
 }
 }
 
 
@@ -139,15 +140,8 @@ void Octant::DeleteChild(unsigned index)
 
 
 void Octant::DeleteChild(Octant* octant)
 void Octant::DeleteChild(Octant* octant)
 {
 {
-    for (unsigned i = 0; i < NUM_OCTANTS; ++i)
-    {
-        if (children_[i] == octant)
-        {
-            delete octant;
-            children_[i] = 0;
-            return;
-        }
-    }
+    assert(octant && octant->index_ < NUM_OCTANTS);
+    DeleteChild(octant->index_);
 }
 }
 
 
 void Octant::InsertDrawable(Drawable* drawable, const Vector3& boxCenter, const Vector3& boxSize)
 void Octant::InsertDrawable(Drawable* drawable, const Vector3& boxCenter, const Vector3& boxSize)
@@ -187,6 +181,10 @@ void Octant::ResetRoot()
 {
 {
     root_ = 0;
     root_ = 0;
     
     
+    // The whole octree is being destroyed, just detach the drawables
+    for (PODVector<Drawable*>::Iterator i = drawables_.Begin(); i != drawables_.End(); ++i)
+        (*i)->SetOctant(0);
+
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     {
     {
         if (children_[i])
         if (children_[i])
@@ -249,13 +247,11 @@ void Octant::GetDrawablesInternal(RayOctreeQuery& query) const
        
        
         while (start != end)
         while (start != end)
         {
         {
-            Drawable* drawable = *start;
+            Drawable* drawable = *start++;
             
             
-            if ((drawable->GetDrawableFlags() & query.drawableFlags_) && drawable->IsVisible() &&
+            if (drawable->IsVisible() && (drawable->GetDrawableFlags() & query.drawableFlags_) &&
                 (drawable->GetViewMask() & query.viewMask_))
                 (drawable->GetViewMask() & query.viewMask_))
                 drawable->ProcessRayQuery(query, query.result_);
                 drawable->ProcessRayQuery(query, query.result_);
-            
-            ++start;
         }
         }
     }
     }
     
     
@@ -279,13 +275,11 @@ void Octant::GetDrawablesOnlyInternal(RayOctreeQuery& query, PODVector<Drawable*
         
         
         while (start != end)
         while (start != end)
         {
         {
-            Drawable* drawable = *start;
+            Drawable* drawable = *start++;
             
             
-            if ((drawable->GetDrawableFlags() & query.drawableFlags_) && drawable->IsVisible() &&
+            if (drawable->IsVisible() && (drawable->GetDrawableFlags() & query.drawableFlags_) &&
                 (drawable->GetViewMask() & query.viewMask_))
                 (drawable->GetViewMask() & query.viewMask_))
                 drawables.Push(drawable);
                 drawables.Push(drawable);
-            
-            ++start;
         }
         }
     }
     }
     
     
@@ -298,7 +292,7 @@ void Octant::GetDrawablesOnlyInternal(RayOctreeQuery& query, PODVector<Drawable*
 
 
 void Octant::Release()
 void Octant::Release()
 {
 {
-    if (root_ && this != root_)
+    if (root_)
     {
     {
         // Remove the drawables (if any) from this octant to the root octant
         // Remove the drawables (if any) from this octant to the root octant
         for (PODVector<Drawable*>::Iterator i = drawables_.Begin(); i != drawables_.End(); ++i)
         for (PODVector<Drawable*>::Iterator i = drawables_.Begin(); i != drawables_.End(); ++i)
@@ -310,12 +304,6 @@ void Octant::Release()
         drawables_.Clear();
         drawables_.Clear();
         numDrawables_ = 0;
         numDrawables_ = 0;
     }
     }
-    else if (!root_)
-    {
-        // If the whole octree is being destroyed, just detach the drawables
-        for (PODVector<Drawable*>::Iterator i = drawables_.Begin(); i != drawables_.End(); ++i)
-            (*i)->SetOctant(0);
-    }
     
     
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
     for (unsigned i = 0; i < NUM_OCTANTS; ++i)
         DeleteChild(i);
         DeleteChild(i);
@@ -374,7 +362,8 @@ void Octree::Resize(const BoundingBox& box, unsigned numLevels)
     numLevels = Max((int)numLevels, 1);
     numLevels = Max((int)numLevels, 1);
     
     
     // If drawables exist, they are temporarily moved to the root
     // If drawables exist, they are temporarily moved to the root
-    Release();
+    for (unsigned i = 0; i < NUM_OCTANTS; ++i)
+        DeleteChild(i);
     
     
     Vector3 halfSize = box.Size() * 0.5f;
     Vector3 halfSize = box.Size() * 0.5f;
     
     

+ 5 - 4
Engine/Graphics/Octree.h

@@ -34,13 +34,14 @@ namespace Urho3D
 class Octree;
 class Octree;
 
 
 static const int NUM_OCTANTS = 8;
 static const int NUM_OCTANTS = 8;
+static const unsigned ROOT_INDEX = M_MAX_UNSIGNED;
 
 
 /// %Octree octant
 /// %Octree octant
 class Octant
 class Octant
 {
 {
 public:
 public:
     /// Construct.
     /// Construct.
-    Octant(const BoundingBox& box, unsigned level, Octant* parent, Octree* root);
+    Octant(const BoundingBox& box, unsigned level, Octant* parent, Octree* root, unsigned index = ROOT_INDEX);
     /// Destruct. Move drawables to root if available (detach if not) and free child octants.
     /// Destruct. Move drawables to root if available (detach if not) and free child octants.
     virtual ~Octant();
     virtual ~Octant();
     
     
@@ -66,12 +67,10 @@ public:
     /// Remove a drawable object from this octant.
     /// Remove a drawable object from this octant.
     void RemoveDrawable(Drawable* drawable, bool resetOctant = true)
     void RemoveDrawable(Drawable* drawable, bool resetOctant = true)
     {
     {
-        PODVector<Drawable*>::Iterator i = drawables_.Find(drawable);
-        if (i != drawables_.End())
+        if (drawables_.Remove(drawable))
         {
         {
             if (resetOctant)
             if (resetOctant)
                 drawable->SetOctant(0);
                 drawable->SetOctant(0);
-            drawables_.Erase(i);
             DecDrawableCount();
             DecDrawableCount();
         }
         }
     }
     }
@@ -150,6 +149,8 @@ protected:
     Octant* parent_;
     Octant* parent_;
     /// Octree root.
     /// Octree root.
     Octree* root_;
     Octree* root_;
+    /// Octant index relative to its siblings or ROOT_INDEX for root octant
+    unsigned index_;
 };
 };
 
 
 /// %Octree component. Should be added only to the root scene node
 /// %Octree component. Should be added only to the root scene node

+ 8 - 16
Engine/Graphics/OctreeQuery.cpp

@@ -41,15 +41,13 @@ void PointOctreeQuery::TestDrawables(Drawable** start, Drawable** end, bool insi
 {
 {
     while (start != end)
     while (start != end)
     {
     {
-        Drawable* drawable = *start;
+        Drawable* drawable = *start++;
         
         
-        if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->IsVisible() && (drawable->GetViewMask() & viewMask_))
+        if (drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) && (drawable->GetViewMask() & viewMask_))
         {
         {
             if (inside || drawable->GetWorldBoundingBox().IsInside(point_))
             if (inside || drawable->GetWorldBoundingBox().IsInside(point_))
                 result_.Push(drawable);
                 result_.Push(drawable);
         }
         }
-        
-        ++start;
     }
     }
 }
 }
 
 
@@ -65,15 +63,13 @@ void SphereOctreeQuery::TestDrawables(Drawable** start, Drawable** end, bool ins
 {
 {
     while (start != end)
     while (start != end)
     {
     {
-        Drawable* drawable = *start;
+        Drawable* drawable = *start++;
         
         
-        if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->IsVisible() && (drawable->GetViewMask() & viewMask_))
+        if (drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) && (drawable->GetViewMask() & viewMask_))
         {
         {
             if (inside || sphere_.IsInsideFast(drawable->GetWorldBoundingBox()))
             if (inside || sphere_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 result_.Push(drawable);
                 result_.Push(drawable);
         }
         }
-        
-        ++start;
     }
     }
 }
 }
 
 
@@ -89,15 +85,13 @@ void BoxOctreeQuery::TestDrawables(Drawable** start, Drawable** end, bool inside
 {
 {
     while (start != end)
     while (start != end)
     {
     {
-        Drawable* drawable = *start;
+        Drawable* drawable = *start++;
         
         
-        if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->IsVisible() && (drawable->GetViewMask() & viewMask_))
+        if (drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) && (drawable->GetViewMask() & viewMask_))
         {
         {
             if (inside || box_.IsInsideFast(drawable->GetWorldBoundingBox()))
             if (inside || box_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 result_.Push(drawable);
                 result_.Push(drawable);
         }
         }
-        
-        ++start;
     }
     }
 }
 }
 
 
@@ -113,15 +107,13 @@ void FrustumOctreeQuery::TestDrawables(Drawable** start, Drawable** end, bool in
 {
 {
     while (start != end)
     while (start != end)
     {
     {
-        Drawable* drawable = *start;
+        Drawable* drawable = *start++;
         
         
-        if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->IsVisible() && (drawable->GetViewMask() & viewMask_))
+        if (drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) && (drawable->GetViewMask() & viewMask_))
         {
         {
             if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
             if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 result_.Push(drawable);
                 result_.Push(drawable);
         }
         }
-        
-        ++start;
     }
     }
 }
 }
 
 

+ 41 - 25
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -144,10 +144,11 @@ static unsigned numInstances = 0;
 
 
 OBJECTTYPESTATIC(Graphics);
 OBJECTTYPESTATIC(Graphics);
 
 
-bool CheckExtension(const String& name)
+bool CheckExtension(String& extensions, const String& name)
 {
 {
-    String extensions((const char*)glGetString(GL_EXTENSIONS));
-    return extensions.Find(name) != String::NPOS;
+    if (extensions.Empty())
+        extensions = (const char*)glGetString(GL_EXTENSIONS);
+    return extensions.Contains(name);
 }
 }
 
 
 Graphics::Graphics(Context* context_) :
 Graphics::Graphics(Context* context_) :
@@ -171,7 +172,8 @@ Graphics::Graphics(Context* context_) :
     maxScratchBufferRequest_(0),
     maxScratchBufferRequest_(0),
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
-    defaultTextureFilterMode_(FILTER_BILINEAR)
+    defaultTextureFilterMode_(FILTER_BILINEAR),
+    releasingGPUObjects_(false)
 {
 {
     SetTextureUnitMappings();
     SetTextureUnitMappings();
     ResetCachedState();
     ResetCachedState();
@@ -255,6 +257,8 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool
         }
         }
     }
     }
     
     
+    String extensions;
+    
     // With an external window, only the size can change after initial setup, so do not recreate context
     // With an external window, only the size can change after initial setup, so do not recreate context
     if (!externalWindow_ || !impl_->context_)
     if (!externalWindow_ || !impl_->context_)
     {
     {
@@ -356,19 +360,19 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool
                 return false;
                 return false;
             }
             }
             
             
-            if (!CheckExtension("EXT_framebuffer_object") || !CheckExtension("EXT_packed_depth_stencil"))
+            if (!_GLEE_EXT_framebuffer_object || !_GLEE_EXT_packed_depth_stencil)
             {
             {
                 LOGERROR("EXT_framebuffer_object and EXT_packed_depth_stencil OpenGL extensions are required");
                 LOGERROR("EXT_framebuffer_object and EXT_packed_depth_stencil OpenGL extensions are required");
                 Release(true, true);
                 Release(true, true);
                 return false;
                 return false;
             }
             }
         
         
-            dxtTextureSupport_ = CheckExtension("EXT_texture_compression_s3tc");
-            anisotropySupport_ = CheckExtension("EXT_texture_filter_anisotropic");
+            dxtTextureSupport_ = _GLEE_EXT_texture_compression_s3tc;
+            anisotropySupport_ = _GLEE_EXT_texture_filter_anisotropic;
             #else
             #else
-            dxtTextureSupport_ = CheckExtension("EXT_texture_compression_dxt1");
-            etcTextureSupport_ = CheckExtension("OES_compressed_ETC1_RGB8_texture");
-            pvrtcTextureSupport_ = CheckExtension("IMG_texture_compression_pvrtc");
+            dxtTextureSupport_ = CheckExtension(extensions, "EXT_texture_compression_dxt1");
+            etcTextureSupport_ = CheckExtension(extensions, "OES_compressed_ETC1_RGB8_texture");
+            pvrtcTextureSupport_ = CheckExtension(extensions, "IMG_texture_compression_pvrtc");
             #endif
             #endif
         }
         }
     }
     }
@@ -395,7 +399,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool
     Clear(CLEAR_COLOR);
     Clear(CLEAR_COLOR);
     SDL_GL_SwapWindow(impl_->window_);
     SDL_GL_SwapWindow(impl_->window_);
     
     
-    CheckFeatureSupport();
+    CheckFeatureSupport(extensions);
     
     
     if (multiSample > 1)
     if (multiSample > 1)
         LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed") +
         LOGINFO("Set screen mode " + String(width_) + "x" + String(height_) + " " + (fullscreen_ ? "fullscreen" : "windowed") +
@@ -1154,14 +1158,21 @@ void Graphics::ClearTransformSources()
 
 
 void Graphics::CleanupShaderPrograms()
 void Graphics::CleanupShaderPrograms()
 {
 {
+    // Ignore individual call from ShaderVariation instance when Graphics subsystem is in process of
+    // releasing all GPU objects or recreating GPU objects due device lost, because the Graphics subsystem
+    // will eventually erase all the shader programs afterward as part of the release process.
+    if (releasingGPUObjects_)
+        return;
+    
     for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
     for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
     {
     {
-        ShaderProgramMap::Iterator current = i++;
-        ShaderVariation* vs = current->second_->GetVertexShader();
-        ShaderVariation* ps = current->second_->GetPixelShader();
+        ShaderVariation* vs = i->second_->GetVertexShader();
+        ShaderVariation* ps = i->second_->GetPixelShader();
         
         
         if (!vs || !ps || !vs->GetGPUObject() || !ps->GetGPUObject())
         if (!vs || !ps || !vs->GetGPUObject() || !ps->GetGPUObject())
-            shaderPrograms_.Erase(current);
+            i = shaderPrograms_.Erase(i);
+        else
+            ++i;
     }
     }
 }
 }
 
 
@@ -1549,7 +1560,6 @@ void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusiv
 void Graphics::SetScissorTest(bool enable, const IntRect& rect)
 void Graphics::SetScissorTest(bool enable, const IntRect& rect)
 {
 {
     IntVector2 rtSize(GetRenderTargetDimensions());
     IntVector2 rtSize(GetRenderTargetDimensions());
-    IntVector2 viewSize(viewport_.Size());
     IntVector2 viewPos(viewport_.left_, viewport_.top_);
     IntVector2 viewPos(viewport_.left_, viewport_.top_);
     
     
     if (enable)
     if (enable)
@@ -1722,9 +1732,10 @@ unsigned Graphics::GetFormat(CompressedFormat format) const
     case CF_PVRTC_RGBA_4BPP:
     case CF_PVRTC_RGBA_4BPP:
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0;
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0;
     #endif
     #endif
-    }
     
     
-    return 0;
+    default:
+        return 0;
+    }
 }
 }
 
 
 
 
@@ -1792,7 +1803,7 @@ void Graphics::AddGPUObject(GPUObject* object)
 
 
 void Graphics::RemoveGPUObject(GPUObject* object)
 void Graphics::RemoveGPUObject(GPUObject* object)
 {
 {
-    gpuObjects_.Erase(gpuObjects_.Find(object));
+    gpuObjects_.Remove(object);
 }
 }
 
 
 void* Graphics::ReserveScratchBuffer(unsigned size)
 void* Graphics::ReserveScratchBuffer(unsigned size)
@@ -1877,6 +1888,8 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
     if (!impl_->window_)
     if (!impl_->window_)
         return;
         return;
     
     
+    releasingGPUObjects_ = true;
+    
     if (clearGPUObjects)
     if (clearGPUObjects)
     {
     {
         // Shutting down: release all GPU objects that still exist
         // Shutting down: release all GPU objects that still exist
@@ -1891,6 +1904,8 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
             (*i)->OnDeviceLost();
             (*i)->OnDeviceLost();
     }
     }
     
     
+    releasingGPUObjects_ = false;
+    
     CleanupFramebuffers(true);
     CleanupFramebuffers(true);
     depthTextures_.Clear();
     depthTextures_.Clear();
     shaderPrograms_.Clear();
     shaderPrograms_.Clear();
@@ -2139,7 +2154,7 @@ unsigned Graphics::GetFormat(const String& formatName)
     return GetRGBFormat();
     return GetRGBFormat();
 }
 }
 
 
-void Graphics::CheckFeatureSupport()
+void Graphics::CheckFeatureSupport(String& extensions)
 {
 {
     // Check supported features: light pre-pass, deferred rendering and hardware depth texture
     // Check supported features: light pre-pass, deferred rendering and hardware depth texture
     lightPrepassSupport_ = false;
     lightPrepassSupport_ = false;
@@ -2156,7 +2171,7 @@ void Graphics::CheckFeatureSupport()
     if (numSupportedRTs >= 4)
     if (numSupportedRTs >= 4)
         deferredSupport_ = true;
         deferredSupport_ = true;
     #else
     #else
-    if (!CheckExtension("GL_OES_depth_texture"))
+    if (!CheckExtension(extensions, "GL_OES_depth_texture"))
     {
     {
         shadowMapFormat_ = 0;
         shadowMapFormat_ = 0;
         hiresShadowMapFormat_ = 0;
         hiresShadowMapFormat_ = 0;
@@ -2370,13 +2385,14 @@ void Graphics::CleanupFramebuffers(bool contextLost)
         for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
         for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
             i != impl_->frameBuffers_.End();)
             i != impl_->frameBuffers_.End();)
         {
         {
-            HashMap<unsigned long long, FrameBufferObject>::Iterator current = i++;
-            if (current->second_.fbo_ != impl_->boundFbo_ && current->second_.useTimer_.GetMSec(false) >
+            if (i->second_.fbo_ != impl_->boundFbo_ && i->second_.useTimer_.GetMSec(false) >
                 MAX_FRAMEBUFFER_AGE)
                 MAX_FRAMEBUFFER_AGE)
             {
             {
-                glDeleteFramebuffersEXT(1, &current->second_.fbo_);
-                impl_->frameBuffers_.Erase(current);
+                glDeleteFramebuffersEXT(1, &i->second_.fbo_);
+                i = impl_->frameBuffers_.Erase(i);
             }
             }
+            else
+                ++i;
         }
         }
     }
     }
     else
     else

+ 3 - 1
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -381,7 +381,7 @@ public:
     
     
 private:
 private:
     /// Check supported rendering features.
     /// Check supported rendering features.
-    void CheckFeatureSupport();
+    void CheckFeatureSupport(String& extensions);
     /// Select FBO and commit changes.
     /// Select FBO and commit changes.
     void CommitFramebuffer();
     void CommitFramebuffer();
     /// Check FBO completeness.
     /// Check FBO completeness.
@@ -513,6 +513,8 @@ private:
     Matrix3 tempMatrices3_[NUM_TEMP_MATRICES];
     Matrix3 tempMatrices3_[NUM_TEMP_MATRICES];
     /// Temp matrices for transposing shader parameters.
     /// Temp matrices for transposing shader parameters.
     Matrix4 tempMatrices4_[NUM_TEMP_MATRICES];
     Matrix4 tempMatrices4_[NUM_TEMP_MATRICES];
+    /// Releasing GPU objects flag.
+    bool releasingGPUObjects_;
 };
 };
 
 
 /// Register Graphics library objects.
 /// Register Graphics library objects.

+ 0 - 4
Engine/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -22,12 +22,8 @@
 //
 //
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
-#include "Context.h"
 #include "Graphics.h"
 #include "Graphics.h"
-#include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "GraphicsImpl.h"
-#include "Mutex.h"
-#include "ProcessUtils.h"
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 

+ 3 - 5
Engine/Graphics/OpenGL/OGLIndexBuffer.cpp

@@ -59,11 +59,6 @@ IndexBuffer::~IndexBuffer()
     Release();
     Release();
 }
 }
 
 
-void IndexBuffer::OnDeviceLost()
-{
-    GPUObject::OnDeviceLost();
-}
-
 void IndexBuffer::OnDeviceReset()
 void IndexBuffer::OnDeviceReset()
 {
 {
     if (!object_)
     if (!object_)
@@ -270,6 +265,9 @@ void IndexBuffer::Unlock()
         lockScratchData_ = 0;
         lockScratchData_ = 0;
         lockState_ = LOCK_NONE;
         lockState_ = LOCK_NONE;
         break;
         break;
+    
+    default:
+        break;
     }
     }
 }
 }
 
 

+ 0 - 2
Engine/Graphics/OpenGL/OGLIndexBuffer.h

@@ -42,8 +42,6 @@ public:
     /// Destruct.
     /// Destruct.
     virtual ~IndexBuffer();
     virtual ~IndexBuffer();
     
     
-    /// Mark the GPU resource destroyed on context destruction.
-    virtual void OnDeviceLost();
     /// Recreate the GPU resource and restore data if applicable.
     /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     virtual void OnDeviceReset();
     /// Release the buffer.
     /// Release the buffer.

+ 2 - 4
Engine/Graphics/OpenGL/OGLShader.cpp

@@ -114,8 +114,7 @@ ShaderVariation* Shader::GetVariation(ShaderType type, const String& name)
             // Create the shader variation now if not created yet
             // Create the shader variation now if not created yet
             if (i == vsVariations_.End())
             if (i == vsVariations_.End())
             {
             {
-                ShaderCombination combination;
-                vsParser_.GetCombination(combination, name);
+                ShaderCombination combination = vsParser_.GetCombination(name);
                 
                 
                 i = vsVariations_.Insert(MakePair(nameHash, SharedPtr<ShaderVariation>(new ShaderVariation(this, VS))));
                 i = vsVariations_.Insert(MakePair(nameHash, SharedPtr<ShaderVariation>(new ShaderVariation(this, VS))));
                 
                 
@@ -145,8 +144,7 @@ ShaderVariation* Shader::GetVariation(ShaderType type, const String& name)
             // Create the shader variation now if not created yet
             // Create the shader variation now if not created yet
             if (i == psVariations_.End())
             if (i == psVariations_.End())
             {
             {
-                ShaderCombination combination;
-                psParser_.GetCombination(combination, name);
+                ShaderCombination combination = psParser_.GetCombination(name);
                 
                 
                 i = psVariations_.Insert(MakePair(nameHash, SharedPtr<ShaderVariation>(new ShaderVariation(this, PS))));
                 i = psVariations_.Insert(MakePair(nameHash, SharedPtr<ShaderVariation>(new ShaderVariation(this, PS))));
                 
                 

+ 1 - 1
Engine/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -152,7 +152,7 @@ bool ShaderProgram::Link()
         if (index != String::NPOS)
         if (index != String::NPOS)
         {
         {
             // If not the first index, skip
             // If not the first index, skip
-            if (name.Find("[0]") == String::NPOS)
+            if (name.Find("[0]", index) == String::NPOS)
                 continue;
                 continue;
             
             
             name = name.Substring(0, index);
             name = name.Substring(0, index);

+ 11 - 12
Engine/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -102,17 +102,6 @@ bool ShaderVariation::Create()
     // Check if there is a version definition; it must stay in the beginning
     // Check if there is a version definition; it must stay in the beginning
     String shaderCode(sourceCode_.Get(), sourceCodeLength_);
     String shaderCode(sourceCode_.Get(), sourceCodeLength_);
     String defines;
     String defines;
-    String version;
-    
-    if (shaderCode.StartsWith("#version"))
-    {
-        unsigned firstLineEnd = shaderCode.Find('\n') + 1;
-        if (firstLineEnd != String::NPOS)
-        {
-            version = shaderCode.Substring(0, firstLineEnd);
-            shaderCode = shaderCode.Substring(firstLineEnd);
-        }
-    }
     
     
     for (unsigned i = 0; i < defines_.Size(); ++i)
     for (unsigned i = 0; i < defines_.Size(); ++i)
         defines += "#define " + defines_[i] + " " + defineValues_[i] + "\n";
         defines += "#define " + defines_[i] + " " + defineValues_[i] + "\n";
@@ -120,7 +109,17 @@ bool ShaderVariation::Create()
     if (!defines_.Empty())
     if (!defines_.Empty())
         defines += "\n";
         defines += "\n";
     
     
-    shaderCode = version + defines + shaderCode;
+    unsigned pos = 0;
+    if (shaderCode.StartsWith("#version"))
+    {
+        pos = shaderCode.Find('\n');
+        if (pos != String::NPOS)
+            ++pos;
+        else
+            pos = 0;
+    }
+    
+    shaderCode.Insert(pos, defines);
     
     
     const char* shaderCStr = shaderCode.CString();
     const char* shaderCStr = shaderCode.CString();
     glShaderSource(object_, 1, &shaderCStr, 0);
     glShaderSource(object_, 1, &shaderCStr, 0);

+ 6 - 3
Engine/Graphics/OpenGL/OGLTexture.cpp

@@ -136,10 +136,10 @@ void Texture::UpdateParameters()
         return;
         return;
     
     
     // Wrapping
     // Wrapping
-    glTexParameteri(target_, GL_TEXTURE_WRAP_S, glWrapModes[addressMode_[0]]);
-    glTexParameteri(target_, GL_TEXTURE_WRAP_T, glWrapModes[addressMode_[1]]);
+    glTexParameteri(target_, GL_TEXTURE_WRAP_S, glWrapModes[addressMode_[COORD_U]]);
+    glTexParameteri(target_, GL_TEXTURE_WRAP_T, glWrapModes[addressMode_[COORD_V]]);
     #ifndef GL_ES_VERSION_2_0
     #ifndef GL_ES_VERSION_2_0
-    glTexParameteri(target_, GL_TEXTURE_WRAP_R, glWrapModes[addressMode_[2]]);
+    glTexParameteri(target_, GL_TEXTURE_WRAP_R, glWrapModes[addressMode_[COORD_W]]);
     #endif
     #endif
     
     
     TextureFilterMode filterMode = filterMode_;
     TextureFilterMode filterMode = filterMode_;
@@ -170,6 +170,9 @@ void Texture::UpdateParameters()
             glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
             glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
         glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
         glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
         break;
         break;
+    
+    default:
+        break;
     }
     }
     
     
     #ifndef GL_ES_VERSION_2_0
     #ifndef GL_ES_VERSION_2_0

+ 1 - 3
Engine/Graphics/OpenGL/OGLTexture2D.cpp

@@ -158,10 +158,8 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
         filterMode_ = FILTER_NEAREST;
         filterMode_ = FILTER_NEAREST;
         requestedLevels_ = 1;
         requestedLevels_ = 1;
     }
     }
-    else if (usage == TEXTURE_DYNAMIC)
-        dynamic_ = true;
     else
     else
-        dynamic_ = false;
+        dynamic_ = usage == TEXTURE_DYNAMIC;
     
     
     width_ = width;
     width_ = width;
     height_ = height;
     height_ = height;

+ 2 - 4
Engine/Graphics/OpenGL/OGLTextureCube.cpp

@@ -155,10 +155,8 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
         requestedLevels_ = 1;
         requestedLevels_ = 1;
         dynamic_ = true;
         dynamic_ = true;
     }
     }
-    else if (usage == TEXTURE_DYNAMIC)
-        dynamic_ = true;
     else
     else
-        dynamic_ = false;
+        dynamic_ = usage == TEXTURE_DYNAMIC;
     
     
     width_ = size;
     width_ = size;
     height_ = size;
     height_ = size;
@@ -283,7 +281,7 @@ bool TextureCube::Load(Deserializer& source)
         
         
         SharedPtr<Image> image(cache->GetResource<Image>(name));
         SharedPtr<Image> image(cache->GetResource<Image>(name));
         Load((CubeMapFace)faces, image);
         Load((CubeMapFace)faces, image);
-        faces++;
+        ++faces;
         
         
         faceElem = faceElem.GetNext("face");
         faceElem = faceElem.GetNext("face");
     }
     }

+ 4 - 9
Engine/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -145,11 +145,6 @@ VertexBuffer::~VertexBuffer()
     Release();
     Release();
 }
 }
 
 
-void VertexBuffer::OnDeviceLost()
-{
-    GPUObject::OnDeviceLost();
-}
-
 void VertexBuffer::OnDeviceReset()
 void VertexBuffer::OnDeviceReset()
 {
 {
     if (!object_)
     if (!object_)
@@ -359,6 +354,9 @@ void VertexBuffer::Unlock()
         lockScratchData_ = 0;
         lockScratchData_ = 0;
         lockState_ = LOCK_NONE;
         lockState_ = LOCK_NONE;
         break;
         break;
+    
+    default:
+        break;
     }
     }
 }
 }
 
 
@@ -395,11 +393,8 @@ unsigned VertexBuffer::GetElementOffset(unsigned elementMask, VertexElement elem
 {
 {
     unsigned offset = 0;
     unsigned offset = 0;
     
     
-    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    for (unsigned i = 0; i != element; ++i)
     {
     {
-        if (i == element)
-            break;
-        
         if (elementMask & (1 << i))
         if (elementMask & (1 << i))
             offset += elementSize[i];
             offset += elementSize[i];
     }
     }

+ 0 - 2
Engine/Graphics/OpenGL/OGLVertexBuffer.h

@@ -41,8 +41,6 @@ public:
     /// Destruct.
     /// Destruct.
     virtual ~VertexBuffer();
     virtual ~VertexBuffer();
     
     
-    /// Mark the GPU resource destroyed on context destruction.
-    virtual void OnDeviceLost();
     /// Recreate the GPU resource and restore data if applicable.
     /// Recreate the GPU resource and restore data if applicable.
     virtual void OnDeviceReset();
     virtual void OnDeviceReset();
     /// Release the buffer.
     /// Release the buffer.

+ 5 - 10
Engine/Graphics/ParticleEmitter.cpp

@@ -324,10 +324,7 @@ bool ParticleEmitter::LoadParameters(XMLFile* file)
         XMLElement colorFadeElem = rootElem.GetChild("colorfade");
         XMLElement colorFadeElem = rootElem.GetChild("colorfade");
         while (colorFadeElem)
         while (colorFadeElem)
         {
         {
-            ColorFade fade;
-            fade.color_ = colorFadeElem.GetColor("color");
-            fade.time_ = colorFadeElem.GetFloat("time");
-            fades.Push(fade);
+            fades.Push(ColorFade(colorFadeElem.GetColor("color"), colorFadeElem.GetFloat("time")));
             
             
             colorFadeElem = colorFadeElem.GetNext("colorfade");
             colorFadeElem = colorFadeElem.GetNext("colorfade");
         }
         }
@@ -394,6 +391,7 @@ ResourceRef ParticleEmitter::GetParameterSourceAttr() const
 VariantVector ParticleEmitter::GetParticlesAttr() const
 VariantVector ParticleEmitter::GetParticlesAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
+    ret.Reserve(particles_.Size() * 8 + 1);
     ret.Push(particles_.Size());
     ret.Push(particles_.Size());
     for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
     for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
     {
     {
@@ -430,12 +428,8 @@ void ParticleEmitter::SetNumParticles(int num)
 
 
 void ParticleEmitter::SetParticleColor(const Color& color)
 void ParticleEmitter::SetParticleColor(const Color& color)
 {
 {
-    ColorFade newColor;
-    newColor.color_ = color;
-    newColor.time_ = 0.0f;
-    
     colors_.Clear();
     colors_.Clear();
-    colors_.Push(newColor);
+    colors_.Push(ColorFade(color));
 }
 }
 
 
 void ParticleEmitter::SetParticleColors(const Vector<ColorFade>& colors)
 void ParticleEmitter::SetParticleColors(const Vector<ColorFade>& colors)
@@ -449,8 +443,9 @@ void ParticleEmitter::SetParticleColors(const Vector<ColorFade>& colors)
 bool ParticleEmitter::EmitNewParticle()
 bool ParticleEmitter::EmitNewParticle()
 {
 {
     unsigned index = GetFreeParticle();
     unsigned index = GetFreeParticle();
-    if (index >= particles_.Size())
+    if (index == M_MAX_UNSIGNED)
         return false;
         return false;
+    assert(index < particles_.Size());
     Particle& particle = particles_[index];
     Particle& particle = particles_[index];
     Billboard& billboard = billboards_[index];
     Billboard& billboard = billboards_[index];
     
     

+ 0 - 1
Engine/Graphics/ParticleEmitter.h

@@ -66,7 +66,6 @@ struct TextureAnimation
     float time_;
     float time_;
 };
 };
 
 
-class ResourceCache;
 class XMLFile;
 class XMLFile;
 class XMLElement;
 class XMLElement;
 
 

+ 49 - 62
Engine/Graphics/Renderer.cpp

@@ -286,7 +286,8 @@ Renderer::Renderer(Context* context) :
 {
 {
     SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
     SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
     SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
     SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
-    SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate));
+
+    // Delay SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate)) until renderer is initialized
     
     
     quadDirLight_->SetLightType(LIGHT_DIRECTIONAL);
     quadDirLight_->SetLightType(LIGHT_DIRECTIONAL);
     
     
@@ -457,7 +458,12 @@ void Renderer::SetMaxShadowMaps(int shadowMaps)
 
 
 void Renderer::SetMaxShadowCascades(int cascades)
 void Renderer::SetMaxShadowCascades(int cascades)
 {
 {
+    #ifndef USE_OPENGL
+    // Due to instruction count limits, deferred modes in SM2.0 can only support up to 3 cascades
+    cascades = Clamp(cascades, 1, renderMode_ != RENDER_FORWARD && !graphics_->GetSM3Support() ? 3 : MAX_CASCADE_SPLITS);
+    #else
     cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
     cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
+    #endif
     
     
     if (cascades != maxShadowCascades_)
     if (cascades != maxShadowCascades_)
     {
     {
@@ -510,17 +516,6 @@ Viewport* Renderer::GetViewport(unsigned index) const
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
 }
 }
 
 
-int Renderer::GetMaxShadowCascades() const
-{
-    // Due to instruction count limits, deferred modes in SM2.0 can only support up to 3 cascades
-    #ifndef USE_OPENGL
-    if (renderMode_ != RENDER_FORWARD && !graphics_->GetSM3Support())
-        return Max(maxShadowCascades_, 3);
-    #endif
-    
-    return maxShadowCascades_;
-}
-
 ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const
 ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const
 {
 {
     return GetShader(VS, name, checkExists);
     return GetShader(VS, name, checkExists);
@@ -649,8 +644,8 @@ void Renderer::Update(float timeStep)
 
 
 void Renderer::Render()
 void Renderer::Render()
 {
 {
-    if (!graphics_)
-        return;
+    // Engine does not render when window is closed or device is lost
+    assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
     
     
     PROFILE(RenderViews);
     PROFILE(RenderViews);
     
     
@@ -750,10 +745,9 @@ bool Renderer::AddView(RenderSurface* renderTarget, Viewport* viewport)
         }
         }
     }
     }
     
     
-    if (views_.Size() <= numViews_)
-        views_.Resize(numViews_ + 1);
-    if (!views_[numViews_])
-        views_[numViews_] = new View(context_);
+    assert(numViews_ <= views_.Size());
+    if (numViews_ == views_.Size())
+        views_.Push(SharedPtr<View>(new View(context_)));
     
     
     if (views_[numViews_]->Define(renderTarget, viewport))
     if (views_[numViews_]->Define(renderTarget, viewport))
     {
     {
@@ -766,15 +760,15 @@ bool Renderer::AddView(RenderSurface* renderTarget, Viewport* viewport)
 
 
 Geometry* Renderer::GetLightGeometry(Light* light)
 Geometry* Renderer::GetLightGeometry(Light* light)
 {
 {
-    LightType type = light->GetLightType();
-    if (type == LIGHT_DIRECTIONAL)
+    switch (light->GetLightType())
+    {
+    case LIGHT_DIRECTIONAL:
         return dirLightGeometry_;
         return dirLightGeometry_;
-    if (type == LIGHT_SPOT)
+    case LIGHT_SPOT:
         return spotLightGeometry_;
         return spotLightGeometry_;
-    else if (type == LIGHT_POINT)
+    case LIGHT_POINT:
         return pointLightGeometry_;
         return pointLightGeometry_;
-    else
-        return 0;
+    }
 }
 }
 
 
 Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight)
 Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight)
@@ -963,7 +957,8 @@ RenderSurface* Renderer::GetDepthStencil(int width, int height)
 
 
 OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
 OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
 {
 {
-    if (numOcclusionBuffers_ >= occlusionBuffers_.Size())
+    assert(numOcclusionBuffers_ <= occlusionBuffers_.Size());
+    if (numOcclusionBuffers_ == occlusionBuffers_.Size())
     {
     {
         SharedPtr<OcclusionBuffer> newBuffer(new OcclusionBuffer(context_));
         SharedPtr<OcclusionBuffer> newBuffer(new OcclusionBuffer(context_));
         occlusionBuffers_.Push(newBuffer);
         occlusionBuffers_.Push(newBuffer);
@@ -972,12 +967,11 @@ OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
     int width = occlusionBufferSize_;
     int width = occlusionBufferSize_;
     int height = (int)((float)occlusionBufferSize_ / camera->GetAspectRatio() + 0.5f);
     int height = (int)((float)occlusionBufferSize_ / camera->GetAspectRatio() + 0.5f);
     
     
-    OcclusionBuffer* buffer = occlusionBuffers_[numOcclusionBuffers_];
+    OcclusionBuffer* buffer = occlusionBuffers_[numOcclusionBuffers_++];
     buffer->SetSize(width, height);
     buffer->SetSize(width, height);
     buffer->SetView(camera);
     buffer->SetView(camera);
     buffer->ResetUseTimer();
     buffer->ResetUseTimer();
     
     
-    ++numOcclusionBuffers_;
     return buffer;
     return buffer;
 }
 }
 
 
@@ -985,18 +979,18 @@ Camera* Renderer::GetShadowCamera()
 {
 {
     MutexLock lock(rendererMutex_);
     MutexLock lock(rendererMutex_);
     
     
-    if (numShadowCameras_ >= shadowCameraNodes_.Size())
+    assert(numShadowCameras_ <= shadowCameraNodes_.Size());
+    if (numShadowCameras_ == shadowCameraNodes_.Size())
     {
     {
         SharedPtr<Node> newNode(new Node(context_));
         SharedPtr<Node> newNode(new Node(context_));
         newNode->CreateComponent<Camera>();
         newNode->CreateComponent<Camera>();
         shadowCameraNodes_.Push(newNode);
         shadowCameraNodes_.Push(newNode);
     }
     }
     
     
-    Camera* camera = shadowCameraNodes_[numShadowCameras_]->GetComponent<Camera>();
+    Camera* camera = shadowCameraNodes_[numShadowCameras_++]->GetComponent<Camera>();
     camera->SetOrthographic(false);
     camera->SetOrthographic(false);
     camera->SetZoom(1.0f);
     camera->SetZoom(1.0f);
     
     
-    ++numShadowCameras_;
     return camera;
     return camera;
 }
 }
 
 
@@ -1088,6 +1082,11 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
             case LIGHT_DIRECTIONAL:
             case LIGHT_DIRECTIONAL:
                 vsi += LVS_DIR;
                 vsi += LVS_DIR;
                 break;
                 break;
+                    
+            case LIGHT_SPOT:
+                psi += LPS_SPOT;
+                vsi += LVS_SPOT;
+                break;
                 
                 
             case LIGHT_POINT:
             case LIGHT_POINT:
                 if (light->GetShapeTexture())
                 if (light->GetShapeTexture())
@@ -1096,11 +1095,6 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
                     psi += LPS_POINT;
                     psi += LPS_POINT;
                 vsi += LVS_POINT;
                 vsi += LVS_POINT;
                 break;
                 break;
-                
-            case LIGHT_SPOT:
-                psi += LPS_SPOT;
-                vsi += LVS_SPOT;
-                break;
             }
             }
             
             
             batch.vertexShader_ = vertexShaders[vsi];
             batch.vertexShader_ = vertexShaders[vsi];
@@ -1172,6 +1166,10 @@ void Renderer::SetLightVolumeBatchShaders(Batch& batch)
     case LIGHT_DIRECTIONAL:
     case LIGHT_DIRECTIONAL:
         vsi += DLVS_DIR;
         vsi += DLVS_DIR;
         break;
         break;
+            
+    case LIGHT_SPOT:
+        psi += DLPS_SPOT;
+        break;
         
         
     case LIGHT_POINT:
     case LIGHT_POINT:
         if (light->GetShapeTexture())
         if (light->GetShapeTexture())
@@ -1179,10 +1177,6 @@ void Renderer::SetLightVolumeBatchShaders(Batch& batch)
         else
         else
             psi += DLPS_POINT;
             psi += DLPS_POINT;
         break;
         break;
-        
-    case LIGHT_SPOT:
-        psi += DLPS_SPOT;
-        break;
     }
     }
     
     
     if (batch.lightQueue_->shadowMap_)
     if (batch.lightQueue_->shadowMap_)
@@ -1341,22 +1335,16 @@ const Rect& Renderer::GetLightScissor(Light* light, Camera* camera)
     const Matrix3x4& view = camera->GetInverseWorldTransform();
     const Matrix3x4& view = camera->GetInverseWorldTransform();
     const Matrix4& projection = camera->GetProjection();
     const Matrix4& projection = camera->GetProjection();
     
     
-    switch (light->GetLightType())
+    assert(light->GetLightType() != LIGHT_DIRECTIONAL);
+    if (light->GetLightType() == LIGHT_SPOT)
     {
     {
-    case LIGHT_POINT:
-        {
-            BoundingBox viewBox(light->GetWorldBoundingBox().Transformed(view));
-            return lightScissorCache_[combination] = viewBox.Projected(projection);
-        }
-        
-    case LIGHT_SPOT:
-        {
-            Frustum viewFrustum(light->GetFrustum().Transformed(view));
-            return lightScissorCache_[combination] = viewFrustum.Projected(projection);
-        }
-        
-    default:
-        return lightScissorCache_[combination] = Rect::FULL;
+        Frustum viewFrustum(light->GetFrustum().Transformed(view));
+        return lightScissorCache_[combination] = viewFrustum.Projected(projection);
+    }
+    else // LIGHT_POINT
+    {
+        BoundingBox viewBox(light->GetWorldBoundingBox().Transformed(view));
+        return lightScissorCache_[combination] = viewBox.Projected(projection);
     }
     }
 }
 }
 
 
@@ -1452,6 +1440,8 @@ void Renderer::Initialize()
     shadersDirty_ = true;
     shadersDirty_ = true;
     initialized_ = true;
     initialized_ = true;
     
     
+    SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate));
+
     LOGINFO("Initialized renderer");
     LOGINFO("Initialized renderer");
 }
 }
 
 
@@ -1538,9 +1528,9 @@ void Renderer::LoadPassShaders(Technique* tech, PassType type)
     String pixelShaderName = pass->GetPixelShader();
     String pixelShaderName = pass->GetPixelShader();
     
     
     // Check if the shader name is already a variation in itself
     // Check if the shader name is already a variation in itself
-    if (vertexShaderName.Find('_') == String::NPOS)
+    if (!vertexShaderName.Contains('_'))
         vertexShaderName += "_";
         vertexShaderName += "_";
-    if (pixelShaderName.Find('_') == String::NPOS)
+    if (!pixelShaderName.Contains('_'))
         pixelShaderName += "_";
         pixelShaderName += "_";
     
     
     Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
     Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
@@ -1787,12 +1777,9 @@ void Renderer::HandleGraphicsFeatures(StringHash eventType, VariantMap& eventDat
 
 
 void Renderer::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 void Renderer::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (initialized_)
-    {
-        using namespace RenderUpdate;
-        
-        Update(eventData[P_TIMESTEP].GetFloat());
-    }
+    using namespace RenderUpdate;
+    
+    Update(eventData[P_TIMESTEP].GetFloat());
 }
 }
 
 
 }
 }

+ 2 - 4
Engine/Graphics/Renderer.h

@@ -51,8 +51,6 @@ class View;
 class Zone;
 class Zone;
 
 
 static const int SHADOW_MIN_PIXELS = 64;
 static const int SHADOW_MIN_PIXELS = 64;
-static const int NUM_LIGHT_TYPES = 3;
-static const int NUM_SHADOWMAP_RESOLUTIONS = 3;
 static const int INSTANCING_BUFFER_DEFAULT_SIZE = 1024;
 static const int INSTANCING_BUFFER_DEFAULT_SIZE = 1024;
 
 
 /// Light vertex shader variations.
 /// Light vertex shader variations.
@@ -235,7 +233,7 @@ public:
     /// Return maximum number of shadow maps per resolution.
     /// Return maximum number of shadow maps per resolution.
     int GetMaxShadowMaps() const { return maxShadowMaps_; }
     int GetMaxShadowMaps() const { return maxShadowMaps_; }
     /// Return maximum number of directional light shadow map cascades.
     /// Return maximum number of directional light shadow map cascades.
-    int GetMaxShadowCascades() const;
+    int GetMaxShadowCascades() const { return maxShadowCascades_; }
     /// Return whether dynamic instancing is in use.
     /// Return whether dynamic instancing is in use.
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     /// Return maximum number of triangles per object for instancing.
     /// Return maximum number of triangles per object for instancing.
@@ -365,7 +363,7 @@ private:
     void ResetBuffers();
     void ResetBuffers();
     /// Handle screen mode event.
     /// Handle screen mode event.
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
-    /// Handle graphics features (re)check event.
+    /// Handle graphics features (re)check event. Event only sent by D3D9Graphics class.
     void HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData);
     void HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData);
     /// Handle render update event.
     /// Handle render update event.
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);

+ 5 - 6
Engine/Graphics/ShaderParser.cpp

@@ -22,7 +22,6 @@
 //
 //
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
-#include "HashSet.h"
 #include "ShaderParser.h"
 #include "ShaderParser.h"
 #include "XMLElement.h"
 #include "XMLElement.h"
 
 
@@ -77,8 +76,10 @@ bool ShaderParser::HasCombination(const String& name) const
     return combinations_.Contains(name);
     return combinations_.Contains(name);
 }
 }
 
 
-bool ShaderParser::GetCombination(ShaderCombination& dest, const String& name) const
+ShaderCombination ShaderParser::GetCombination(const String& name) const
 {
 {
+    ShaderCombination dest;
+    
     HashMap<String, unsigned>::ConstIterator i = combinations_.Find(name);
     HashMap<String, unsigned>::ConstIterator i = combinations_.Find(name);
     if (i != combinations_.End())
     if (i != combinations_.End())
     {
     {
@@ -100,11 +101,9 @@ bool ShaderParser::GetCombination(ShaderCombination& dest, const String& name) c
                 }
                 }
             }
             }
         }
         }
-        
-        return true;
     }
     }
-    else
-        return false;
+
+    return dest;
 }
 }
 
 
 bool ShaderParser::ParseOptions(const XMLElement& element)
 bool ShaderParser::ParseOptions(const XMLElement& element)

+ 1 - 1
Engine/Graphics/ShaderParser.h

@@ -83,7 +83,7 @@ public:
     /// Return whether a shader combination exists.
     /// Return whether a shader combination exists.
     bool HasCombination(const String& name) const;
     bool HasCombination(const String& name) const;
     /// Return a combination by name.
     /// Return a combination by name.
-    bool GetCombination(ShaderCombination& dest, const String& name) const;
+    ShaderCombination GetCombination(const String& name) const;
     
     
 private:
 private:
     /// Parse options for a shader.
     /// Parse options for a shader.

+ 2 - 1
Engine/Graphics/Skeleton.cpp

@@ -48,6 +48,7 @@ bool Skeleton::Load(Deserializer& source)
         return false;
         return false;
     
     
     unsigned bones = source.ReadUInt();
     unsigned bones = source.ReadUInt();
+    bones_.Reserve(bones);
     
     
     for (unsigned i = 0; i < bones; ++i)
     for (unsigned i = 0; i < bones; ++i)
     {
     {
@@ -106,7 +107,7 @@ void Skeleton::Define(const Skeleton& src)
 {
 {
     ClearBones();
     ClearBones();
     
     
-    bones_ = src.GetBones();
+    bones_ = src.bones_;
     // Make sure we clear node references, if they exist
     // Make sure we clear node references, if they exist
     // (AnimatedModel will create new nodes on its own)
     // (AnimatedModel will create new nodes on its own)
     for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
     for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)

+ 26 - 44
Engine/Graphics/StaticModel.cpp

@@ -44,11 +44,10 @@ namespace Urho3D
 OBJECTTYPESTATIC(StaticModel);
 OBJECTTYPESTATIC(StaticModel);
 
 
 StaticModel::StaticModel(Context* context) :
 StaticModel::StaticModel(Context* context) :
-    Drawable(context),
-    occlusionLodLevel_(M_MAX_UNSIGNED)
+    Drawable(context, DRAWABLE_GEOMETRY),
+    occlusionLodLevel_(M_MAX_UNSIGNED),
+    materialsAttr_(Material::GetTypeStatic())
 {
 {
-    drawableFlags_ = DRAWABLE_GEOMETRY;
-    materialsAttr_.type_ = Material::GetTypeStatic();
 }
 }
 
 
 StaticModel::~StaticModel()
 StaticModel::~StaticModel()
@@ -84,50 +83,37 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
         break;
         break;
         
         
     case RAY_OBB:
     case RAY_OBB:
-        {
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
-        }
-        break;
-        
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
+        Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
+        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        float distance = localRay.HitDistance(boundingBox_);
+        if (distance <= query.maxDistance_)
         {
         {
-            // Do a pretest using the OBB
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
+            if (level == RAY_TRIANGLE)
             {
             {
-                // Then the actual test using triangle geometry
-                for (unsigned i = 0; i < batches_.Size(); ++i)
+                // After a pretest using the OBB, do the actual test using triangle geometry
+                unsigned i = 0;
+                while (i < batches_.Size())
                 {
                 {
-                    Geometry* geometry = batches_[i].geometry_;
+                    Geometry* geometry = batches_[i++].geometry_;
                     if (geometry)
                     if (geometry)
                     {
                     {
                         distance = geometry->GetHitDistance(localRay);
                         distance = geometry->GetHitDistance(localRay);
                         if (distance <= query.maxDistance_)
                         if (distance <= query.maxDistance_)
-                        {
-                            RayQueryResult result;
-                            result.drawable_ = this;
-                            result.node_ = GetNode();
-                            result.distance_ = distance;
-                            result.subObject_ = M_MAX_UNSIGNED;
-                            results.Push(result);
                             break;
                             break;
-                        }
                     }
                     }
                 }
                 }
+                if (i == batches_.Size())
+                    break;
             }
             }
+            
+            // If the code reaches here then we have a hit
+            RayQueryResult result;
+            result.drawable_ = this;
+            result.node_ = node_;
+            result.distance_ = distance;
+            result.subObject_ = M_MAX_UNSIGNED;
+            results.Push(result);
         }
         }
         break;
         break;
     }
     }
@@ -197,8 +183,6 @@ unsigned StaticModel::GetNumOccluderTriangles()
 
 
 bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
 bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
 {
 {
-    bool success = true;
-    
     for (unsigned i = 0; i < batches_.Size(); ++i)
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
     {
         Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_);
         Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_);
@@ -231,13 +215,11 @@ bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
         unsigned indexCount = geometry->GetIndexCount();
         unsigned indexCount = geometry->GetIndexCount();
         
         
         // Draw and check for running out of triangles
         // Draw and check for running out of triangles
-        success = buffer->Draw(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, indexStart, indexCount);
-        
-        if (!success)
-            break;
+        if (!buffer->Draw(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, indexStart, indexCount))
+            return false;
     }
     }
     
     
-    return success;
+    return true;
 }
 }
 
 
 void StaticModel::SetModel(Model* model)
 void StaticModel::SetModel(Model* model)
@@ -386,7 +368,7 @@ void StaticModel::CalculateLodLevels()
 void StaticModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
 void StaticModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
 {
 {
     Model* currentModel = model_;
     Model* currentModel = model_;
-    model_ = 0; // Set null to allow to be re-set
+    model_.Reset(); // Set null to allow to be re-set
     SetModel(currentModel);
     SetModel(currentModel);
 }
 }
 
 

+ 15 - 7
Engine/Graphics/Tangent.cpp

@@ -28,12 +28,18 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-inline unsigned GetIndex(unsigned index, const void* indexData, unsigned indexSize)
+inline unsigned GetIndex(void*& indexPointer, unsigned indexSize)
 {
 {
     if (indexSize == sizeof(unsigned short))
     if (indexSize == sizeof(unsigned short))
-        return ((const unsigned short*)indexData)[index];
+    {
+        unsigned short* p = (unsigned short*)indexPointer;
+        return *p++;
+    }
     else
     else
-        return ((const unsigned*)indexData)[index];
+    {
+        unsigned* p = (unsigned*)indexPointer;
+        return *p++;
+    }
 }
 }
 
 
 void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart,
 void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart,
@@ -45,9 +51,10 @@ void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexDa
     unsigned maxVertex = 0;
     unsigned maxVertex = 0;
     unsigned char* vertices = (unsigned char*)vertexData;
     unsigned char* vertices = (unsigned char*)vertexData;
     
     
+    void* indexPointer = const_cast<void*>(indexData);
     for (unsigned i = indexStart; i < indexStart + indexCount; ++i)
     for (unsigned i = indexStart; i < indexStart + indexCount; ++i)
     {
     {
-        unsigned v = GetIndex(i, indexData, indexSize);
+        unsigned v = GetIndex(indexPointer, indexSize);
         if (v < minVertex)
         if (v < minVertex)
             minVertex = v;
             minVertex = v;
         if (v > maxVertex)
         if (v > maxVertex)
@@ -59,11 +66,12 @@ void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexDa
     Vector3 *tan2 = tan1 + vertexCount;
     Vector3 *tan2 = tan1 + vertexCount;
     memset(tan1, 0, sizeof(Vector3) * vertexCount * 2);
     memset(tan1, 0, sizeof(Vector3) * vertexCount * 2);
     
     
+    indexPointer = const_cast<void*>(indexData);
     for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
     for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
     {
     {
-        unsigned i1 = GetIndex(i, indexData, indexSize);
-        unsigned i2 = GetIndex(i + 1, indexData, indexSize);
-        unsigned i3 = GetIndex(i + 2, indexData, indexSize);
+        unsigned i1 = GetIndex(indexPointer, indexSize);
+        unsigned i2 = GetIndex(indexPointer, indexSize);
+        unsigned i3 = GetIndex(indexPointer, indexSize);
         
         
         const Vector3& v1 = *((Vector3*)(vertices + i1 * vertexSize));
         const Vector3& v1 = *((Vector3*)(vertices + i1 * vertexSize));
         const Vector3& v2 = *((Vector3*)(vertices + i2 * vertexSize));
         const Vector3& v2 = *((Vector3*)(vertices + i2 * vertexSize));

+ 6 - 6
Engine/Graphics/Terrain.cpp

@@ -61,12 +61,12 @@ Terrain::Terrain(Context* context) :
     Component(context),
     Component(context),
     indexBuffer_(new IndexBuffer(context)),
     indexBuffer_(new IndexBuffer(context)),
     spacing_(DEFAULT_SPACING),
     spacing_(DEFAULT_SPACING),
-    patchWorldSize_(Vector2::ZERO),
     patchWorldOrigin_(Vector2::ZERO),
     patchWorldOrigin_(Vector2::ZERO),
+    patchWorldSize_(Vector2::ZERO),
     numVertices_(IntVector2::ZERO),
     numVertices_(IntVector2::ZERO),
     numPatches_(IntVector2::ZERO),
     numPatches_(IntVector2::ZERO),
-    numLodLevels_(1),
     patchSize_(DEFAULT_PATCH_SIZE),
     patchSize_(DEFAULT_PATCH_SIZE),
+    numLodLevels_(1),
     visible_(true),
     visible_(true),
     castShadows_(false),
     castShadows_(false),
     occluder_(false),
     occluder_(false),
@@ -122,10 +122,7 @@ void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 void Terrain::ApplyAttributes()
 void Terrain::ApplyAttributes()
 {
 {
     if (recreateTerrain_)
     if (recreateTerrain_)
-    {
         CreateGeometry();
         CreateGeometry();
-        recreateTerrain_ = false;
-    }
 }
 }
 
 
 void Terrain::SetSpacing(const Vector3& spacing)
 void Terrain::SetSpacing(const Vector3& spacing)
@@ -642,7 +639,9 @@ void Terrain::CreateGeometry()
                         (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_;
                         (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_;
             }
             }
         }
         }
-        
+
+        patches_.Reserve(numPatches_.x_ * numPatches_.y_);
+
         // Create patches and set node transforms
         // Create patches and set node transforms
         for (int z = 0; z < numPatches_.y_; ++z)
         for (int z = 0; z < numPatches_.y_; ++z)
         {
         {
@@ -922,6 +921,7 @@ void Terrain::CalculateLodErrors(TerrainPatch* patch)
     const IntVector2& coords = patch->GetCoordinates();
     const IntVector2& coords = patch->GetCoordinates();
     PODVector<float>& lodErrors = patch->GetLodErrors();
     PODVector<float>& lodErrors = patch->GetLodErrors();
     lodErrors.Clear();
     lodErrors.Clear();
+    lodErrors.Reserve(numLodLevels_);
     
     
     int xStart = coords.x_ * patchSize_;
     int xStart = coords.x_ * patchSize_;
     int zStart = coords.y_ * patchSize_;
     int zStart = coords.y_ * patchSize_;

+ 16 - 33
Engine/Graphics/TerrainPatch.cpp

@@ -45,7 +45,7 @@ static const float LOD_CONSTANT = 1.0f / 150.0f;
 OBJECTTYPESTATIC(TerrainPatch);
 OBJECTTYPESTATIC(TerrainPatch);
 
 
 TerrainPatch::TerrainPatch(Context* context) :
 TerrainPatch::TerrainPatch(Context* context) :
-    Drawable(context),
+    Drawable(context, DRAWABLE_GEOMETRY),
     geometry_(new Geometry(context)),
     geometry_(new Geometry(context)),
     maxLodGeometry_(new Geometry(context)),
     maxLodGeometry_(new Geometry(context)),
     minLodGeometry_(new Geometry(context)),
     minLodGeometry_(new Geometry(context)),
@@ -54,8 +54,6 @@ TerrainPatch::TerrainPatch(Context* context) :
     lodLevel_(0),
     lodLevel_(0),
     occlusionOffset_(0.0f)
     occlusionOffset_(0.0f)
 {
 {
-    drawableFlags_ = DRAWABLE_GEOMETRY;
-    
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     maxLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     maxLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     minLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     minLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
@@ -86,43 +84,28 @@ void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQue
         break;
         break;
         
         
     case RAY_OBB:
     case RAY_OBB:
-        {
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.drawable_ = this;
-                result.node_ = GetNode();
-                result.distance_ = distance;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
-        }
-        break;
-        
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
+        Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
+        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        float distance = localRay.HitDistance(boundingBox_);
+        if (distance <= query.maxDistance_)
         {
         {
-            // Do a pretest using the OBB
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
-            float distance = localRay.HitDistance(boundingBox_);
-            if (distance <= query.maxDistance_)
+            if (level == RAY_TRIANGLE)
             {
             {
+                // Ater a pretest using the OBB, do the actual test using triangle geometry
                 distance = geometry_->GetHitDistance(localRay);
                 distance = geometry_->GetHitDistance(localRay);
                 
                 
-                if (distance <= query.maxDistance_)
-                {
-                    RayQueryResult result;
-                    result.drawable_ = this;
-                    result.node_ = GetNode();
-                    result.distance_ = distance;
-                    result.subObject_ = M_MAX_UNSIGNED;
-                    results.Push(result);
+                if (distance > query.maxDistance_)
                     break;
                     break;
-                }
             }
             }
+            
+            // If the code reaches here then we have a hit
+            RayQueryResult result;
+            result.drawable_ = this;
+            result.node_ = node_;
+            result.distance_ = distance;
+            result.subObject_ = M_MAX_UNSIGNED;
+            results.Push(result);
         }
         }
         break;
         break;
     }
     }

+ 93 - 132
Engine/Graphics/View.cpp

@@ -38,7 +38,6 @@
 #include "Scene.h"
 #include "Scene.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "Skybox.h"
 #include "Skybox.h"
-#include "Sort.h"
 #include "Technique.h"
 #include "Technique.h"
 #include "Texture2D.h"
 #include "Texture2D.h"
 #include "TextureCube.h"
 #include "TextureCube.h"
@@ -54,87 +53,62 @@ namespace Urho3D
 
 
 static const Vector3 directions[] =
 static const Vector3 directions[] =
 {
 {
-    Vector3(1.0f, 0.0f, 0.0f),
-    Vector3(-1.0f, 0.0f, 0.0f),
-    Vector3(0.0f, 1.0f, 0.0f),
-    Vector3(0.0f, -1.0f, 0.0f),
-    Vector3(0.0f, 0.0f, 1.0f),
-    Vector3(0.0f, 0.0f, -1.0f)
+    Vector3::RIGHT,
+    Vector3::LEFT,
+    Vector3::UP,
+    Vector3::DOWN,
+    Vector3::FORWARD,
+    Vector3::BACK
 };
 };
 
 
 static const int CHECK_DRAWABLES_PER_WORK_ITEM = 64;
 static const int CHECK_DRAWABLES_PER_WORK_ITEM = 64;
 static const float LIGHT_INTENSITY_THRESHOLD = 0.001f;
 static const float LIGHT_INTENSITY_THRESHOLD = 0.001f;
 
 
 /// %Frustum octree query for shadowcasters.
 /// %Frustum octree query for shadowcasters.
-class ShadowCasterOctreeQuery : public OctreeQuery
+class ShadowCasterOctreeQuery : public FrustumOctreeQuery
 {
 {
 public:
 public:
     /// Construct with frustum and query parameters.
     /// Construct with frustum and query parameters.
     ShadowCasterOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
     ShadowCasterOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
         unsigned viewMask = DEFAULT_VIEWMASK) :
         unsigned viewMask = DEFAULT_VIEWMASK) :
-        OctreeQuery(result, drawableFlags, viewMask),
-        frustum_(frustum)
+        FrustumOctreeQuery(result, frustum, drawableFlags, viewMask)
     {
     {
     }
     }
     
     
-    /// Intersection test for an octant.
-    virtual Intersection TestOctant(const BoundingBox& box, bool inside)
-    {
-        if (inside)
-            return INSIDE;
-        else
-            return frustum_.IsInside(box);
-    }
-    
     /// Intersection test for drawables.
     /// Intersection test for drawables.
     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
     {
     {
         while (start != end)
         while (start != end)
         {
         {
-            Drawable* drawable = *start;
+            Drawable* drawable = *start++;
             
             
-            if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->GetCastShadows() && drawable->IsVisible() &&
+            if (drawable->GetCastShadows() && drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) &&
                 (drawable->GetViewMask() & viewMask_))
                 (drawable->GetViewMask() & viewMask_))
             {
             {
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                     result_.Push(drawable);
                     result_.Push(drawable);
             }
             }
-            
-            ++start;
         }
         }
     }
     }
-    
-    /// Frustum.
-    Frustum frustum_;
 };
 };
 
 
 /// %Frustum octree query for zones and occluders.
 /// %Frustum octree query for zones and occluders.
-class ZoneOccluderOctreeQuery : public OctreeQuery
+class ZoneOccluderOctreeQuery : public FrustumOctreeQuery
 {
 {
 public:
 public:
     /// Construct with frustum and query parameters.
     /// Construct with frustum and query parameters.
     ZoneOccluderOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
     ZoneOccluderOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
         unsigned viewMask = DEFAULT_VIEWMASK) :
         unsigned viewMask = DEFAULT_VIEWMASK) :
-        OctreeQuery(result, drawableFlags, viewMask),
-        frustum_(frustum)
+        FrustumOctreeQuery(result, frustum, drawableFlags, viewMask)
     {
     {
     }
     }
     
     
-    /// Intersection test for an octant.
-    virtual Intersection TestOctant(const BoundingBox& box, bool inside)
-    {
-        if (inside)
-            return INSIDE;
-        else
-            return frustum_.IsInside(box);
-    }
-    
     /// Intersection test for drawables.
     /// Intersection test for drawables.
     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
     {
     {
         while (start != end)
         while (start != end)
         {
         {
-            Drawable* drawable = *start;
+            Drawable* drawable = *start++;
             unsigned char flags = drawable->GetDrawableFlags();
             unsigned char flags = drawable->GetDrawableFlags();
             
             
             if ((flags == DRAWABLE_ZONE || (flags == DRAWABLE_GEOMETRY && drawable->IsOccluder())) && drawable->IsVisible() &&
             if ((flags == DRAWABLE_ZONE || (flags == DRAWABLE_GEOMETRY && drawable->IsOccluder())) && drawable->IsVisible() &&
@@ -143,24 +117,18 @@ public:
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                     result_.Push(drawable);
                     result_.Push(drawable);
             }
             }
-            
-            ++start;
         }
         }
     }
     }
-    
-    /// Frustum.
-    Frustum frustum_;
 };
 };
 
 
 /// %Frustum octree query with occlusion.
 /// %Frustum octree query with occlusion.
-class OccludedFrustumOctreeQuery : public OctreeQuery
+class OccludedFrustumOctreeQuery : public FrustumOctreeQuery
 {
 {
 public:
 public:
     /// Construct with frustum, occlusion buffer and query parameters.
     /// Construct with frustum, occlusion buffer and query parameters.
     OccludedFrustumOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, OcclusionBuffer* buffer, unsigned char
     OccludedFrustumOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, OcclusionBuffer* buffer, unsigned char
         drawableFlags = DRAWABLE_ANY, unsigned viewMask = DEFAULT_VIEWMASK) :
         drawableFlags = DRAWABLE_ANY, unsigned viewMask = DEFAULT_VIEWMASK) :
-        OctreeQuery(result, drawableFlags, viewMask),
-        frustum_(frustum),
+        FrustumOctreeQuery(result, frustum, drawableFlags, viewMask),
         buffer_(buffer)
         buffer_(buffer)
     {
     {
     }
     }
@@ -184,21 +152,17 @@ public:
     {
     {
         while (start != end)
         while (start != end)
         {
         {
-            Drawable* drawable = *start;
+            Drawable* drawable = *start++;
             
             
-            if ((drawable->GetDrawableFlags() & drawableFlags_) && drawable->IsVisible() &&
+            if (drawable->IsVisible() && (drawable->GetDrawableFlags() & drawableFlags_) &&
                 (drawable->GetViewMask() & viewMask_))
                 (drawable->GetViewMask() & viewMask_))
             {
             {
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
                     result_.Push(drawable);
                     result_.Push(drawable);
             }
             }
-            
-            ++start;
         }
         }
     }
     }
     
     
-    /// Frustum.
-    Frustum frustum_;
     /// Occlusion buffer.
     /// Occlusion buffer.
     OcclusionBuffer* buffer_;
     OcclusionBuffer* buffer_;
 };
 };
@@ -210,6 +174,8 @@ void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
     Drawable** end = reinterpret_cast<Drawable**>(item->end_);
     Drawable** end = reinterpret_cast<Drawable**>(item->end_);
     OcclusionBuffer* buffer = view->occlusionBuffer_;
     OcclusionBuffer* buffer = view->occlusionBuffer_;
     const Matrix3x4& viewMatrix = view->camera_->GetInverseWorldTransform();
     const Matrix3x4& viewMatrix = view->camera_->GetInverseWorldTransform();
+    Vector3 viewZ = Vector3(viewMatrix.m20_, viewMatrix.m21_, viewMatrix.m22_);
+    Vector3 absViewZ = viewZ.Abs();
     
     
     while (start != end)
     while (start != end)
     {
     {
@@ -228,11 +194,9 @@ void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
             {
             {
                 const BoundingBox& geomBox = drawable->GetWorldBoundingBox();
                 const BoundingBox& geomBox = drawable->GetWorldBoundingBox();
                 Vector3 center = geomBox.Center();
                 Vector3 center = geomBox.Center();
-                float viewCenterZ = viewMatrix.m20_ * center.x_ + viewMatrix.m21_ * center.y_ + viewMatrix.m22_ * center.z_ +
-                    viewMatrix.m23_;
+                float viewCenterZ = viewZ.DotProduct(center) + viewMatrix.m23_;
                 Vector3 edge = geomBox.Size() * 0.5f;
                 Vector3 edge = geomBox.Size() * 0.5f;
-                float viewEdgeZ = Abs(viewMatrix.m20_) * edge.x_ + Abs(viewMatrix.m21_) * edge.y_ + Abs(viewMatrix.m22_) *
-                    edge.z_;
+                float viewEdgeZ = absViewZ.DotProduct(edge);
                 
                 
                 drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
                 drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
                 drawable->ClearLights();
                 drawable->ClearLights();
@@ -257,9 +221,8 @@ void UpdateDrawableGeometriesWork(const WorkItem* item, unsigned threadIndex)
     
     
     while (start != end)
     while (start != end)
     {
     {
-        Drawable* drawable = *start;
+        Drawable* drawable = *start++;
         drawable->UpdateGeometry(frame);
         drawable->UpdateGeometry(frame);
-        ++start;
     }
     }
 }
 }
 
 
@@ -300,12 +263,10 @@ View::View(Context* context) :
     camera_(0),
     camera_(0),
     cameraZone_(0),
     cameraZone_(0),
     farClipZone_(0),
     farClipZone_(0),
-    renderTarget_(0)
+    renderTarget_(0),
+    tempDrawables_(GetSubsystem<WorkQueue>()->GetNumThreads() + 1)  // Create octree query vector for each thread
 {
 {
     frame_.camera_ = 0;
     frame_.camera_ = 0;
-    
-    // Create octree query vector for each thread
-    tempDrawables_.Resize(GetSubsystem<WorkQueue>()->GetNumThreads() + 1);
 }
 }
 
 
 View::~View()
 View::~View()
@@ -549,7 +510,7 @@ void View::GetDrawables()
             int priority = zone->GetPriority();
             int priority = zone->GetPriority();
             if (priority > highestZonePriority_)
             if (priority > highestZonePriority_)
                 highestZonePriority_ = priority;
                 highestZonePriority_ = priority;
-            if (zone->IsInside(cameraPos) && priority > bestPriority)
+            if (priority > bestPriority && zone->IsInside(cameraPos))
             {
             {
                 cameraZone_ = zone;
                 cameraZone_ = zone;
                 bestPriority = priority;
                 bestPriority = priority;
@@ -569,7 +530,7 @@ void View::GetDrawables()
         for (PODVector<Zone*>::Iterator i = zones_.Begin(); i != zones_.End(); ++i)
         for (PODVector<Zone*>::Iterator i = zones_.Begin(); i != zones_.End(); ++i)
         {
         {
             int priority = (*i)->GetPriority();
             int priority = (*i)->GetPriority();
-            if ((*i)->IsInside(farClipPos) && priority > bestPriority)
+            if (priority > bestPriority && (*i)->IsInside(farClipPos))
             {
             {
                 farClipZone_ = *i;
                 farClipZone_ = *i;
                 bestPriority = priority;
                 bestPriority = priority;
@@ -1815,11 +1776,8 @@ void View::ProcessLight(LightQueryResult& query, unsigned threadIndex)
                 continue;
                 continue;
             if (maxZ_ < query.shadowNearSplits_[i])
             if (maxZ_ < query.shadowNearSplits_[i])
                 continue;
                 continue;
-        }
         
         
-        // Reuse lit geometry query for all except directional lights
-        if (type == LIGHT_DIRECTIONAL)
-        {
+            // Reuse lit geometry query for all except directional lights
             ShadowCasterOctreeQuery query(tempDrawables, shadowCameraFrustum, DRAWABLE_GEOMETRY,
             ShadowCasterOctreeQuery query(tempDrawables, shadowCameraFrustum, DRAWABLE_GEOMETRY,
                 camera_->GetViewMask());
                 camera_->GetViewMask());
             octree_->GetDrawables(query);
             octree_->GetDrawables(query);
@@ -1873,7 +1831,10 @@ void View::ProcessShadowCasters(LightQueryResult& query, const PODVector<Drawabl
         // Check for that first
         // Check for that first
         if (!drawable->GetCastShadows())
         if (!drawable->GetCastShadows())
             continue;
             continue;
-        // For point light, check that this drawable is inside the split shadow camera frustum
+        // Check shadow mask
+        if (!(GetShadowMask(drawable) & light->GetLightMask()))
+            continue;
+       // For point light, check that this drawable is inside the split shadow camera frustum
         if (type == LIGHT_POINT && shadowCameraFrustum.IsInsideFast(drawable->GetWorldBoundingBox()) == OUTSIDE)
         if (type == LIGHT_POINT && shadowCameraFrustum.IsInsideFast(drawable->GetWorldBoundingBox()) == OUTSIDE)
             continue;
             continue;
         
         
@@ -1891,10 +1852,6 @@ void View::ProcessShadowCasters(LightQueryResult& query, const PODVector<Drawabl
         if (maxShadowDistance > 0.0f && drawable->GetDistance() > maxShadowDistance)
         if (maxShadowDistance > 0.0f && drawable->GetDistance() > maxShadowDistance)
             continue;
             continue;
         
         
-        // Check shadow mask
-        if (!(GetShadowMask(drawable) & light->GetLightMask()))
-            continue;
-        
         // Project shadow caster bounding box to light view space for visibility check
         // Project shadow caster bounding box to light view space for visibility check
         lightViewBox = drawable->GetWorldBoundingBox().Transformed(lightView);
         lightViewBox = drawable->GetWorldBoundingBox().Transformed(lightView);
         
         
@@ -1922,7 +1879,7 @@ bool View::IsShadowCasterVisible(Drawable* drawable, BoundingBox lightViewBox, C
     {
     {
         // Extrude the light space bounding box up to the far edge of the frustum's light space bounding box
         // Extrude the light space bounding box up to the far edge of the frustum's light space bounding box
         lightViewBox.max_.z_ = Max(lightViewBox.max_.z_,lightViewFrustumBox.max_.z_);
         lightViewBox.max_.z_ = Max(lightViewBox.max_.z_,lightViewFrustumBox.max_.z_);
-        return lightViewFrustum.IsInsideFast(lightViewBox) != OUTSIDE;
+        return lightViewFrustum.IsInsideFast(lightViewBox);
     }
     }
     else
     else
     {
     {
@@ -1947,7 +1904,7 @@ bool View::IsShadowCasterVisible(Drawable* drawable, BoundingBox lightViewBox, C
         BoundingBox extrudedBox(newCenter - newHalfSize, newCenter + newHalfSize);
         BoundingBox extrudedBox(newCenter - newHalfSize, newCenter + newHalfSize);
         lightViewBox.Merge(extrudedBox);
         lightViewBox.Merge(extrudedBox);
         
         
-        return lightViewFrustum.IsInsideFast(lightViewBox) != OUTSIDE;
+        return lightViewFrustum.IsInsideFast(lightViewBox);
     }
     }
 }
 }
 
 
@@ -1983,72 +1940,77 @@ void View::SetupShadowCameras(LightQueryResult& query)
 {
 {
     Light* light = query.light_;
     Light* light = query.light_;
     
     
-    LightType type = light->GetLightType();
     int splits = 0;
     int splits = 0;
     
     
-    if (type == LIGHT_DIRECTIONAL)
+    switch (light->GetLightType())
     {
     {
-        const CascadeParameters& cascade = light->GetShadowCascade();
-        
-        float nearSplit = camera_->GetNearClip();
-        float farSplit;
-        
-        while (splits < renderer_->GetMaxShadowCascades())
+    case LIGHT_DIRECTIONAL:
         {
         {
-            // If split is completely beyond camera far clip, we are done
-            if (nearSplit > camera_->GetFarClip())
-                break;
+            const CascadeParameters& cascade = light->GetShadowCascade();
             
             
-            farSplit = Min(camera_->GetFarClip(), cascade.splits_[splits]);
-            if (farSplit <= nearSplit)
-                break;
+            float nearSplit = camera_->GetNearClip();
+            float farSplit;
             
             
-            // Setup the shadow camera for the split
-            Camera* shadowCamera = renderer_->GetShadowCamera();
-            query.shadowCameras_[splits] = shadowCamera;
-            query.shadowNearSplits_[splits] = nearSplit;
-            query.shadowFarSplits_[splits] = farSplit;
-            SetupDirLightShadowCamera(shadowCamera, light, nearSplit, farSplit);
-            
-            nearSplit = farSplit;
-            ++splits;
+            while (splits < renderer_->GetMaxShadowCascades())
+            {
+                // If split is completely beyond camera far clip, we are done
+                if (nearSplit > camera_->GetFarClip())
+                    break;
+                
+                farSplit = Min(camera_->GetFarClip(), cascade.splits_[splits]);
+                if (farSplit <= nearSplit)
+                    break;
+                
+                // Setup the shadow camera for the split
+                Camera* shadowCamera = renderer_->GetShadowCamera();
+                query.shadowCameras_[splits] = shadowCamera;
+                query.shadowNearSplits_[splits] = nearSplit;
+                query.shadowFarSplits_[splits] = farSplit;
+                SetupDirLightShadowCamera(shadowCamera, light, nearSplit, farSplit);
+                
+                nearSplit = farSplit;
+                ++splits;
+            }
         }
         }
-    }
-    
-    if (type == LIGHT_SPOT)
-    {
-        Camera* shadowCamera = renderer_->GetShadowCamera();
-        query.shadowCameras_[0] = shadowCamera;
-        Node* cameraNode = shadowCamera->GetNode();
-        Node* lightNode = light->GetNode();
-        
-        cameraNode->SetTransform(lightNode->GetWorldPosition(), lightNode->GetWorldRotation());
-        shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
-        shadowCamera->SetFarClip(light->GetRange());
-        shadowCamera->SetFov(light->GetFov());
-        shadowCamera->SetAspectRatio(light->GetAspectRatio());
-        
-        splits = 1;
-    }
+        break;
     
     
-    if (type == LIGHT_POINT)
-    {
-        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    case LIGHT_SPOT:
         {
         {
             Camera* shadowCamera = renderer_->GetShadowCamera();
             Camera* shadowCamera = renderer_->GetShadowCamera();
-            query.shadowCameras_[i] = shadowCamera;
+            query.shadowCameras_[0] = shadowCamera;
             Node* cameraNode = shadowCamera->GetNode();
             Node* cameraNode = shadowCamera->GetNode();
+            Node* lightNode = light->GetNode();
             
             
-            // When making a shadowed point light, align the splits along X, Y and Z axes regardless of light rotation
-            cameraNode->SetPosition(light->GetNode()->GetWorldPosition());
-            cameraNode->SetDirection(directions[i]);
+            cameraNode->SetTransform(lightNode->GetWorldPosition(), lightNode->GetWorldRotation());
             shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
             shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
             shadowCamera->SetFarClip(light->GetRange());
             shadowCamera->SetFarClip(light->GetRange());
-            shadowCamera->SetFov(90.0f);
-            shadowCamera->SetAspectRatio(1.0f);
+            shadowCamera->SetFov(light->GetFov());
+            shadowCamera->SetAspectRatio(light->GetAspectRatio());
+            
+            splits = 1;
         }
         }
-        
-        splits = MAX_CUBEMAP_FACES;
+        break;
+    
+    case LIGHT_POINT:
+        {
+            for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+            {
+                Camera* shadowCamera = renderer_->GetShadowCamera();
+                query.shadowCameras_[i] = shadowCamera;
+                Node* cameraNode = shadowCamera->GetNode();
+                
+                // When making a shadowed point light, align the splits along X, Y and Z axes regardless of light rotation
+                cameraNode->SetPosition(light->GetNode()->GetWorldPosition());
+                cameraNode->SetDirection(directions[i]);
+                shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
+                shadowCamera->SetFarClip(light->GetRange());
+                shadowCamera->SetFov(90.0f);
+                shadowCamera->SetAspectRatio(1.0f);
+            }
+            
+            splits = MAX_CUBEMAP_FACES;
+        }
+        break;
     }
     }
     
     
     query.numSplits_ = splits;
     query.numSplits_ = splits;
@@ -2062,8 +2024,7 @@ void View::SetupDirLightShadowCamera(Camera* shadowCamera, Light* light, float n
     const FocusParameters& parameters = light->GetShadowFocus();
     const FocusParameters& parameters = light->GetShadowFocus();
     
     
     // Calculate initial position & rotation
     // Calculate initial position & rotation
-    Vector3 lightWorldDirection = lightNode->GetWorldRotation() * Vector3::FORWARD;
-    Vector3 pos = cameraNode_->GetWorldPosition() - extrusionDistance * lightWorldDirection;
+    Vector3 pos = cameraNode_->GetWorldPosition() - extrusionDistance * lightNode->GetWorldDirection();
     shadowCameraNode->SetTransform(pos, lightNode->GetWorldRotation());
     shadowCameraNode->SetTransform(pos, lightNode->GetWorldRotation());
     
     
     // Calculate main camera shadowed frustum in light's view space
     // Calculate main camera shadowed frustum in light's view space
@@ -2240,8 +2201,8 @@ void View::FindZone(Drawable* drawable)
     
     
     // First check if the last zone remains a conclusive result
     // First check if the last zone remains a conclusive result
     Zone* lastZone = drawable->GetLastZone();
     Zone* lastZone = drawable->GetLastZone();
-    if (lastZone && lastZone->IsInside(center) && (drawable->GetZoneMask() & lastZone->GetZoneMask()) &&
-        lastZone->GetPriority() >= highestZonePriority_)
+    if (lastZone && lastZone->GetPriority() >= highestZonePriority_ &&
+        (drawable->GetZoneMask() & lastZone->GetZoneMask()) && lastZone->IsInside(center))
         newZone = lastZone;
         newZone = lastZone;
     else
     else
     {
     {
@@ -2249,7 +2210,7 @@ void View::FindZone(Drawable* drawable)
         {
         {
             Zone* zone = *i;
             Zone* zone = *i;
             int priority = zone->GetPriority();
             int priority = zone->GetPriority();
-            if (zone->IsInside(center) && (drawable->GetZoneMask() & zone->GetZoneMask()) && priority > bestPriority)
+            if (priority > bestPriority && (drawable->GetZoneMask() & zone->GetZoneMask()) && zone->IsInside(center))
             {
             {
                 newZone = zone;
                 newZone = zone;
                 bestPriority = priority;
                 bestPriority = priority;

+ 0 - 8
Engine/Graphics/View.h

@@ -145,14 +145,6 @@ private:
     bool IsShadowCasterVisible(Drawable* drawable, BoundingBox lightViewBox, Camera* shadowCamera, const Matrix3x4& lightView, const Frustum& lightViewFrustum, const BoundingBox& lightViewFrustumBox);
     bool IsShadowCasterVisible(Drawable* drawable, BoundingBox lightViewBox, Camera* shadowCamera, const Matrix3x4& lightView, const Frustum& lightViewFrustum, const BoundingBox& lightViewFrustumBox);
     /// Return the viewport for a shadow map split.
     /// Return the viewport for a shadow map split.
     IntRect GetShadowMapViewport(Light* light, unsigned splitIndex, Texture2D* shadowMap);
     IntRect GetShadowMapViewport(Light* light, unsigned splitIndex, Texture2D* shadowMap);
-    /// Optimize light rendering by setting up a scissor rectangle.
-    void OptimizeLightByScissor(Light* light);
-    /// Optimize spot or point light rendering by drawing its volume to the stencil buffer.
-    void OptimizeLightByStencil(Light* light);
-    /// Return scissor rectangle for a light.
-    const Rect& GetLightScissor(Light* light);
-    /// Split directional or point light for shadow rendering.
-    unsigned SplitLight(Light* light);
     /// Find and set a new zone for a drawable when it has moved.
     /// Find and set a new zone for a drawable when it has moved.
     void FindZone(Drawable* drawable);
     void FindZone(Drawable* drawable);
     /// Return the drawable's zone, or camera zone if it has override mode enabled.
     /// Return the drawable's zone, or camera zone if it has override mode enabled.

+ 1 - 1
Engine/Graphics/Viewport.cpp

@@ -108,7 +108,7 @@ void Viewport::InsertPostProcess(unsigned index, PostProcess* effect)
     if (index >= postProcesses_.Size())
     if (index >= postProcesses_.Size())
         postProcesses_.Push(effectPtr);
         postProcesses_.Push(effectPtr);
     else
     else
-        postProcesses_.Insert(postProcesses_.Begin() + index, effectPtr);
+        postProcesses_.Insert(index, effectPtr);
 }
 }
 
 
 void Viewport::RemovePostProcess(PostProcess* effect)
 void Viewport::RemovePostProcess(PostProcess* effect)

+ 7 - 9
Engine/Graphics/Zone.cpp

@@ -44,7 +44,7 @@ static const float DEFAULT_FOG_END = 1000.0f;
 OBJECTTYPESTATIC(Zone);
 OBJECTTYPESTATIC(Zone);
 
 
 Zone::Zone(Context* context) :
 Zone::Zone(Context* context) :
-    Drawable(context),
+    Drawable(context, DRAWABLE_ZONE),
     inverseWorldDirty_(true),
     inverseWorldDirty_(true),
     override_(false),
     override_(false),
     ambientGradient_(false),
     ambientGradient_(false),
@@ -55,7 +55,6 @@ Zone::Zone(Context* context) :
     fogEnd_(DEFAULT_FOG_END),
     fogEnd_(DEFAULT_FOG_END),
     priority_(0)
     priority_(0)
 {
 {
-    drawableFlags_ =  DRAWABLE_ZONE;
 }
 }
 
 
 Zone::~Zone()
 Zone::~Zone()
@@ -85,7 +84,7 @@ void Zone::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
 {
     Component::OnSetAttribute(attr, src);
     Component::OnSetAttribute(attr, src);
     
     
-    // If bounding box, override mode, visibility or priority changes, dirty the drawable as applicable
+    // If bounding box, visibility or priority changes, dirty the drawable as applicable
     if ((attr.offset_ >= offsetof(Zone, boundingBox_) && attr.offset_ < (offsetof(Zone, boundingBox_) + sizeof(BoundingBox))) ||
     if ((attr.offset_ >= offsetof(Zone, boundingBox_) && attr.offset_ < (offsetof(Zone, boundingBox_) + sizeof(BoundingBox))) ||
         attr.offset_ == offsetof(Zone, visible_) || attr.offset_ == offsetof(Zone, priority_))
         attr.offset_ == offsetof(Zone, visible_) || attr.offset_ == offsetof(Zone, priority_))
         OnMarkedDirty(node_);
         OnMarkedDirty(node_);
@@ -154,10 +153,9 @@ void Zone::SetAmbientGradient(bool enable)
 
 
 const Matrix3x4& Zone::GetInverseWorldTransform() const
 const Matrix3x4& Zone::GetInverseWorldTransform() const
 {
 {
-    if (inverseWorldDirty_ && node_)
+    if (inverseWorldDirty_)
     {
     {
-        const Matrix3x4& worldTransform = node_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;
-        inverseWorld_ = worldTransform.Inverse();
+        inverseWorld_ = node_ ? node_->GetWorldTransform().Inverse() : Matrix3x4::IDENTITY;
         inverseWorldDirty_ = false;
         inverseWorldDirty_ = false;
     }
     }
     
     
@@ -190,7 +188,7 @@ bool Zone::IsInside(const Vector3& point) const
 {
 {
     // Use an oriented bounding box test
     // Use an oriented bounding box test
     Vector3 localPoint(GetInverseWorldTransform() * point);
     Vector3 localPoint(GetInverseWorldTransform() * point);
-    return boundingBox_.IsInside(localPoint) != OUTSIDE;
+    return boundingBox_.IsInside(localPoint);
 }
 }
 
 
 void Zone::OnMarkedDirty(Node* node)
 void Zone::OnMarkedDirty(Node* node)
@@ -267,7 +265,7 @@ void Zone::UpdateAmbientGradient()
         {
         {
             Zone* zone = *i;
             Zone* zone = *i;
             int priority = zone->GetPriority();
             int priority = zone->GetPriority();
-            if (zone != this && priority > bestPriority && zone->IsInside(minZPosition))
+            if (priority > bestPriority && zone != this && zone->IsInside(minZPosition))
             {
             {
                 bestZone = zone;
                 bestZone = zone;
                 bestPriority = priority;
                 bestPriority = priority;
@@ -292,7 +290,7 @@ void Zone::UpdateAmbientGradient()
         {
         {
             Zone* zone = *i;
             Zone* zone = *i;
             int priority = zone->GetPriority();
             int priority = zone->GetPriority();
-            if (zone != this && priority > bestPriority && zone->IsInside(maxZPosition))
+            if (priority > bestPriority && zone != this && zone->IsInside(maxZPosition))
             {
             {
                 bestZone = zone;
                 bestZone = zone;
                 bestPriority = priority;
                 bestPriority = priority;

+ 16 - 9
Engine/IO/FileSystem.cpp

@@ -203,7 +203,6 @@ int FileSystem::SystemRun(const String& fileName, const Vector<String>& argument
 
 
 bool FileSystem::SystemOpen(const String& fileName, const String& mode)
 bool FileSystem::SystemOpen(const String& fileName, const String& mode)
 {
 {
-    #ifdef WIN32
     if (allowedPaths_.Empty())
     if (allowedPaths_.Empty())
     {
     {
         if (!FileExists(fileName) && !DirExists(fileName))
         if (!FileExists(fileName) && !DirExists(fileName))
@@ -212,22 +211,27 @@ bool FileSystem::SystemOpen(const String& fileName, const String& mode)
             return false;
             return false;
         }
         }
         
         
+        #ifdef WIN32
         bool success = (int)ShellExecuteW(0, !mode.Empty() ? WString(mode).CString() : 0,
         bool success = (int)ShellExecuteW(0, !mode.Empty() ? WString(mode).CString() : 0,
             GetWideNativePath(fileName).CString(), 0, 0, SW_SHOW) > 32;
             GetWideNativePath(fileName).CString(), 0, 0, SW_SHOW) > 32;
         if (!success)
         if (!success)
             LOGERROR("Failed to open " + fileName + " externally");
             LOGERROR("Failed to open " + fileName + " externally");
         return success;
         return success;
+        #elif defined(__APPLE__)
+        Vector<String> arguments;
+        arguments.Push(fileName);
+        return SystemRun("open", arguments) == 0;
+        #else
+        /// \todo Implement on Unix-like systems
+        LOGERROR("SystemOpen not implemented");
+        return false;
+        #endif
     }
     }
     else
     else
     {
     {
         LOGERROR("Opening a file externally is not allowed");
         LOGERROR("Opening a file externally is not allowed");
         return false;
         return false;
     }
     }
-    #else
-    /// \todo Implement on Unix-like systems
-    LOGERROR("SystemOpen not implemented");
-    return false;
-    #endif
 }
 }
 
 
 bool FileSystem::Copy(const String& srcFileName, const String& destFileName)
 bool FileSystem::Copy(const String& srcFileName, const String& destFileName)
@@ -315,7 +319,7 @@ bool FileSystem::CheckAccess(const String& pathName) const
         return true;
         return true;
     
     
     // If there is any attempt to go to a parent directory, disallow
     // If there is any attempt to go to a parent directory, disallow
-    if (fixedPath.Find("..") != String::NPOS)
+    if (fixedPath.Contains(".."))
         return false;
         return false;
     
     
     // Check if the path is a partial match of any of the allowed directories
     // Check if the path is a partial match of any of the allowed directories
@@ -528,7 +532,7 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
     }
     }
     #else
     #else
     String filterExtension = filter.Substring(filter.Find('.'));
     String filterExtension = filter.Substring(filter.Find('.'));
-    if (filterExtension.Find('*') != String::NPOS)
+    if (filterExtension.Contains('*'))
         filterExtension.Clear();
         filterExtension.Clear();
     DIR *dir;
     DIR *dir;
     struct dirent *de;
     struct dirent *de;
@@ -540,6 +544,9 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
         {
         {
             /// \todo Filename may be unnormalized Unicode on Mac OS X. Re-normalize as necessary
             /// \todo Filename may be unnormalized Unicode on Mac OS X. Re-normalize as necessary
             String fileName(de->d_name);
             String fileName(de->d_name);
+            bool normalEntry = fileName != "." && fileName != "..";
+            if (normalEntry && !(flags & SCAN_HIDDEN) && fileName.StartsWith("."))
+                continue;
             String pathAndName = path + fileName;
             String pathAndName = path + fileName;
             if (!stat(pathAndName.CString(), &st))
             if (!stat(pathAndName.CString(), &st))
             {
             {
@@ -547,7 +554,7 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
                 {
                 {
                     if (flags & SCAN_DIRS)
                     if (flags & SCAN_DIRS)
                         result.Push(deltaPath + fileName);
                         result.Push(deltaPath + fileName);
-                    if (recursive && fileName != "." && fileName != "..")
+                    if (recursive && normalEntry)
                         ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
                         ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
                 }
                 }
                 else if (flags & SCAN_FILES)
                 else if (flags & SCAN_FILES)

+ 6 - 2
Engine/IO/Log.cpp

@@ -55,7 +55,7 @@ OBJECTTYPESTATIC(Log);
 
 
 Log::Log(Context* context) :
 Log::Log(Context* context) :
     Object(context),
     Object(context),
-    #if defined(_DEBUG) || defined(XCODE_DEBUG_CONFIGURATION)
+    #ifdef _DEBUG
     level_(LOG_DEBUG),
     level_(LOG_DEBUG),
     #else
     #else
     level_(LOG_INFO),
     level_(LOG_INFO),
@@ -88,12 +88,14 @@ void Log::Open(const String& fileName)
 
 
 void Log::Write(int level, const String& message)
 void Log::Write(int level, const String& message)
 {
 {
+    assert(level >= LOG_DEBUG && level < LOG_NONE);
+    
     // Prevent recursion
     // Prevent recursion
     if (inWrite_)
     if (inWrite_)
         return;
         return;
     
     
     // Check message level
     // Check message level
-    if (level_ > level || level < LOG_DEBUG || level >= LOG_NONE)
+    if (level_ > level)
         return;
         return;
     
     
     inWrite_ = true;
     inWrite_ = true;
@@ -165,6 +167,8 @@ void Log::WriteRaw(const String& message)
 
 
 void Log::SetLevel(int level)
 void Log::SetLevel(int level)
 {
 {
+    assert(level >= LOG_DEBUG && level < LOG_NONE);
+
     level_ = level;
     level_ = level;
 }
 }
 
 

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini