Sfoglia il codice sorgente

Additional Updates

Josh Engebretson 10 anni fa
parent
commit
e195fa58da
100 ha cambiato i file con 2644 aggiunte e 2078 eliminazioni
  1. 1 1
      CMakeLists.txt
  2. 12 20
      Source/Atomic/Atomic2D/AnimatedSprite2D.cpp
  3. 3 5
      Source/Atomic/Atomic2D/AnimatedSprite2D.h
  4. 17 97
      Source/Atomic/Atomic2D/Drawable2D.cpp
  5. 26 40
      Source/Atomic/Atomic2D/Drawable2D.h
  6. 9 7
      Source/Atomic/Atomic2D/Light2D.cpp
  7. 7 1
      Source/Atomic/Atomic2D/Light2D.h
  8. 64 24
      Source/Atomic/Atomic2D/ParticleEmitter2D.cpp
  9. 13 3
      Source/Atomic/Atomic2D/ParticleEmitter2D.h
  10. 207 210
      Source/Atomic/Atomic2D/Renderer2D.cpp
  11. 41 29
      Source/Atomic/Atomic2D/Renderer2D.h
  12. 56 1
      Source/Atomic/Atomic2D/Sprite2D.cpp
  13. 8 1
      Source/Atomic/Atomic2D/Sprite2D.h
  14. 109 83
      Source/Atomic/Atomic2D/StaticSprite2D.cpp
  15. 23 3
      Source/Atomic/Atomic2D/StaticSprite2D.h
  16. 3 3
      Source/Atomic/Atomic3D/AnimatedModel.cpp
  17. 2 2
      Source/Atomic/Atomic3D/DecalSet.cpp
  18. 2 0
      Source/Atomic/Atomic3D/Model.h
  19. 2 2
      Source/Atomic/Atomic3D/ParticleEmitter.cpp
  20. 10 12
      Source/Atomic/Atomic3D/StaticModel.cpp
  21. 208 244
      Source/Atomic/Graphics/Batch.cpp
  22. 3 1
      Source/Atomic/Graphics/Batch.h
  23. 2 2
      Source/Atomic/Graphics/ConstantBuffer.h
  24. 27 3
      Source/Atomic/Graphics/DebugRenderer.cpp
  25. 5 1
      Source/Atomic/Graphics/DebugRenderer.h
  26. 26 20
      Source/Atomic/Graphics/Drawable.cpp
  27. 22 28
      Source/Atomic/Graphics/Drawable.h
  28. 4 2
      Source/Atomic/Graphics/GPUObject.h
  29. 4 2
      Source/Atomic/Graphics/Graphics.h
  30. 2 15
      Source/Atomic/Graphics/GraphicsDefs.cpp
  31. 25 29
      Source/Atomic/Graphics/GraphicsDefs.h
  32. 0 5
      Source/Atomic/Graphics/GraphicsEvents.h
  33. 4 2
      Source/Atomic/Graphics/GraphicsImpl.h
  34. 4 2
      Source/Atomic/Graphics/IndexBuffer.h
  35. 3 12
      Source/Atomic/Graphics/Light.cpp
  36. 65 24
      Source/Atomic/Graphics/Material.cpp
  37. 16 8
      Source/Atomic/Graphics/Material.h
  38. 1 1
      Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.cpp
  39. 2 2
      Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.h
  40. 322 316
      Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp
  41. 61 42
      Source/Atomic/Graphics/OpenGL/OGLGraphics.h
  42. 5 3
      Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.cpp
  43. 12 4
      Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.h
  44. 4 8
      Source/Atomic/Graphics/OpenGL/OGLIndexBuffer.cpp
  45. 185 11
      Source/Atomic/Graphics/OpenGL/OGLShaderProgram.cpp
  46. 34 2
      Source/Atomic/Graphics/OpenGL/OGLShaderProgram.h
  47. 10 5
      Source/Atomic/Graphics/OpenGL/OGLShaderVariation.cpp
  48. 41 20
      Source/Atomic/Graphics/OpenGL/OGLTexture.cpp
  49. 4 4
      Source/Atomic/Graphics/OpenGL/OGLTexture.h
  50. 11 2
      Source/Atomic/Graphics/OpenGL/OGLTexture2D.cpp
  51. 12 3
      Source/Atomic/Graphics/OpenGL/OGLTexture3D.cpp
  52. 11 2
      Source/Atomic/Graphics/OpenGL/OGLTextureCube.cpp
  53. 5 4
      Source/Atomic/Graphics/OpenGL/OGLVertexBuffer.cpp
  54. 42 14
      Source/Atomic/Graphics/RenderPath.cpp
  55. 16 5
      Source/Atomic/Graphics/RenderPath.h
  56. 4 2
      Source/Atomic/Graphics/RenderSurface.h
  57. 54 56
      Source/Atomic/Graphics/Renderer.cpp
  58. 8 15
      Source/Atomic/Graphics/Renderer.h
  59. 6 2
      Source/Atomic/Graphics/ShaderProgram.h
  60. 4 2
      Source/Atomic/Graphics/ShaderVariation.h
  61. 120 57
      Source/Atomic/Graphics/Technique.cpp
  62. 53 40
      Source/Atomic/Graphics/Technique.h
  63. 4 2
      Source/Atomic/Graphics/Texture.h
  64. 4 2
      Source/Atomic/Graphics/Texture2D.h
  65. 4 2
      Source/Atomic/Graphics/Texture3D.h
  66. 4 2
      Source/Atomic/Graphics/TextureCube.h
  67. 4 2
      Source/Atomic/Graphics/VertexBuffer.h
  68. 4 2
      Source/Atomic/Graphics/VertexDeclaration.h
  69. 252 302
      Source/Atomic/Graphics/View.cpp
  70. 35 23
      Source/Atomic/Graphics/View.h
  71. 1 2
      Source/Atomic/Input/Input.cpp
  72. 5 5
      Source/Atomic/Navigation/CrowdAgent.cpp
  73. 2 2
      Source/Atomic/Navigation/CrowdAgent.h
  74. 3 3
      Source/Atomic/Navigation/DetourCrowdManager.cpp
  75. 2 2
      Source/Atomic/Navigation/DetourCrowdManager.h
  76. 8 8
      Source/Atomic/Navigation/DynamicNavigationMesh.cpp
  77. 2 2
      Source/Atomic/Navigation/DynamicNavigationMesh.h
  78. 1 1
      Source/Atomic/Navigation/NavArea.cpp
  79. 2 2
      Source/Atomic/Navigation/NavArea.h
  80. 7 7
      Source/Atomic/Navigation/NavBuildData.cpp
  81. 3 3
      Source/Atomic/Navigation/NavBuildData.h
  82. 1 1
      Source/Atomic/Navigation/NavigationEvents.h
  83. 1 0
      Source/Atomic/Navigation/NavigationMesh.h
  84. 1 1
      Source/Atomic/Navigation/Obstacle.cpp
  85. 2 2
      Source/Atomic/Navigation/Obstacle.h
  86. 54 37
      Source/Atomic/Network/Connection.cpp
  87. 7 0
      Source/Atomic/Network/Connection.h
  88. 9 1
      Source/Atomic/Scene/ReplicationState.h
  89. 11 1
      Source/Atomic/Scene/SceneEvents.h
  90. 117 10
      Source/Atomic/Scene/Serializable.cpp
  91. 14 8
      Source/Atomic/Scene/Serializable.h
  92. 3 4
      Source/Atomic/UI/UI.cpp
  93. 1 1
      Source/Atomic/UI/UI.h
  94. 2 0
      Source/ThirdParty/CMakeLists.txt
  95. 3 32
      Source/ThirdParty/DetourCrowd/CMakeLists.txt
  96. 2 2
      Source/ThirdParty/DetourCrowd/include/DetourCrowd.h
  97. 1 1
      Source/ThirdParty/DetourCrowd/include/DetourLocalBoundary.h
  98. 1 1
      Source/ThirdParty/DetourCrowd/include/DetourPathCorridor.h
  99. 2 2
      Source/ThirdParty/DetourCrowd/include/DetourPathQueue.h
  100. 3 34
      Source/ThirdParty/DetourTileCache/CMakeLists.txt

+ 1 - 1
CMakeLists.txt

@@ -26,7 +26,7 @@ endif()
 
 if (NOT EMSCRIPTEN)
     add_definitions( -DATOMIC_NETWORK)
-    set (ATOMIC_LINK_LIBRARIES ${ATOMIC_LINK_LIBRARIES} SDL Civetweb Recast Detour kNet )
+    set (ATOMIC_LINK_LIBRARIES ${ATOMIC_LINK_LIBRARIES} SDL Civetweb Recast Detour DetourCrowd DetourTileCache kNet )
 endif()
 
 if (MSVC)

+ 12 - 20
Source/Atomic/Atomic2D/AnimatedSprite2D.cpp

@@ -21,13 +21,13 @@
 //
 
 #include "Precompiled.h"
-#include "../Atomic2D/AnimatedSprite2D.h"
-#include "../Atomic2D/Animation2D.h"
-#include "../Atomic2D/AnimationSet2D.h"
 #include "../Core/Context.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
+#include "../Atomic2D/AnimatedSprite2D.h"
+#include "../Atomic2D/Animation2D.h"
+#include "../Atomic2D/AnimationSet2D.h"
 #include "../Atomic2D/Sprite2D.h"
 #include "../Atomic2D/StaticSprite2D.h"
 
@@ -222,7 +222,7 @@ void AnimatedSprite2D::OnWorldBoundingBoxUpdate()
     boundingBox_ = worldBoundingBox_.Transformed(node_->GetWorldTransform().Inverse());
 }
 
-void AnimatedSprite2D::OnLayerChanged()
+void AnimatedSprite2D::OnDrawOrderChanged()
 {
     for (unsigned i = 0; i < numTracks_; ++i)
     {
@@ -235,18 +235,6 @@ void AnimatedSprite2D::OnLayerChanged()
     }
 }
 
-void AnimatedSprite2D::OnBlendModeChanged()
-{
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        if (!trackNodes_[i])
-            continue;
-
-        StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        staticSprite->SetBlendMode(blendMode_);
-    }
-}
-
 void AnimatedSprite2D::OnFlipChanged()
 {
     for (unsigned i = 0; i < numTracks_; ++i)
@@ -255,16 +243,17 @@ void AnimatedSprite2D::OnFlipChanged()
             continue;
 
         StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        staticSprite->SetFlip(flipX_, flipY_);
+        if (staticSprite)
+            staticSprite->SetFlip(flipX_, flipY_);
     }
 
     // For editor paused mode
     UpdateAnimation(0.0f);
 }
 
-void AnimatedSprite2D::UpdateVertices()
+void AnimatedSprite2D::UpdateSourceBatches()
 {
-    verticesDirty_ = false;
+    sourceBatchesDirty_ = false;
 }
 
 void AnimatedSprite2D::SetAnimation(Animation2D* animation, LoopMode2D loopMode)
@@ -376,7 +365,7 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
     {
         trackNodeInfos_[i].worldSpace = false;
         
-        const AnimationTrack2D& track = animation_->GetTrack(i);        
+        const AnimationTrack2D& track = animation_->GetTrack(i);
         const Vector<AnimationKeyFrame2D>& keyFrames = track.keyFrames_;
 
         // Time out of range
@@ -430,6 +419,9 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
     for (unsigned i = 0; i < numTracks_; ++i)
     {
         Node* node = trackNodes_[i];
+        if (!node)
+            continue;
+
         TrackNodeInfo& nodeInfo = trackNodeInfos_[i];
 
         if (!nodeInfo.value.enabled_)

+ 3 - 5
Source/Atomic/Atomic2D/AnimatedSprite2D.h

@@ -91,12 +91,10 @@ protected:
     virtual void OnNodeSet(Node* node);
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
-    /// Handle layer changed.
-    virtual void OnLayerChanged();
-    /// Handle blend mode changed.
-    virtual void OnBlendModeChanged();
+    /// Handle draw order changed.
+    virtual void OnDrawOrderChanged();
     /// Handle update vertices.
-    virtual void UpdateVertices();
+    virtual void UpdateSourceBatches();
     /// Handle flip changed.
     virtual void OnFlipChanged();
     /// Set animation.

+ 17 - 97
Source/Atomic/Atomic2D/Drawable2D.cpp

@@ -21,13 +21,13 @@
 //
 
 #include "Precompiled.h"
-#include "../Graphics/Camera.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/Drawable2D.h"
+#include "../Graphics/Camera.h"
 #include "../Graphics/Material.h"
-#include "../Atomic2D/Renderer2D.h"
-#include "../Scene/Scene.h"
 #include "../Graphics/Texture2D.h"
+#include "../Scene/Scene.h"
+#include "../Atomic2D/Drawable2D.h"
+#include "../Atomic2D/Renderer2D.h"
 
 #include "../DebugNew.h"
 
@@ -35,14 +35,16 @@ namespace Atomic
 {
 
 const float PIXEL_SIZE = 0.01f;
-extern const char* blendModeNames[];
+
+SourceBatch2D::SourceBatch2D() : drawOrder_(0)
+{
+}
 
 Drawable2D::Drawable2D(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY2D),
     layer_(0),
     orderInLayer_(0),
-    blendMode_(BLEND_ALPHA),
-    verticesDirty_(true)
+    sourceBatchesDirty_(true)
 {
 }
 
@@ -56,8 +58,6 @@ void Drawable2D::RegisterObject(Context* context)
 {
     ACCESSOR_ATTRIBUTE("Layer", GetLayer, SetLayer, int, 0, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Order in Layer", GetOrderInLayer, SetOrderInLayer, int, 0, AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable);
 }
 
 void Drawable2D::OnSetEnabled()
@@ -77,11 +77,7 @@ void Drawable2D::SetLayer(int layer)
 
     layer_ = layer;
 
-    OnLayerChanged();
-
-    if (renderer_)
-        renderer_->MarkOrderDirty();
-
+    OnDrawOrderChanged();
     MarkNetworkUpdate();
 }
 
@@ -92,96 +88,28 @@ void Drawable2D::SetOrderInLayer(int orderInLayer)
 
     orderInLayer_ = orderInLayer;
     
-    OnLayerChanged();
-
-    if (renderer_)
-        renderer_->MarkOrderDirty();
-
-    MarkNetworkUpdate();
-}
-
-void Drawable2D::SetTexture(Texture2D* texture)
-{
-    if (texture == texture_)
-        return;
-
-    texture_ = texture;
-
-    verticesDirty_ = true;
-    material_ = 0;
-    if (renderer_)
-        renderer_->MarkMaterialDirty(this);
-
-    OnMarkedDirty(node_);
-
+    OnDrawOrderChanged();
     MarkNetworkUpdate();
 }
 
-void Drawable2D::SetBlendMode(BlendMode blendMode)
+const Vector<SourceBatch2D>& Drawable2D::GetSourceBatches()
 {
-    if (blendMode == blendMode_)
-        return;
+    if (sourceBatchesDirty_)
+        UpdateSourceBatches();
 
-    blendMode_ = blendMode;
-    material_ = 0;
-    if (renderer_)
-        renderer_->MarkMaterialDirty(this);
-
-    OnBlendModeChanged();
-
-    MarkNetworkUpdate();
-}
-
-void Drawable2D::SetCustomMaterial(Material* customMaterial)
-{
-    if (customMaterial == customMaterial_)
-        return;
-
-    customMaterial_ = customMaterial;
-    material_ = 0;
-    if (renderer_)
-        renderer_->MarkMaterialDirty(this);
-
-    MarkNetworkUpdate();
-}
-
-Texture2D* Drawable2D::GetTexture() const
-{
-    return texture_;
-}
-
-Material* Drawable2D::GetCustomMaterial() const
-{
-    return customMaterial_;
-}
-
-void Drawable2D::SetMaterial(Material* material)
-{
-    material_ = material;
-}
-
-Material* Drawable2D::GetMaterial() const
-{
-    return customMaterial_ ? customMaterial_ : material_;
-}
-
-const Vector<Vertex2D>& Drawable2D::GetVertices()
-{
-    if (verticesDirty_)
-        UpdateVertices();
-    return vertices_;
+    return sourceBatches_;
 }
 
 void Drawable2D::OnNodeSet(Node* node)
 {
     // Do not call Drawable::OnNodeSet(node)
-
     if (node)
     {
         Scene* scene = GetScene();
         if (scene)
         {
             renderer_ = scene->GetOrCreateComponent<Renderer2D>();
+
             if (IsEnabledEffective())
                 renderer_->AddDrawable(this);
         }
@@ -199,15 +127,7 @@ void Drawable2D::OnMarkedDirty(Node* node)
 {
     Drawable::OnMarkedDirty(node);
 
-    verticesDirty_ = true;
-}
-
-void Drawable2D::OnLayerChanged()
-{
-}
-
-void Drawable2D::OnBlendModeChanged()
-{
+    sourceBatchesDirty_ = true;
 }
 
 }

+ 26 - 40
Source/Atomic/Atomic2D/Drawable2D.h

@@ -43,7 +43,19 @@ struct Vertex2D
     Vector2 uv_;
 };
 
-static const unsigned MASK_VERTEX2D = MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1;
+/// 2D source batch.
+struct SourceBatch2D
+{
+    /// Construct.
+    SourceBatch2D();
+
+    /// Draw order.
+    int drawOrder_;
+    /// Material.
+    SharedPtr<Material> material_;
+    /// Vertices.
+    Vector<Vertex2D> vertices_;
+};
 
 /// Pixel size (equal 0.01f).
 extern ATOMIC_API const float PIXEL_SIZE;
@@ -68,61 +80,35 @@ public:
     void SetLayer(int layer);
     /// Set order in layer.
     void SetOrderInLayer(int orderInLayer);
-    /// Set texture.
-    void SetTexture(Texture2D* texture);
-    /// Set blend mode.
-    void SetBlendMode(BlendMode mode);
-    /// Set custom material. 
-    void SetCustomMaterial(Material* customMaterial);
     
     /// Return layer.
     int GetLayer() const { return layer_; }
     /// Return order in layer.
     int GetOrderInLayer() const { return orderInLayer_; }
-    /// Return texture.
-    Texture2D* GetTexture() const;
-    /// Return blend mode.
-    BlendMode GetBlendMode() const { return blendMode_; }
-    /// Return custom material.
-    Material* GetCustomMaterial() const;
 
-    /// Set material (called by Renderer2D).
-    void SetMaterial(Material* material);
-    /// Return custom material or material (called by Renderer2D).
-    Material* GetMaterial() const;
-    /// Return all vertices (called by Renderer2D).
-    const Vector<Vertex2D>& GetVertices();
+    /// Return all source batches (called by Renderer2D).
+    const Vector<SourceBatch2D>& GetSourceBatches();
 
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
     /// Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
-    /// Handle layer changed.
-    virtual void OnLayerChanged();
-    /// Handle blend mode changed.
-    virtual void OnBlendModeChanged();
-    /// Update vertices.
-    virtual void UpdateVertices() = 0;
-    
-
+    /// Handle draw order changed.
+    virtual void OnDrawOrderChanged() = 0;
+    /// Update source batches.
+    virtual void UpdateSourceBatches() = 0;
+    /// Return draw order by layer and order in layer.
+    int GetDrawOrder() const { return (layer_ << 20) + (orderInLayer_ << 10); }
+  
     /// Layer.
     int layer_;
     /// Order in layer.
     int orderInLayer_;
-    /// Texture.
-    SharedPtr<Texture2D> texture_;
-    /// Blend mode.
-    BlendMode blendMode_;
-    /// Custom material.
-    SharedPtr<Material> customMaterial_;
-
-    /// Vertices.
-    Vector<Vertex2D> vertices_;
-    /// Vertices dirty flag.
-    bool verticesDirty_;
-    /// Material.
-    SharedPtr<Material> material_;
+    /// Source batches.
+    Vector<SourceBatch2D> sourceBatches_;
+    /// Source batches dirty flag.
+    bool sourceBatchesDirty_;
     /// Renderer2D.
     WeakPtr<Renderer2D> renderer_;
 };

+ 9 - 7
Source/Atomic/Atomic2D/Light2D.cpp

@@ -452,7 +452,7 @@ void Light2DGroup::OnNodeSet(Node* node)
         if (renderer_.Null())
         {
             renderer_ = node->GetOrCreateComponent<Renderer2D>();
-            renderer_->SetUseTris(true);
+            //renderer_->SetUseTris(true);
         }
 
         if (light2DMaterial_.Null())
@@ -527,7 +527,7 @@ void Light2DGroup::HandleBeginViewUpdate(StringHash eventType, VariantMap& event
 
 void Light2DGroup::HandleBeginRendering(StringHash eventType, VariantMap& eventData)
 {
-    verticesDirty_ = true;
+    //verticesDirty_ = true;
 }
 
 void Light2DGroup::OnWorldBoundingBoxUpdate()
@@ -539,10 +539,10 @@ void Light2DGroup::OnWorldBoundingBoxUpdate()
 void Light2DGroup::UpdateVertices()
 {
     // This is the shadow map
-    if (!verticesDirty_)
-        return;
+    //if (!verticesDirty_)
+    //    return;
 
-    vertices_.Clear();
+    //vertices_.Clear();
 
     for (Vector<WeakPtr<Light2D> >::Iterator itr = lights_.Begin(); itr != lights_.End(); itr++)
     {
@@ -550,10 +550,10 @@ void Light2DGroup::UpdateVertices()
         if (!light->IsEnabled())
             continue;
         light->UpdateVertices();
-        light->AddVertices(vertices_);
+      //  light->AddVertices(vertices_);
     }
 
-    verticesDirty_ = false;
+    //verticesDirty_ = false;
 
 }
 
@@ -653,6 +653,7 @@ void Light2DGroup::CreateLight2DMaterial()
     light2DMaterial_ = new Material(context_);
     light2DMaterial_->SetName("Light2DMaterial");
 
+    /*
     Technique* tech = new Technique(context_);
     Pass* pass = tech->CreatePass(PASS_LIGHT2D);
     pass->SetBlendMode(BLEND_ADDALPHA);
@@ -667,6 +668,7 @@ void Light2DGroup::CreateLight2DMaterial()
     light2DMaterial_->SetCullMode(CULL_NONE);
 
     SetCustomMaterial(light2DMaterial_);
+    */
 
 }
 

+ 7 - 1
Source/Atomic/Atomic2D/Light2D.h

@@ -174,7 +174,7 @@ public:
     void AddLight(Light2D* light);
     Vector<WeakPtr<Light2D> >& GetLights() { return lights_; }
 
-    void SetDirty() { verticesDirty_ = true; }
+    void SetDirty() { /*verticesDirty_ = true;*/ }
 
     void SetAmbientColor(const Color& color);
     const Color& GetAmbientColor() { return ambientColor_; }
@@ -188,6 +188,12 @@ protected:
     void UpdateVertices();
     void OnNodeSet(Node* node);
 
+    /// Handle draw order changed.
+    virtual void OnDrawOrderChanged(){}
+    /// Update source batches.
+    virtual void UpdateSourceBatches(){}
+
+
 private:
 
     Color ambientColor_;

+ 64 - 24
Source/Atomic/Atomic2D/ParticleEmitter2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -21,13 +21,15 @@
 //
 
 #include "Precompiled.h"
-#include "../Graphics/Camera.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ParticleEffect2D.h"
-#include "../Atomic2D/ParticleEmitter2D.h"
+#include "../Graphics/Camera.h"
+#include "../Graphics/Material.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
+#include "../Atomic2D/ParticleEffect2D.h"
+#include "../Atomic2D/ParticleEmitter2D.h"
+#include "../Atomic2D/Renderer2D.h"
 #include "../Atomic2D/Sprite2D.h"
 
 #include "../DebugNew.h"
@@ -36,15 +38,18 @@ namespace Atomic
 {
 
 extern const char* ATOMIC2D_CATEGORY;
+extern const char* blendModeNames[];
 
 ParticleEmitter2D::ParticleEmitter2D(Context* context) :
     Drawable2D(context),
+    blendMode_(BLEND_ADDALPHA),
     numParticles_(0),
     emissionTime_(0.0f),
     emitParticleTime_(0.0f),
     boundingBoxMinPoint_(Vector3::ZERO),
     boundingBoxMaxPoint_(Vector3::ZERO)
 {
+    sourceBatches_.Resize(1);
 }
 
 ParticleEmitter2D::~ParticleEmitter2D()
@@ -56,9 +61,10 @@ void ParticleEmitter2D::RegisterObject(Context* context)
     context->RegisterFactory<ParticleEmitter2D>(ATOMIC2D_CATEGORY);
 
     ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    COPY_BASE_ATTRIBUTES(Drawable2D);
     MIXED_ACCESSOR_ATTRIBUTE("Particle Effect", GetParticleEffectAttr, SetParticleEffectAttr, ResourceRef, ResourceRef(ParticleEffect2D::GetTypeStatic()), AM_DEFAULT);
     MIXED_ACCESSOR_ATTRIBUTE("Sprite ", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()), AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable2D);
+    ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
 }
 
 void ParticleEmitter2D::OnSetEnabled()
@@ -100,8 +106,20 @@ void ParticleEmitter2D::SetSprite(Sprite2D* sprite)
         return;
 
     sprite_ = sprite;
+    UpdateMaterial();
 
-    SetTexture(sprite_ ? sprite_->GetTexture() : 0);
+    MarkNetworkUpdate();
+}
+
+void ParticleEmitter2D::SetBlendMode(BlendMode blendMode)
+{
+    if (blendMode == blendMode_)
+        return;
+
+    blendMode_ = blendMode;
+    UpdateMaterial();
+
+    MarkNetworkUpdate();
 }
 
 void ParticleEmitter2D::SetMaxParticles(unsigned maxParticles)
@@ -109,7 +127,7 @@ void ParticleEmitter2D::SetMaxParticles(unsigned maxParticles)
     maxParticles = Max(maxParticles, 1);
 
     particles_.Resize(maxParticles);
-    vertices_.Reserve(maxParticles * 4);
+    sourceBatches_[0].vertices_.Reserve(maxParticles * 4);
 
     numParticles_ = Min(maxParticles, numParticles_);
 }
@@ -169,30 +187,44 @@ void ParticleEmitter2D::OnWorldBoundingBoxUpdate()
     worldBoundingBox_ = boundingBox_;
 }
 
-void ParticleEmitter2D::UpdateVertices()
+void ParticleEmitter2D::OnDrawOrderChanged()
 {
-    if (!verticesDirty_)
+    sourceBatches_[0].drawOrder_ = GetDrawOrder();
+}
+
+void ParticleEmitter2D::UpdateSourceBatches()
+{
+    if (!sourceBatchesDirty_)
         return;
 
-    vertices_.Clear();
+    Vector<Vertex2D>& vertices = sourceBatches_[0].vertices_;
+    vertices.Clear();
 
-    Texture2D* texture = GetTexture();
-    if (!texture)
+    if (!sprite_)
         return;
 
-    const IntRect& rectangle_ = sprite_->GetRectangle();
-    if (rectangle_.Width() == 0 || rectangle_.Height() == 0)
+    Rect textureRect;
+    if (!sprite_->GetTextureRectangle(textureRect))
         return;
 
+    /*
+    V1---------V2
+    |         / |
+    |       /   |
+    |     /     |
+    |   /       |
+    | /         |
+    V0---------V3
+    */
     Vertex2D vertex0;
     Vertex2D vertex1;
     Vertex2D vertex2;
     Vertex2D vertex3;
 
-    vertex0.uv_ = Vector2(0.0f, 1.0f);
-    vertex1.uv_ = Vector2(0.0f, 0.0f);
-    vertex2.uv_ = Vector2(1.0f, 0.0f);
-    vertex3.uv_ = Vector2(1.0f, 1.0f);
+    vertex0.uv_ = textureRect.min_;
+    vertex1.uv_ = Vector2(textureRect.min_.x_, textureRect.max_.y_);
+    vertex2.uv_ = textureRect.max_;
+    vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
 
     for (int i = 0; i < numParticles_; ++i)
     {
@@ -211,13 +243,21 @@ void ParticleEmitter2D::UpdateVertices()
 
         vertex0.color_ = vertex1.color_ = vertex2.color_  = vertex3.color_ = p.color_.ToUInt();
 
-        vertices_.Push(vertex0);
-        vertices_.Push(vertex1);
-        vertices_.Push(vertex2);
-        vertices_.Push(vertex3);
+        vertices.Push(vertex0);
+        vertices.Push(vertex1);
+        vertices.Push(vertex2);
+        vertices.Push(vertex3);
     }
 
-    verticesDirty_ = false;
+    sourceBatchesDirty_ = false;
+}
+
+void ParticleEmitter2D::UpdateMaterial()
+{
+    if (sprite_)
+        sourceBatches_[0].material_ = renderer_->GetMaterial(sprite_->GetTexture(), blendMode_);
+    else
+        sourceBatches_[0].material_ = 0;
 }
 
 void ParticleEmitter2D::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
@@ -274,7 +314,7 @@ void ParticleEmitter2D::Update(float timeStep)
             emissionTime_ = Max(0.0f, emissionTime_ - timeStep);
     }
 
-    verticesDirty_ = true;
+    sourceBatchesDirty_ = true;
 
     OnMarkedDirty(node_);
 }

+ 13 - 3
Source/Atomic/Atomic2D/ParticleEmitter2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -92,6 +92,8 @@ public:
     void SetEffect(ParticleEffect2D* effect);
     /// Set sprite.
     void SetSprite(Sprite2D* sprite);
+    /// Set blend mode.
+    void SetBlendMode(BlendMode blendMode);
     /// Set max particles.
     void SetMaxParticles(unsigned maxParticles);
 
@@ -99,6 +101,8 @@ public:
     ParticleEffect2D* GetEffect() const;
     /// Return sprite.
     Sprite2D* GetSprite() const;
+    /// Return blend mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
     /// Return max particles.
     unsigned GetMaxParticles() const { return particles_.Size(); }
 
@@ -116,8 +120,12 @@ private:
     virtual void OnNodeSet(Node* node);
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
-    /// Update vertices.
-    virtual void UpdateVertices();
+    /// Handle draw order changed.
+    virtual void OnDrawOrderChanged();
+    /// Update source batches.
+    virtual void UpdateSourceBatches();
+    /// Update material.
+    void UpdateMaterial();
     /// Handle scene post update.
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
     /// Update.
@@ -131,6 +139,8 @@ private:
     SharedPtr<ParticleEffect2D> effect_;
     /// Sprite.
     SharedPtr<Sprite2D> sprite_;
+    /// Blend mode.
+    BlendMode blendMode_;
     /// Num particles.
     int numParticles_;
     /// Emission time.

+ 207 - 210
Source/Atomic/Atomic2D/Renderer2D.cpp

@@ -21,24 +21,25 @@
 //
 
 #include "Precompiled.h"
-#include "../Graphics/Camera.h"
+#include "../Container/Sort.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/Drawable2D.h"
+#include "../Core/Profiler.h"
+#include "../Core/WorkQueue.h"
+#include "../Graphics/Camera.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/GraphicsEvents.h"
 #include "../Graphics/IndexBuffer.h"
-#include "../IO/Log.h"
 #include "../Graphics/Material.h"
-#include "../Scene/Node.h"
-#include "../Core/Profiler.h"
-#include "../Atomic2D/Renderer2D.h"
-#include "../Scene/Scene.h"
-#include "../Container/Sort.h"
+#include "../Graphics/OctreeQuery.h"
 #include "../Graphics/Technique.h"
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../Graphics/View.h"
-#include "../Core/WorkQueue.h"
+#include "../IO/Log.h"
+#include "../Scene/Node.h"
+#include "../Scene/Scene.h"
+#include "../Atomic2D/Drawable2D.h"
+#include "../Atomic2D/Renderer2D.h"
 
 #include "../DebugNew.h"
 
@@ -47,15 +48,33 @@ namespace Atomic
 
 extern const char* blendModeNames[];
 
+static const unsigned MASK_VERTEX2D = MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1;
+
+ViewBatchInfo2D::ViewBatchInfo2D() : vertexBufferUpdateFrameNumber_(0),
+    indexCount_(0),
+    vertexCount_(0),
+    batchUpdatedFrameNumber_(0), 
+    batchCount_(0)
+{
+}
+
 Renderer2D::Renderer2D(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
+    material_(new Material(context)),
     indexBuffer_(new IndexBuffer(context_)),
-    vertexBuffer_(new VertexBuffer(context_)),
-    orderDirty_(true),
-    frustum_(0),
-    useTris_(false),
-    geometryCount_(0)
+    frustum_(0)
 {
+    material_->SetName("Atomic2D");
+
+    Technique* tech = new Technique(context_);
+    Pass* pass = tech->CreatePass("alpha");
+    pass->SetVertexShader("Atomic2D");
+    pass->SetPixelShader("Atomic2D");
+    pass->SetDepthWrite(false);
+
+    material_->SetTechnique(0, tech);
+    material_->SetCullMode(CULL_NONE);
+
     frame_.frameNumber_ = 0;
     SubscribeToEvent(E_BEGINVIEWUPDATE, HANDLER(Renderer2D, HandleBeginViewUpdate));
 }
@@ -69,37 +88,27 @@ void Renderer2D::RegisterObject(Context* context)
     context->RegisterFactory<Renderer2D>();
 }
 
-static inline bool CompareDrawable2Ds(Drawable2D* lhs, Drawable2D* rhs)
+static inline bool CompareRayQueryResults(RayQueryResult& lr, RayQueryResult& rr)
 {
+    Drawable2D* lhs = static_cast<Drawable2D*>(lr.drawable_);
+    Drawable2D* rhs = static_cast<Drawable2D*>(rr.drawable_);
     if (lhs->GetLayer() != rhs->GetLayer())
-        return lhs->GetLayer() < rhs->GetLayer();
+        return lhs->GetLayer() > rhs->GetLayer();
 
     if (lhs->GetOrderInLayer() != rhs->GetOrderInLayer())
-        return lhs->GetOrderInLayer() < rhs->GetOrderInLayer();
-
-    Material* lhsUsedMaterial = lhs->GetMaterial();
-    Material* rhsUsedMaterial = rhs->GetMaterial();
-    if (lhsUsedMaterial && rhsUsedMaterial && lhsUsedMaterial != rhsUsedMaterial)
-        return lhsUsedMaterial->GetNameHash() < rhsUsedMaterial->GetNameHash();
+        return lhs->GetOrderInLayer() > rhs->GetOrderInLayer();
 
-    return lhs->GetID() < rhs->GetID();
+    return lhs->GetID() > rhs->GetID();
 }
 
 void Renderer2D::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 {
-    if (orderDirty_)
-    {
-        Sort(drawables_.Begin(), drawables_.End(), CompareDrawable2Ds);
-        orderDirty_ = false;
-    }
-
     unsigned resultSize = results.Size();
-    for (unsigned i = drawables_.Size() - 1; i < drawables_.Size(); --i)
-    {
+    for (unsigned i = 0; i < drawables_.Size(); ++i)
         drawables_[i]->ProcessRayQuery(query, results);
-        if (results.Size() != resultSize)
-            return;
-    }
+
+    if (results.Size() != resultSize)
+        Sort(results.Begin() + resultSize, results.End(), CompareRayQueryResults);
 }
 
 void Renderer2D::UpdateBatches(const FrameInfo& frame)
@@ -116,36 +125,36 @@ void Renderer2D::UpdateBatches(const FrameInfo& frame)
 
 void Renderer2D::UpdateGeometry(const FrameInfo& frame)
 {
-    unsigned& vertexCount = vertexCount_[frame.camera_];
-    unsigned& indexCount = indexCount_[frame.camera_];
+    unsigned indexCount = 0;
+    for (HashMap<Camera*, ViewBatchInfo2D>::ConstIterator i = viewBatchInfos_.Begin(); i != viewBatchInfos_.End(); ++i)
+    {
+        if (i->second_.batchUpdatedFrameNumber_ == frame_.frameNumber_)
+            indexCount = Max((int)indexCount, (int)i->second_.indexCount_);
+    }
 
     // Fill index buffer
     if (indexBuffer_->GetIndexCount() < indexCount || indexBuffer_->IsDataLost())
     {
-        bool largeIndices = vertexCount > 0xffff;
+        bool largeIndices = (indexCount * 4 / 6) > 0xffff;
         indexBuffer_->SetSize(indexCount, largeIndices);
+
         void* buffer = indexBuffer_->Lock(0, indexCount, true);
         if (buffer)
         {
-            unsigned quadCount =  useTris_ ? indexCount/3 : indexCount / 6;
+            unsigned quadCount = indexCount / 6;
             if (largeIndices)
             {
                 unsigned* dest = reinterpret_cast<unsigned*>(buffer);
                 for (unsigned i = 0; i < quadCount; ++i)
                 {
-                    unsigned base = i * (useTris_ ? 3 : 4);
+                    unsigned base = i * 4;
                     dest[0] = base;
                     dest[1] = base + 1;
                     dest[2] = base + 2;
-                    if (!useTris_)
-                    {
-                        dest[3] = base;
-                        dest[4] = base + 2;
-                        dest[5] = base + 3;
-                        dest += 6;
-                    }
-                    else
-                        dest += 3;
+                    dest[3] = base;
+                    dest[4] = base + 2;
+                    dest[5] = base + 3;
+                    dest += 6;
                 }
             }
             else
@@ -153,19 +162,14 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
                 unsigned short* dest = reinterpret_cast<unsigned short*>(buffer);
                 for (unsigned i = 0; i < quadCount; ++i)
                 {
-                    unsigned base = i * (useTris_ ? 3 : 4);
+                    unsigned base = i * 4;
                     dest[0] = (unsigned short)(base);
                     dest[1] = (unsigned short)(base + 1);
                     dest[2] = (unsigned short)(base + 2);
-                    if (!useTris_)
-                    {
-                        dest[3] = (unsigned short)(base);
-                        dest[4] = (unsigned short)(base + 2);
-                        dest[5] = (unsigned short)(base + 3);
-                        dest += 6;
-                    }
-                    else
-                        dest += 3;
+                    dest[3] = (unsigned short)(base);
+                    dest[4] = (unsigned short)(base + 2);
+                    dest[5] = (unsigned short)(base + 3);
+                    dest += 6;
                 }
             }
 
@@ -178,29 +182,37 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
         }
     }
 
-    if (vertexBuffer_->GetVertexCount() < vertexCount)
-        vertexBuffer_->SetSize(vertexCount, MASK_VERTEX2D);
+    Camera* camera = frame.camera_;
+    ViewBatchInfo2D& viewBatchInfo = viewBatchInfos_[camera];
 
-    if (vertexCount)
-    {
-        Vertex2D* dest = reinterpret_cast<Vertex2D*>(vertexBuffer_->Lock(0, vertexCount, true));
-        if (dest)
+    if (viewBatchInfo.vertexBufferUpdateFrameNumber_ != frame_.frameNumber_)
+    {       
+        unsigned vertexCount = viewBatchInfo.vertexCount_;
+        VertexBuffer* vertexBuffer = viewBatchInfo.vertexBuffer_;
+        if (vertexBuffer->GetVertexCount() < vertexCount)
+            vertexBuffer->SetSize(vertexCount, MASK_VERTEX2D, true);
+
+        if (vertexCount)
         {
-            for (unsigned d = 0; d < drawables_.Size(); ++d)
+            Vertex2D* dest = reinterpret_cast<Vertex2D*>(vertexBuffer->Lock(0, vertexCount, true));
+            if (dest)
             {
-                if (!drawables_[d]->IsInView(frame) || drawables_[d]->GetVertices().Empty())
-                    continue;
+                const PODVector<const SourceBatch2D*>& sourceBatches = viewBatchInfo.sourceBatches_;
+                for (unsigned b = 0; b < sourceBatches.Size(); ++b)
+                {
+                    const Vector<Vertex2D>& vertices = sourceBatches[b]->vertices_;
+                    for (unsigned i = 0; i < vertices.Size(); ++i)
+                        dest[i] = vertices[i];
+                    dest += vertices.Size();
+                }
 
-                const Vector<Vertex2D>& vertices = drawables_[d]->GetVertices();
-                for (unsigned i = 0; i < vertices.Size(); ++i)
-                    dest[i] = vertices[i];
-                dest += vertices.Size();
+                vertexBuffer->Unlock();
             }
-
-            vertexBuffer_->Unlock();
+            else
+                LOGERROR("Failed to lock vertex buffer");
         }
-        else
-            LOGERROR("Failed to lock vertex buffer");
+
+        viewBatchInfo.vertexBufferUpdateFrameNumber_ = frame_.frameNumber_;
     }
 }
 
@@ -215,9 +227,6 @@ void Renderer2D::AddDrawable(Drawable2D* drawable)
         return;
 
     drawables_.Push(drawable);
-    materialDirtyDrawables_.Push(drawable);
-
-    orderDirty_ = true;
 }
 
 void Renderer2D::RemoveDrawable(Drawable2D* drawable)
@@ -226,24 +235,33 @@ void Renderer2D::RemoveDrawable(Drawable2D* drawable)
         return;
 
     drawables_.Remove(drawable);
+}
+
+Material* Renderer2D::GetMaterial(Texture2D* texture, BlendMode blendMode)
+{
+    if (!material_)
+        return 0;
+
+    if (!texture)
+        return material_;
 
-    // Drawable may be on the dirty list multiple times; remove all instances
-    for (;;)
+    HashMap<Texture2D*, HashMap<int, SharedPtr<Material> > >::Iterator t = cachedMaterials_.Find(texture);
+    if (t == cachedMaterials_.End())
     {
-        PODVector<Drawable2D*>::Iterator i = materialDirtyDrawables_.Find(drawable);
-        if (i != materialDirtyDrawables_.End())
-            materialDirtyDrawables_.Erase(i);
-        else
-            break;
+        SharedPtr<Material> newMaterial = CreateMaterial(texture, blendMode);
+        cachedMaterials_[texture][blendMode] = newMaterial;
+        return newMaterial;
     }
 
-    orderDirty_ = true;
-}
+    HashMap<int, SharedPtr<Material> >& materials = t->second_;
+    HashMap<int, SharedPtr<Material> >::Iterator b = materials.Find(blendMode);
+    if (b != materials.End())
+        return b->second_;
 
-void Renderer2D::MarkMaterialDirty(Drawable2D* drawable)
-{
-    // Note: this may cause the drawable to appear on the dirty list multiple times
-    materialDirtyDrawables_.Push(drawable);
+    SharedPtr<Material> newMaterial = CreateMaterial(texture, blendMode);
+    materials[blendMode] = newMaterial;
+
+    return newMaterial;
 }
 
 bool Renderer2D::CheckVisibility(Drawable2D* drawable) const
@@ -262,6 +280,17 @@ void Renderer2D::OnWorldBoundingBoxUpdate()
     worldBoundingBox_ = boundingBox_;
 }
 
+SharedPtr<Material> Renderer2D::CreateMaterial(Texture2D* texture, BlendMode blendMode)
+{
+    SharedPtr<Material> newMaterial = material_->Clone();
+
+    newMaterial->SetName(texture->GetName() + "_" + blendModeNames[blendMode]);
+    newMaterial->SetTexture(TU_DIFFUSE, texture);
+    newMaterial->GetTechnique(0)->GetPass("alpha")->SetBlendMode(blendMode);
+
+    return newMaterial;
+}
+
 void CheckDrawableVisibility(const WorkItem* item, unsigned threadIndex)
 {
     Renderer2D* renderer = reinterpret_cast<Renderer2D*>(item->aux_);
@@ -271,7 +300,7 @@ void CheckDrawableVisibility(const WorkItem* item, unsigned threadIndex)
     while (start != end)
     {
         Drawable2D* drawable = *start++;
-        if (renderer->CheckVisibility(drawable) && drawable->GetVertices().Size())
+        if (renderer->CheckVisibility(drawable))
             drawable->MarkInView(renderer->frame_);
     }
 }
@@ -280,35 +309,14 @@ void Renderer2D::HandleBeginViewUpdate(StringHash eventType, VariantMap& eventDa
 {
     using namespace BeginViewUpdate;
 
-    Scene* scene = GetScene();
     // Check that we are updating the correct scene
-    if (scene != eventData[P_SCENE].GetPtr())
+    if (GetScene() != eventData[P_SCENE].GetPtr())
         return;
-    unsigned lastFrameNumber = frame_.frameNumber_;
+
     frame_ = static_cast<View*>(eventData[P_VIEW].GetPtr())->GetFrameInfo();
-    // Reset geometry use when new frame started
-    if (frame_.frameNumber_ != lastFrameNumber)
-        geometryCount_ = 0;
 
     PROFILE(UpdateRenderer2D);
 
-    if (!materialDirtyDrawables_.Empty())
-    {
-        for (unsigned i = 0; i < materialDirtyDrawables_.Size(); ++i)
-        {
-            Drawable2D* drawable = materialDirtyDrawables_[i];
-            if (!drawable->GetMaterial())
-                drawable->SetMaterial(GetMaterial(drawable->GetTexture(), drawable->GetBlendMode()));
-        }
-        materialDirtyDrawables_.Clear();
-    }
-
-    if (orderDirty_)
-    {
-        Sort(drawables_.Begin(), drawables_.End(), CompareDrawable2Ds);
-        orderDirty_ = false;
-    }
-
     Camera* camera = static_cast<Camera*>(eventData[P_CAMERA].GetPtr());
     frustum_ = &camera->GetFrustum();
     if (camera->IsOrthographic() && camera->GetNode()->GetWorldDirection() == Vector3::FORWARD)
@@ -348,59 +356,24 @@ void Renderer2D::HandleBeginViewUpdate(StringHash eventType, VariantMap& eventDa
         queue->Complete(M_MAX_UNSIGNED);
     }
 
+    ViewBatchInfo2D& viewBatchInfo = viewBatchInfos_[camera];
+    
+    // Create vertex buffer
+    if (!viewBatchInfo.vertexBuffer_)
+        viewBatchInfo.vertexBuffer_ = new VertexBuffer(context_);
+
+    UpdateViewBatchInfo(viewBatchInfo, camera);
+
     // Go through the drawables to form geometries & batches and calculate the total vertex / index count,
     // but upload the actual vertex data later. The idea is that the View class copies our batch vector to
     // its internal data structures, so we can reuse the batches for each view, provided that unique Geometry
     // objects are used for each view to specify the draw ranges
-    batches_.Clear();
-    Material* material = 0;
-    unsigned iStart = 0;
-    unsigned iCount = 0;
-    unsigned vStart = 0;
-    unsigned vCount = 0;
-    unsigned& vTotal = vertexCount_[frame_.camera_];
-    unsigned& iTotal = indexCount_[frame_.camera_];
-    vTotal = 0;
-    iTotal = 0;
-
-    for (unsigned d = 0; d < drawables_.Size(); ++d)
+    batches_.Resize(viewBatchInfo.batchCount_);
+    for (unsigned i = 0; i < viewBatchInfo.batchCount_; ++i)
     {
-        if (!drawables_[d]->IsInView(frame_))
-            continue;
-
-        Material* usedMaterial = drawables_[d]->GetMaterial();
-        const Vector<Vertex2D>& vertices = drawables_[d]->GetVertices();
-
-        // When new material encountered, finish the current batch and start new
-        if (material != usedMaterial)
-        {
-            if (material)
-            {
-                AddBatch(material, iStart, iCount, vStart, vCount);
-                iStart += iCount;
-                iCount = 0;
-                vStart += vCount;
-                vCount = 0;
-            }
-
-            material = usedMaterial;
-        }
-
-        unsigned indices;
-        if (useTris_)
-            indices = vertices.Size();
-        else
-            indices = vertices.Size() / 4 * 6;
-
-        iCount += indices;
-        iTotal += indices;
-        vCount += vertices.Size();
-        vTotal += vertices.Size();
+        batches_[i].material_ = viewBatchInfo.materials_[i];
+        batches_[i].geometry_ = viewBatchInfo.geometries_[i];
     }
-
-    // Add the final batch if necessary
-    if (material && vCount)
-        AddBatch(material, iStart, iCount, vStart, vCount);
 }
 
 void Renderer2D::GetDrawables(PODVector<Drawable2D*>& dest, Node* node)
@@ -421,79 +394,103 @@ void Renderer2D::GetDrawables(PODVector<Drawable2D*>& dest, Node* node)
         GetDrawables(dest, i->Get());
 }
 
-Material* Renderer2D::GetMaterial(Texture2D* texture, BlendMode blendMode)
+static inline bool CompareSourceBatch2Ds(const SourceBatch2D* lhs, const SourceBatch2D* rhs)
 {
-    HashMap<Texture2D*, HashMap<int, SharedPtr<Material> > >::Iterator t = cachedMaterials_.Find(texture);
-    if (t == cachedMaterials_.End())
-    {
-        SharedPtr<Material> material(CreateMaterial(texture, blendMode));
-        cachedMaterials_[texture][blendMode] = material;
-        return material;
-    }
-
-    HashMap<int, SharedPtr<Material> >& materials = t->second_;
-    HashMap<int, SharedPtr<Material> >::Iterator b = materials.Find(blendMode);
-    if (b != materials.End())
-        return b->second_;
+    if (lhs->drawOrder_ != rhs->drawOrder_)
+        return lhs->drawOrder_ < rhs->drawOrder_;
 
-    SharedPtr<Material> material(CreateMaterial(texture, blendMode));
-    materials[blendMode] = material;
+    if (lhs->material_ != rhs->material_)
+        return lhs->material_->GetNameHash() < rhs->material_->GetNameHash();
 
-    return material;
+    return lhs < rhs;
 }
 
-Material* Renderer2D::CreateMaterial(Texture2D* texture, BlendMode blendMode)
+void Renderer2D::UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* camera)
 {
-    Material* material = new Material(context_);
-    if (texture)
-        material->SetName(texture->GetName() + "_" + blendModeNames[blendMode]);
-    else
-        material->SetName(blendModeNames[blendMode]);
+    // Already update in same frame
+    if (viewBatchInfo.batchUpdatedFrameNumber_ == frame_.frameNumber_)
+        return;
 
-    Technique* tech = new Technique(context_);
-    Pass* pass = tech->CreatePass(PASS_ALPHA);
-    pass->SetBlendMode(blendMode);
+    PODVector<const SourceBatch2D*>& soruceBatches = viewBatchInfo.sourceBatches_;
+    soruceBatches.Clear();
+    for (unsigned d = 0; d < drawables_.Size(); ++d)
+    {
+        if (!drawables_[d]->IsInView(camera))
+            continue;
 
-    pass->SetVertexShader("Atomic2D");
-    pass->SetPixelShader("Atomic2D");
+        const Vector<SourceBatch2D>& batches = drawables_[d]->GetSourceBatches();
+        for (unsigned b = 0; b < batches.Size(); ++b)
+        {
+            if (batches[b].material_ && !batches[b].vertices_.Empty())
+                soruceBatches.Push(&batches[b]);
+        }
+    }
 
-    pass->SetDepthWrite(false);
+    Sort(soruceBatches.Begin(), soruceBatches.End(), CompareSourceBatch2Ds);
+
+    viewBatchInfo.batchCount_ = 0;
+    Material* currMaterial = 0;
+    unsigned iStart = 0;
+    unsigned iCount = 0;
+    unsigned vStart = 0;
+    unsigned vCount = 0;
+
+    for (unsigned b = 0; b < soruceBatches.Size(); ++b)
+    {
+        Material* material = soruceBatches[b]->material_;
+        const Vector<Vertex2D>& vertices = soruceBatches[b]->vertices_;
+
+        // When new material encountered, finish the current batch and start new
+        if (currMaterial != material)
+        {
+            if (currMaterial)
+            {
+                AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount);
+                iStart += iCount;
+                iCount = 0;
+                vStart += vCount;
+                vCount = 0;
+            }
 
-    material->SetTechnique(0, tech);
-    material->SetCullMode(CULL_NONE);
+            currMaterial = material;
+        }
 
-    material->SetTexture(TU_DIFFUSE, texture);
+        iCount += vertices.Size() * 6 / 4;
+        vCount += vertices.Size();
+    }
+
+    // Add the final batch if necessary
+    if (currMaterial && vCount)
+        AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount);
 
-    return material;
+    viewBatchInfo.indexCount_ = iStart + iCount;
+    viewBatchInfo.vertexCount_ = vStart + vCount;
+    viewBatchInfo.batchUpdatedFrameNumber_ = frame_.frameNumber_;
 }
 
-void Renderer2D::AddBatch(Material* material, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount)
+void Renderer2D::AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount)
 {
     if (!material || indexCount == 0 || vertexCount == 0)
         return;
 
+    if (viewBatchInfo.materials_.Size() <= viewBatchInfo.batchCount_)
+        viewBatchInfo.materials_.Resize(viewBatchInfo.batchCount_ + 1);
+    viewBatchInfo.materials_[viewBatchInfo.batchCount_] = material;
+
     // Allocate new geometry if necessary
-    if (geometries_.Size() <= geometryCount_)
+    if (viewBatchInfo.geometries_.Size() <= viewBatchInfo.batchCount_)
     {
         SharedPtr<Geometry> geometry(new Geometry(context_));
         geometry->SetIndexBuffer(indexBuffer_);
-        geometry->SetVertexBuffer(0, vertexBuffer_, MASK_VERTEX2D);
-        geometries_.Push(geometry);
-    }
+        geometry->SetVertexBuffer(0, viewBatchInfo.vertexBuffer_, MASK_VERTEX2D);
 
-    geometries_[geometryCount_]->SetDrawRange(TRIANGLE_LIST, indexStart, indexCount, vertexStart, vertexCount, false);
-
-    batches_.Resize(batches_.Size() + 1);
-    SourceBatch& batch = batches_.Back();
-    batch.geometry_ = geometries_[geometryCount_];
-    batch.material_ = material;
+        viewBatchInfo.geometries_.Push(geometry);
+    }
 
-    ++geometryCount_;
-}
+    Geometry* geometry = viewBatchInfo.geometries_[viewBatchInfo.batchCount_];
+    geometry->SetDrawRange(TRIANGLE_LIST, indexStart, indexCount, vertexStart, vertexCount, false);
 
-void Renderer2D::SetUseTris(bool tris)
-{
-    useTris_ = tris;
+    viewBatchInfo.batchCount_++;
 }
 
 }

+ 41 - 29
Source/Atomic/Atomic2D/Renderer2D.h

@@ -32,6 +32,33 @@ class IndexBuffer;
 class Material;
 class VertexBuffer;
 struct FrameInfo;
+struct SourceBatch2D;
+
+/// 2D view batch info.
+struct ViewBatchInfo2D
+{
+    /// Construct.
+    ViewBatchInfo2D();
+
+    /// Vertex buffer update frame number.
+    unsigned vertexBufferUpdateFrameNumber_;
+    /// Index count.
+    unsigned indexCount_;
+    /// Vertex count.
+    unsigned vertexCount_;
+    /// Vertex buffer.
+    SharedPtr<VertexBuffer> vertexBuffer_;
+    /// Batch updated frame number.
+    unsigned batchUpdatedFrameNumber_;
+    /// Source batches.
+    PODVector<const SourceBatch2D*> sourceBatches_;
+    /// Batch count;
+    unsigned batchCount_;
+    /// Materials.
+    Vector<SharedPtr<Material> > materials_;
+    /// Geometries.
+    Vector<SharedPtr<Geometry> > geometries_;
+};
 
 /// 2D renderer components.
 class ATOMIC_API Renderer2D : public Drawable
@@ -61,57 +88,42 @@ public:
     void AddDrawable(Drawable2D* drawable);
     /// Remove Drawable2D.
     void RemoveDrawable(Drawable2D* drawable);
-    /// Mark material dirty.
-    void MarkMaterialDirty(Drawable2D* drawable);
-    /// Mark order dirty.
-    void MarkOrderDirty(){ orderDirty_ = true; }
+    /// Return material by texture and blend mode.
+    Material* GetMaterial(Texture2D* texture, BlendMode blendMode);
+
     /// Check visibility.
     bool CheckVisibility(Drawable2D* drawable) const;
 
-    void SetUseTris(bool tris);
-
 private:
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
+    /// Create material by texture and blend mode.
+    SharedPtr<Material> CreateMaterial(Texture2D* texture, BlendMode blendMode);
     /// Handle view update begin event. Determine Drawable2D's and their batches here.
     void HandleBeginViewUpdate(StringHash eventType, VariantMap& eventData);
     /// Get all drawables in node.
     void GetDrawables(PODVector<Drawable2D*>& drawables, Node* node);
-    /// Return material by texture and blend mode.
-    Material* GetMaterial(Texture2D* texture, BlendMode blendMode);
-    /// Create new material by texture and blend mode.
-    Material* CreateMaterial(Texture2D* Texture, BlendMode blendMode);
-    /// Add batch.
-    void AddBatch(Material* material, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount);
+    /// Update view batch info.
+    void UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* camera);
+    /// Add view batch.
+    void AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount);
 
     /// Index buffer.
     SharedPtr<IndexBuffer> indexBuffer_;
-    /// Vertex buffer.
-    SharedPtr<VertexBuffer> vertexBuffer_;
+    /// Material.
+    SharedPtr<Material> material_;
     /// Drawables.
     PODVector<Drawable2D*> drawables_;
-    /// Material dirty drawables.
-    PODVector<Drawable2D*> materialDirtyDrawables_;
-    /// Order dirty.
-    bool orderDirty_;
-    /// View frameinfo for current frame.
+    /// View frame info for current frame.
     FrameInfo frame_;
-    /// Used geometry count. Shared by all views and reset when a new frame begins.
-    unsigned geometryCount_;
-    /// Vertex count by view.
-    HashMap<Camera*, unsigned> vertexCount_;
-    /// Index count by view.
-    HashMap<Camera*, unsigned> indexCount_;
-    /// Geometries used in all views.
-    Vector<SharedPtr<Geometry> > geometries_;
+    /// View batch info.
+    HashMap<Camera*, ViewBatchInfo2D> viewBatchInfos_;
     /// Frustum for current frame.
     const Frustum* frustum_;
     /// Frustum bounding box for current frame.
     BoundingBox frustumBoundingBox_;
     /// Cached materials.
     HashMap<Texture2D*, HashMap<int, SharedPtr<Material> > > cachedMaterials_;
-
-    bool useTris_;
 };
 
 }

+ 56 - 1
Source/Atomic/Atomic2D/Sprite2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,7 @@
 #include "../Core/Context.h"
 #include "../IO/Deserializer.h"
 #include "../Resource/ResourceCache.h"
+#include "../Atomic2D/Drawable2D.h"
 #include "../Atomic2D/Sprite2D.h"
 #include "../Atomic2D/SpriteSheet2D.h"
 #include "../Graphics/Texture2D.h"
@@ -126,6 +127,60 @@ void Sprite2D::SetSpriteSheet(SpriteSheet2D* spriteSheet)
     spriteSheet_ = spriteSheet;
 }
 
+bool Sprite2D::GetDrawRectangle(Rect& rect, bool flipX, bool flipY) const
+{
+    return GetDrawRectangle(rect, hotSpot_, flipX, flipY);
+}
+
+bool Sprite2D::GetDrawRectangle(Rect& rect, const Vector2& hotSpot, bool flipX, bool flipY) const
+{
+    if (rectangle_.Width() == 0 || rectangle_.Height() == 0)
+        return false;
+
+    float width = (float)rectangle_.Width() * PIXEL_SIZE;
+    float height = (float)rectangle_.Height() * PIXEL_SIZE;        
+
+    float hotSpotX = flipX ? (1.0f - hotSpot.x_) : hotSpot.x_;
+    float hotSpotY = flipY ? (1.0f - hotSpot.y_) : hotSpot.y_;
+
+#ifdef ATOMIC_OPENGL
+    rect.min_.x_ = -width * hotSpotX;
+    rect.max_.x_ = width * (1.0f - hotSpotX);
+    rect.min_.y_ = -height * hotSpotY;
+    rect.max_.y_ = height * (1.0f - hotSpotY);
+#else
+    const float halfPixelOffset = 0.5f * PIXEL_SIZE;
+    rect.min_.x_ = -width * hotSpotX + halfPixelOffset;
+    rect.max_.x_ = width * (1.0f - hotSpotX) + halfPixelOffset;
+    rect.min_.y_ = -height * hotSpotY + halfPixelOffset;
+    rect.max_.y_ = height * (1.0f - hotSpotY) + halfPixelOffset;
+#endif
+    return true;
+}
+
+bool Sprite2D::GetTextureRectangle(Rect& rect, bool flipX, bool flipY) const
+{
+    if (!texture_)
+        return false;
+
+    float invWidth = 1.0f / (float)texture_->GetWidth();
+    float invHeight = 1.0f / (float)texture_->GetHeight();
+
+    rect.min_.x_ = rectangle_.left_ * invWidth;
+    rect.max_.x_ = rectangle_.right_ * invWidth;
+
+    rect.min_.y_ = rectangle_.bottom_ * invHeight;
+    rect.max_.y_ = rectangle_.top_ * invHeight;
+
+    if (flipX)
+        Swap(rect.min_.x_, rect.max_.x_);
+    
+    if (flipY)
+        Swap(rect.min_.y_, rect.max_.y_);
+
+    return true;
+}
+
 ResourceRef Sprite2D::SaveToResourceRef(Sprite2D* sprite)
 {
     SpriteSheet2D* spriteSheet = 0;

+ 8 - 1
Source/Atomic/Atomic2D/Sprite2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -70,6 +70,13 @@ public:
     /// Return sprite sheet.
     SpriteSheet2D* GetSpriteSheet() const { return spriteSheet_; }
 
+    /// Return draw rectangle.
+    bool GetDrawRectangle(Rect& rect, bool flipX = false, bool flipY = false) const;
+    /// Return draw rectangle with custom hot spot.
+    bool GetDrawRectangle(Rect& rect, const Vector2& hotSpot, bool flipX = false, bool flipY = false) const;
+    /// Return texture rectangle.
+    bool GetTextureRectangle(Rect& rect, bool flipX = false, bool flipY = false) const;
+
     /// Save sprite to ResourceRef.
     static ResourceRef SaveToResourceRef(Sprite2D* sprite);
     /// Load sprite from ResourceRef.

+ 109 - 83
Source/Atomic/Atomic2D/StaticSprite2D.cpp

@@ -22,10 +22,13 @@
 
 #include "Precompiled.h"
 #include "../Core/Context.h"
+#include "../Graphics/Material.h"
+#include "../Graphics/Texture2D.h"
+#include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
+#include "../Atomic2D/Renderer2D.h"
 #include "../Atomic2D/Sprite2D.h"
 #include "../Atomic2D/StaticSprite2D.h"
-#include "../Graphics/Texture2D.h"
 
 #include "../DebugNew.h"
 
@@ -33,16 +36,18 @@ namespace Atomic
 {
 
 extern const char* ATOMIC2D_CATEGORY;
+extern const char* blendModeNames[];
 
 StaticSprite2D::StaticSprite2D(Context* context) :
     Drawable2D(context),
+    blendMode_(BLEND_ALPHA),
     flipX_(false),
     flipY_(false),
     color_(Color::WHITE),
     useHotSpot_(false),
     hotSpot_(0.5f, 0.5f)
 {
-    vertices_.Reserve(6);
+    sourceBatches_.Resize(1);
 }
 
 StaticSprite2D::~StaticSprite2D()
@@ -54,11 +59,13 @@ void StaticSprite2D::RegisterObject(Context* context)
     context->RegisterFactory<StaticSprite2D>(ATOMIC2D_CATEGORY);
 
     ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    COPY_BASE_ATTRIBUTES(Drawable2D);
     MIXED_ACCESSOR_ATTRIBUTE("Sprite", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()), AM_DEFAULT);
+    ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Flip X", GetFlipX, SetFlipX, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Flip Y", GetFlipY, SetFlipY, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable2D);
+    MIXED_ACCESSOR_ATTRIBUTE("Custom material", GetCustomMaterialAttr, SetCustomMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
 }
 
 void StaticSprite2D::SetSprite(Sprite2D* sprite)
@@ -67,11 +74,21 @@ void StaticSprite2D::SetSprite(Sprite2D* sprite)
         return;
 
     sprite_ = sprite;
-    if (sprite)
-        verticesDirty_ = true;
+    UpdateMaterial();
 
+    sourceBatchesDirty_ = true;
+    MarkNetworkUpdate();
+}
 
-    SetTexture(sprite_ ? sprite_->GetTexture() : 0);
+void StaticSprite2D::SetBlendMode(BlendMode blendMode)
+{
+    if (blendMode == blendMode_)
+        return;
+
+    blendMode_ = blendMode;
+
+    UpdateMaterial();
+    MarkNetworkUpdate();
 }
 
 void StaticSprite2D::SetFlip(bool flipX, bool flipY)
@@ -81,7 +98,7 @@ void StaticSprite2D::SetFlip(bool flipX, bool flipY)
 
     flipX_ = flipX;
     flipY_ = flipY;
-    verticesDirty_ = true;
+    sourceBatchesDirty_ = true;
 
     OnFlipChanged();
 
@@ -104,7 +121,7 @@ void StaticSprite2D::SetColor(const Color& color)
         return;
 
     color_ = color;
-    verticesDirty_ = true;
+    sourceBatchesDirty_ = true;
     MarkNetworkUpdate();
 }
 
@@ -114,7 +131,7 @@ void StaticSprite2D::SetAlpha(float alpha)
         return;
 
     color_.a_ = alpha;
-    verticesDirty_ = true;
+    sourceBatchesDirty_ = true;
     MarkNetworkUpdate();
 }
 
@@ -124,7 +141,7 @@ void StaticSprite2D::SetUseHotSpot(bool useHotSpot)
         return;
 
     useHotSpot_ = useHotSpot;
-    verticesDirty_ = true;
+    sourceBatchesDirty_ = true;
     MarkNetworkUpdate();
 }
 
@@ -137,16 +154,34 @@ void StaticSprite2D::SetHotSpot(const Vector2& hotspot)
 
     if (useHotSpot_)
     {
-        verticesDirty_ = true;
+        sourceBatchesDirty_ = true;
         MarkNetworkUpdate();
     }
 }
 
+void StaticSprite2D::SetCustomMaterial(Material* customMaterial)
+{
+    if (customMaterial == customMaterial_)
+        return;
+
+    customMaterial_ = customMaterial;
+    sourceBatchesDirty_ = true;
+
+    UpdateMaterial();
+    MarkNetworkUpdate();
+}
+
 Sprite2D* StaticSprite2D::GetSprite() const
 {
     return sprite_;
 }
 
+
+Material* StaticSprite2D::GetCustomMaterial() const
+{
+    return customMaterial_;
+}
+
 void StaticSprite2D::SetSpriteAttr(const ResourceRef& value)
 {
     Sprite2D* sprite = Sprite2D::LoadFromResourceRef(this, value);
@@ -159,6 +194,17 @@ ResourceRef StaticSprite2D::GetSpriteAttr() const
     return Sprite2D::SaveToResourceRef(sprite_);
 }
 
+void StaticSprite2D::SetCustomMaterialAttr(const ResourceRef& value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetCustomMaterial(cache->GetResource<Material>(value.name_));
+}
+
+ResourceRef StaticSprite2D::GetCustomMaterialAttr() const
+{
+    return GetResourceRef(customMaterial_, Material::GetTypeStatic());
+}
+
 void StaticSprite2D::OnWorldBoundingBoxUpdate()
 {
     boundingBox_.Clear();
@@ -185,21 +231,38 @@ void StaticSprite2D::OnWorldBoundingBoxUpdate()
     worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
 }
 
-void StaticSprite2D::UpdateVertices()
+void StaticSprite2D::OnDrawOrderChanged()
 {
-    if (!verticesDirty_)
+    sourceBatches_[0].drawOrder_ = GetDrawOrder();
+}
+
+void StaticSprite2D::UpdateSourceBatches()
+{
+    if (!sourceBatchesDirty_)
         return;
 
-    vertices_.Clear();
+    Vector<Vertex2D>& vertices = sourceBatches_[0].vertices_;
+    vertices.Clear();
 
-    Texture2D* texture = GetTexture();
-    if (!texture)
+    if (!sprite_)
         return;
 
-    const IntRect& rectangle_ = sprite_->GetRectangle();
-    if (rectangle_.Width() == 0 || rectangle_.Height() == 0)
-        return;
+    Rect drawRect;
+    if (useHotSpot_)
+    {
+        if (!sprite_->GetDrawRectangle(drawRect, hotSpot_, flipX_, flipY_))
+            return;
+    }
+    else
+    {
+        if (!sprite_->GetDrawRectangle(drawRect, flipX_, flipY_))
+            return;
+    }
 
+    Rect textureRect;
+    if (!sprite_->GetTextureRectangle(textureRect, flipX_, flipY_))
+        return;
+    
     /*
     V1---------V2
     |         / |
@@ -214,76 +277,26 @@ void StaticSprite2D::UpdateVertices()
     Vertex2D vertex2;
     Vertex2D vertex3;
 
-    float width = (float)rectangle_.Width() * PIXEL_SIZE;     // Compute width and height in pixels
-    float height = (float)rectangle_.Height() * PIXEL_SIZE;
-
-    float hotSpotX;
-    float hotSpotY;
-
-    if (useHotSpot_)
-    {
-        hotSpotX = flipX_ ? (1.0f - hotSpot_.x_) : hotSpot_.x_;
-        hotSpotY = flipY_ ? (1.0f - hotSpot_.y_) : hotSpot_.y_;
-    }
-    else
-    {
-        const Vector2& hotSpot = sprite_->GetHotSpot();
-        hotSpotX = flipX_ ? (1.0f - hotSpot.x_) : hotSpot.x_;
-        hotSpotY = flipY_ ? (1.0f - hotSpot.y_) : hotSpot.y_;
-    }
-
-#ifdef ATOMIC_OPENGL
-    float leftX = -width * hotSpotX;
-    float rightX = width * (1.0f - hotSpotX);
-    float bottomY = -height * hotSpotY;
-    float topY = height * (1.0f - hotSpotY);
-#else
-    const float halfPixelOffset = 0.5f * PIXEL_SIZE;
-    float leftX = -width * hotSpotX + halfPixelOffset;
-    float rightX = width * (1.0f - hotSpotX) + halfPixelOffset;
-    float bottomY = -height * hotSpotY + halfPixelOffset;
-    float topY = height * (1.0f - hotSpotY) + halfPixelOffset;
-#endif
-
+    // Convert to world space
     const Matrix3x4& worldTransform = node_->GetWorldTransform();
+    vertex0.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.min_.y_, 0.0f);
+    vertex1.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.max_.y_, 0.0f);
+    vertex2.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.max_.y_, 0.0f);
+    vertex3.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.min_.y_, 0.0f);
 
-    vertex0.position_ = worldTransform * Vector3(leftX, bottomY, 0.0f);
-    vertex1.position_ = worldTransform * Vector3(leftX, topY, 0.0f);
-    vertex2.position_ = worldTransform * Vector3(rightX, topY, 0.0f);
-    vertex3.position_ = worldTransform * Vector3(rightX, bottomY, 0.0f);
-
-    float invTexW = 1.0f / (float)texture->GetWidth();
-    float invTexH = 1.0f / (float)texture->GetHeight();
-
-    float leftU = rectangle_.left_ * invTexW;
-    float rightU = rectangle_.right_ * invTexW;
-    float topV = rectangle_.top_ * invTexH;
-    float bottomV = rectangle_.bottom_ * invTexH;
-    vertex0.uv_ = Vector2(leftU, bottomV);
-    vertex1.uv_ = Vector2(leftU, topV);
-    vertex2.uv_ = Vector2(rightU, topV);
-    vertex3.uv_ = Vector2(rightU, bottomV);
-
-    if (flipX_)
-    {
-        Swap(vertex0.uv_.x_, vertex3.uv_.x_);
-        Swap(vertex1.uv_.x_, vertex2.uv_.x_);
-    }
-    
-    if (flipY_)
-    {
-        Swap(vertex0.uv_.y_, vertex1.uv_.y_);
-        Swap(vertex2.uv_.y_, vertex3.uv_.y_);
-    }
+    vertex0.uv_ = textureRect.min_;
+    vertex1.uv_ = Vector2(textureRect.min_.x_, textureRect.max_.y_);
+    vertex2.uv_ = textureRect.max_;
+    vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
 
     vertex0.color_ = vertex1.color_ = vertex2.color_  = vertex3.color_ = color_.ToUInt();
 
-    vertices_.Push(vertex0);
-    vertices_.Push(vertex1);
-    vertices_.Push(vertex2);
-    vertices_.Push(vertex3);
+    vertices.Push(vertex0);
+    vertices.Push(vertex1);
+    vertices.Push(vertex2);
+    vertices.Push(vertex3);
 
-    verticesDirty_ = false;
+    sourceBatchesDirty_ = false;
 }
 
 void StaticSprite2D::OnFlipChanged()
@@ -291,4 +304,17 @@ void StaticSprite2D::OnFlipChanged()
 
 }
 
+void StaticSprite2D::UpdateMaterial()
+{
+    if (customMaterial_)
+        sourceBatches_[0].material_ = customMaterial_;
+    else
+    {
+        if (sprite_)
+            sourceBatches_[0].material_ = renderer_->GetMaterial(sprite_->GetTexture(), blendMode_);
+        else
+            sourceBatches_[0].material_ = 0;
+    }
+}
+
 }

+ 23 - 3
Source/Atomic/Atomic2D/StaticSprite2D.h

@@ -44,6 +44,8 @@ public:
 
     /// Set sprite.
     void SetSprite(Sprite2D* sprite);
+    /// Set blend mode.
+    void SetBlendMode(BlendMode blendMode);
     /// Set flip.
     void SetFlip(bool flipX, bool flipY);
     /// Set flip X.
@@ -58,9 +60,13 @@ public:
     void SetUseHotSpot(bool useHotSpot);
     /// Set hot spot.
     void SetHotSpot(const Vector2& hotspot);
-    
+    /// Set custom material.
+    void SetCustomMaterial(Material* customMaterial);
+
     /// Return sprite.
     Sprite2D* GetSprite() const;
+    /// Return blend mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
     /// Return flip X.
     bool GetFlipX() const { return flipX_; }
     /// Return flip Y.
@@ -73,22 +79,34 @@ public:
     bool GetUseHotSpot() const { return useHotSpot_; }
     /// Return hot spot.
     const Vector2& GetHotSpot() const { return hotSpot_; }
+    /// Return custom material.
+    Material* GetCustomMaterial() const;
 
     /// Set sprite attribute.
     void SetSpriteAttr(const ResourceRef& value);
     /// Return sprite attribute.
     ResourceRef GetSpriteAttr() const;
+    /// Set custom material attribute.
+    void SetCustomMaterialAttr(const ResourceRef& value);
+    /// Return custom material attribute.
+    ResourceRef GetCustomMaterialAttr() const;
 
 protected:
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
-    /// Update vertices.
-    virtual void UpdateVertices();
+    /// Handle draw order changed.
+    virtual void OnDrawOrderChanged();
+    /// Update source batches.
+    virtual void UpdateSourceBatches();
     /// Handle flip changed.
     virtual void OnFlipChanged();
+    /// Update material.
+    void UpdateMaterial();
 
     /// Sprite.
     SharedPtr<Sprite2D> sprite_;
+    /// Blend mode.
+    BlendMode blendMode_;
     /// Flip X.
     bool flipX_;
     /// Flip Y.
@@ -99,6 +117,8 @@ protected:
     bool useHotSpot_;
     /// Hot spot.
     Vector2 hotSpot_;
+    /// Custom material.
+    SharedPtr<Material> customMaterial_;
 };
 
 }

+ 3 - 3
Source/Atomic/Atomic3D/AnimatedModel.cpp

@@ -230,13 +230,13 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
 
     // Note: per-geometry distances do not take skinning into account. Especially in case of a ragdoll they may be
     // much off base if the node's own transform is not updated
-    if (batches_.Size() > 1)
+    if (batches_.Size() == 1)
+        batches_[0].distance_ = distance_;
+    else
     {
         for (unsigned i = 0; i < batches_.Size(); ++i)
             batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
     }
-    else if (batches_.Size() == 1)
-        batches_[0].distance_ = distance_;
 
     // Use a transformed version of the model's bounding box instead of world bounding box for LOD scale
     // determination so that animation does not change the scale

+ 2 - 2
Source/Atomic/Atomic3D/DecalSet.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -914,7 +914,7 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
 
             if (!found)
             {
-                if (bones_.Size() >= MAX_SKIN_MATRICES)
+                if (bones_.Size() >= Graphics::GetMaxBones())
                 {
                     LOGWARNING("Maximum skinned decal bone count reached");
                     return false;

+ 2 - 0
Source/Atomic/Atomic3D/Model.h

@@ -165,6 +165,8 @@ public:
     const PODVector<Vector3>& GetGeometryCenters() const { return geometryCenters_; }
     /// Return geometry by index and LOD level. The LOD level is clamped if out of range.
     Geometry* GetGeometry(unsigned index, unsigned lodLevel) const;
+    /// Return geometry center by index.
+    const Vector3& GetGeometryCenter(unsigned index) const { return index < geometryCenters_.Size() ? geometryCenters_[index] : Vector3::ZERO; }
     /// Return geometery bone mappings.
     const Vector<PODVector<unsigned> >& GetGeometryBoneMappings() const { return geometryBoneMappings_; }
     /// Return vertex morphs.

+ 2 - 2
Source/Atomic/Atomic3D/ParticleEmitter.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -484,7 +484,7 @@ bool ParticleEmitter::EmitNewParticle()
     billboard.uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
     billboard.rotation_ = effect_->GetRandomRotation();
     const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
-    billboard.color_ = colorFrames_[0].color_;
+    billboard.color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
     billboard.enabled_ = true;
 
     return true;

+ 10 - 12
Source/Atomic/Atomic3D/StaticModel.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -87,6 +87,7 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
         Ray localRay = query.ray_.Transformed(inverse);
         float distance = localRay.HitDistance(boundingBox_);
         Vector3 normal = -query.ray_.direction_;
+        unsigned hitBatch = M_MAX_UNSIGNED;
 
         if (level == RAY_TRIANGLE && distance < query.maxDistance_)
         {
@@ -103,6 +104,7 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
                     {
                         distance = geometryDistance;
                         normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized();
+                        hitBatch = i;
                     }
                 }
             }
@@ -116,7 +118,7 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
             result.distance_ = distance;
             result.drawable_ = this;
             result.node_ = node_;
-            result.subObject_ = M_MAX_UNSIGNED;
+            result.subObject_ = hitBatch;
             results.Push(result);
         }
         break;
@@ -126,21 +128,15 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
 void StaticModel::UpdateBatches(const FrameInfo& frame)
 {
     const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
     distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
 
-    if (batches_.Size() > 1)
+    if (batches_.Size() == 1)
+        batches_[0].distance_ = distance_;
+    else
     {
+        const Matrix3x4& worldTransform = node_->GetWorldTransform();
         for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
             batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
-            batches_[i].worldTransform_ = &worldTransform;
-        }
-    }
-    else if (batches_.Size() == 1)
-    {
-        batches_[0].distance_ = distance_;
-        batches_[0].worldTransform_ = &worldTransform;
     }
 
     float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
@@ -255,8 +251,10 @@ void StaticModel::SetModel(Model* model)
         SetNumGeometries(model->GetNumGeometries());
         const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries();
         const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters();
+        const Matrix3x4* worldTransform = node_ ? &node_->GetWorldTransform() : (const Matrix3x4*)0;
         for (unsigned i = 0; i < geometries.Size(); ++i)
         {
+            batches_[i].worldTransform_ = worldTransform;
             geometries_[i] = geometries[i];
             geometryData_[i].center_ = geometryCenters[i];
         }

+ 208 - 244
Source/Atomic/Graphics/Batch.cpp

@@ -89,40 +89,39 @@ void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split
     float width = (float)shadowMap->GetWidth();
     float height = (float)shadowMap->GetHeight();
     
-    Vector2 offset(
+    Vector3 offset(
         (float)viewport.left_ / width,
-        (float)viewport.top_ / height
+        (float)viewport.top_ / height,
+        0.0f
     );
     
-    Vector2 scale(
+    Vector3 scale(
         0.5f * (float)viewport.Width() / width,
-        0.5f * (float)viewport.Height() / height
+        0.5f * (float)viewport.Height() / height,
+        1.0f
     );
-    
+
+    // Add pixel-perfect offset if needed by the graphics API
+    const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
+    offset.x_ += scale.x_ + pixelUVOffset.x_ / width;
+    offset.y_ += scale.y_ + pixelUVOffset.y_ / height;
+
     #ifdef ATOMIC_OPENGL
-    offset.x_ += scale.x_;
-    offset.y_ += scale.y_;
+    offset.z_ = 0.5f;
+    scale.z_ = 0.5f;
     offset.y_ = 1.0f - offset.y_;
-    // If using 4 shadow samples, offset the position diagonally by half pixel
-    if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
-    {
-        offset.x_ -= 0.5f / width;
-        offset.y_ -= 0.5f / height;
-    }
-    texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.5f));
-    texAdjust.SetScale(Vector3(scale.x_, scale.y_, 0.5f));
     #else
-    offset.x_ += scale.x_ + 0.5f / width;
-    offset.y_ += scale.y_ + 0.5f / height;
+    scale.y_ = -scale.y_;
+    #endif
+
+    // If using 4 shadow samples, offset the position diagonally by half pixel
     if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
     {
         offset.x_ -= 0.5f / width;
         offset.y_ -= 0.5f / height;
     }
-    scale.y_ = -scale.y_;
-    texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.0f));
-    texAdjust.SetScale(Vector3(scale.x_, scale.y_, 1.0f));
-    #endif
+    texAdjust.SetTranslation(offset);
+    texAdjust.SetScale(scale);
     
     dest = texAdjust * shadowProj * shadowView * posAdjust;
 }
@@ -181,11 +180,12 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     Light* light = lightQueue_ ? lightQueue_->light_ : 0;
     Texture2D* shadowMap = lightQueue_ ? lightQueue_->shadowMap_ : 0;
 
+    // Set shaders first. The available shader parameters and their register/uniform positions depend on the currently set shaders
+    graphics->SetShaders(vertexShader_, pixelShader_);
+
     // Set pass / material-specific renderstates
     if (pass_ && material_)
     {
-        bool isShadowPass = pass_->GetType() == PASS_SHADOW;
-        
         BlendMode blend = pass_->GetBlendMode();
         // Turn additive blending into subtract if the light is negative
         if (light && light->IsNegative())
@@ -195,43 +195,40 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
             else if (blend == BLEND_ADDALPHA)
                 blend = BLEND_SUBTRACTALPHA;
         }
-        
         graphics->SetBlendMode(blend);
+
+        bool isShadowPass = pass_->GetIndex() == Technique::shadowPassIndex;
         renderer->SetCullMode(isShadowPass ? material_->GetShadowCullMode() : material_->GetCullMode(), camera_);
         if (!isShadowPass)
         {
             const BiasParameters& depthBias = material_->GetDepthBias();
             graphics->SetDepthBias(depthBias.constantBias_, depthBias.slopeScaledBias_);
         }
+
+        // Use the "least filled" fill mode combined from camera & material
+        graphics->SetFillMode((FillMode)(Max(camera_->GetFillMode(), material_->GetFillMode())));
         graphics->SetDepthTest(pass_->GetDepthTestMode());
         graphics->SetDepthWrite(pass_->GetDepthWrite() && allowDepthWrite);
     }
     
-    // Set shaders first. The available shader parameters and their register/uniform positions depend on the currently set shaders
-    graphics->SetShaders(vertexShader_, pixelShader_);
-    
     // Set global (per-frame) shader parameters
     if (graphics->NeedParameterUpdate(SP_FRAME, (void*)0))
         view->SetGlobalShaderParameters();
     
-    // Set camera shader parameters
+    // Set camera & viewport shader parameters
     unsigned cameraHash = (unsigned)(size_t)camera_;
-    if (graphics->NeedParameterUpdate(SP_CAMERA, reinterpret_cast<void*>(cameraHash)))
-        view->SetCameraShaderParameters(camera_, true);
-    
-    // Set viewport shader parameters
     IntRect viewport = graphics->GetViewport();
     IntVector2 viewSize = IntVector2(viewport.Width(), viewport.Height());
     unsigned viewportHash = viewSize.x_ | (viewSize.y_ << 16);
-    
-    if (graphics->NeedParameterUpdate(SP_VIEWPORT, reinterpret_cast<void*>(viewportHash)))
+    if (graphics->NeedParameterUpdate(SP_CAMERA, reinterpret_cast<const void*>(cameraHash + viewportHash)))
     {
+        view->SetCameraShaderParameters(camera_, true);
         // During renderpath commands the G-Buffer or viewport texture is assumed to always be viewport-sized
         view->SetGBufferShaderParameters(viewSize, IntRect(0, 0, viewSize.x_, viewSize.y_));
     }
     
     // Set model or skinning transforms
-    if (setModelTransform && graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, worldTransform_))
+    if (setModelTransform && graphics->NeedParameterUpdate(SP_OBJECT, worldTransform_))
     {
         if (geometryType_ == GEOM_SKINNED)
         {
@@ -263,7 +260,7 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     unsigned zoneHash = (unsigned)(size_t)zone_;
     if (overrideFogColorToBlack)
         zoneHash += 0x80000000;
-    if (zone_ && graphics->NeedParameterUpdate(SP_ZONE, reinterpret_cast<void*>(zoneHash)))
+    if (zone_ && graphics->NeedParameterUpdate(SP_ZONE, reinterpret_cast<const void*>(zoneHash)))
     {
         graphics->SetShaderParameter(VSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor());
         graphics->SetShaderParameter(VSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4() - zone_->GetAmbientStartColor().ToVector4());
@@ -301,104 +298,52 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     // Set light-related shader parameters
     if (lightQueue_)
     {
-        if (graphics->NeedParameterUpdate(SP_VERTEXLIGHTS, lightQueue_) && graphics->HasShaderParameter(VSP_VERTEXLIGHTS))
+        if (light && graphics->NeedParameterUpdate(SP_LIGHT, lightQueue_))
         {
-            Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
-            const PODVector<Light*>& lights = lightQueue_->vertexLights_;
-            
-            for (unsigned i = 0; i < lights.Size(); ++i)
-            {
-                Light* vertexLight = lights[i];
-                Node* vertexLightNode = vertexLight->GetNode();
-                LightType type = vertexLight->GetLightType();
-                
-                // Attenuation
-                float invRange, cutoff, invCutoff;
-                if (type == LIGHT_DIRECTIONAL)
-                    invRange = 0.0f;
-                else
-                    invRange = 1.0f / Max(vertexLight->GetRange(), M_EPSILON);
-                if (type == LIGHT_SPOT)
-                {
-                    cutoff = Cos(vertexLight->GetFov() * 0.5f);
-                    invCutoff = 1.0f / (1.0f - cutoff);
-                }
-                else
-                {
-                    cutoff = -1.0f;
-                    invCutoff = 1.0f;
-                }
-                
-                // Color
-                float fade = 1.0f;
-                float fadeEnd = vertexLight->GetDrawDistance();
-                float fadeStart = vertexLight->GetFadeDistance();
-                
-                // Do fade calculation for light if both fade & draw distance defined
-                if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
-                    fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
-                
-                Color color = vertexLight->GetEffectiveColor() * fade;
-                vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
-                
-                // Direction
-                vertexLights[i * 3 + 1] = Vector4(-(vertexLightNode->GetWorldDirection()), cutoff);
-                
-                // Position
-                vertexLights[i * 3 + 2] = Vector4(vertexLightNode->GetWorldPosition(), invCutoff);
-            }
-            
-            if (lights.Size())
-                graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].Data(), lights.Size() * 3 * 4);
-        }
-    }
-    
-    if (light && graphics->NeedParameterUpdate(SP_LIGHT, light))
-    {
-        // Deferred light volume batches operate in a camera-centered space. Detect from material, zone & pass all being null
-        bool isLightVolume = !material_ && !pass_ && !zone_;
-        
-        Matrix3x4 cameraEffectiveTransform = camera_->GetEffectiveWorldTransform();
-        Vector3 cameraEffectivePos = cameraEffectiveTransform.Translation();
+            // Deferred light volume batches operate in a camera-centered space. Detect from material, zone & pass all being null
+            bool isLightVolume = !material_ && !pass_ && !zone_;
 
-        Node* lightNode = light->GetNode();
-        Matrix3 lightWorldRotation = lightNode->GetWorldRotation().RotationMatrix();
-        
-        graphics->SetShaderParameter(VSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
-        
-        float atten = 1.0f / Max(light->GetRange(), M_EPSILON);
-        graphics->SetShaderParameter(VSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition(), atten));
-        
-        if (graphics->HasShaderParameter(VSP_LIGHTMATRICES))
-        {
-            switch (light->GetLightType())
+            Matrix3x4 cameraEffectiveTransform = camera_->GetEffectiveWorldTransform();
+            Vector3 cameraEffectivePos = cameraEffectiveTransform.Translation();
+
+            Node* lightNode = light->GetNode();
+            Matrix3 lightWorldRotation = lightNode->GetWorldRotation().RotationMatrix();
+
+            graphics->SetShaderParameter(VSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
+
+            float atten = 1.0f / Max(light->GetRange(), M_EPSILON);
+            graphics->SetShaderParameter(VSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition(), atten));
+
+            if (graphics->HasShaderParameter(VSP_LIGHTMATRICES))
             {
-            case LIGHT_DIRECTIONAL:
+                switch (light->GetLightType())
+                {
+                case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
                     unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
 
                     for (unsigned i = 0; i < numSplits; ++i)
                         CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, Vector3::ZERO);
-                    
+
                     graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
                 }
                 break;
-                
-            case LIGHT_SPOT:
+
+                case LIGHT_SPOT:
                 {
                     Matrix4 shadowMatrices[2];
-                    
+
                     CalculateSpotMatrix(shadowMatrices[0], light, Vector3::ZERO);
                     bool isShadowed = shadowMap && graphics->HasTextureUnit(TU_SHADOWMAP);
                     if (isShadowed)
                         CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, Vector3::ZERO);
-                    
+
                     graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
                 }
                 break;
-                
-            case LIGHT_POINT:
+
+                case LIGHT_POINT:
                 {
                     Matrix4 lightVecRot(lightNode->GetWorldRotation().RotationMatrix());
                     // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
@@ -410,29 +355,29 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     #endif
                 }
                 break;
+                }
             }
-        }
-        
-        float fade = 1.0f;
-        float fadeEnd = light->GetDrawDistance();
-        float fadeStart = light->GetFadeDistance();
-        
-        // Do fade calculation for light if both fade & draw distance defined
-        if (light->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
-            fade = Min(1.0f - (light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
-        
-        // Negative lights will use subtract blending, so write absolute RGB values to the shader parameter
-        graphics->SetShaderParameter(PSP_LIGHTCOLOR, Color(light->GetEffectiveColor().Abs(),
-            light->GetEffectiveSpecularIntensity()) * fade);
-        graphics->SetShaderParameter(PSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
-        graphics->SetShaderParameter(PSP_LIGHTPOS, Vector4((isLightVolume ? (lightNode->GetWorldPosition() -
-            cameraEffectivePos) : lightNode->GetWorldPosition()), atten));
-        
-        if (graphics->HasShaderParameter(PSP_LIGHTMATRICES))
-        {
-            switch (light->GetLightType())
+
+            float fade = 1.0f;
+            float fadeEnd = light->GetDrawDistance();
+            float fadeStart = light->GetFadeDistance();
+
+            // Do fade calculation for light if both fade & draw distance defined
+            if (light->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
+                fade = Min(1.0f - (light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
+
+            // Negative lights will use subtract blending, so write absolute RGB values to the shader parameter
+            graphics->SetShaderParameter(PSP_LIGHTCOLOR, Color(light->GetEffectiveColor().Abs(),
+                light->GetEffectiveSpecularIntensity()) * fade);
+            graphics->SetShaderParameter(PSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
+            graphics->SetShaderParameter(PSP_LIGHTPOS, Vector4((isLightVolume ? (lightNode->GetWorldPosition() -
+                cameraEffectivePos) : lightNode->GetWorldPosition()), atten));
+
+            if (graphics->HasShaderParameter(PSP_LIGHTMATRICES))
             {
-            case LIGHT_DIRECTIONAL:
+                switch (light->GetLightType())
+                {
+                case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
                     unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
@@ -445,11 +390,11 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
                 }
                 break;
-                
-            case LIGHT_SPOT:
+
+                case LIGHT_SPOT:
                 {
                     Matrix4 shadowMatrices[2];
-                    
+
                     CalculateSpotMatrix(shadowMatrices[0], light, cameraEffectivePos);
                     bool isShadowed = lightQueue_->shadowMap_ != 0;
                     if (isShadowed)
@@ -457,12 +402,12 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                         CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, isLightVolume ? cameraEffectivePos :
                             Vector3::ZERO);
                     }
-                    
+
                     graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
                 }
                 break;
-                
-            case LIGHT_POINT:
+
+                case LIGHT_POINT:
                 {
                     Matrix4 lightVecRot(lightNode->GetWorldRotation().RotationMatrix());
                     // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
@@ -474,103 +419,151 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     #endif
                 }
                 break;
+                }
             }
-        }
-        
-        // Set shadow mapping shader parameters
-        if (shadowMap)
-        {
+
+            // Set shadow mapping shader parameters
+            if (shadowMap)
             {
-                // Calculate point light shadow sampling offsets (unrolled cube map)
-                unsigned faceWidth = shadowMap->GetWidth() / 2;
-                unsigned faceHeight = shadowMap->GetHeight() / 3;
-                float width = (float)shadowMap->GetWidth();
-                float height = (float)shadowMap->GetHeight();
-                #ifdef ATOMIC_OPENGL
+                {
+                    // Calculate point light shadow sampling offsets (unrolled cube map)
+                    unsigned faceWidth = shadowMap->GetWidth() / 2;
+                    unsigned faceHeight = shadowMap->GetHeight() / 3;
+                    float width = (float)shadowMap->GetWidth();
+                    float height = (float)shadowMap->GetHeight();
+                    #ifdef ATOMIC_OPENGL
                     float mulX = (float)(faceWidth - 3) / width;
                     float mulY = (float)(faceHeight - 3) / height;
                     float addX = 1.5f / width;
                     float addY = 1.5f / height;
-                #else
+                    #else
                     float mulX = (float)(faceWidth - 4) / width;
                     float mulY = (float)(faceHeight - 4) / height;
                     float addX = 2.5f / width;
                     float addY = 2.5f / height;
-                #endif
-                // If using 4 shadow samples, offset the position diagonally by half pixel
-                if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
+                    #endif
+                    // If using 4 shadow samples, offset the position diagonally by half pixel
+                    if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
+                    {
+                        addX -= 0.5f / width;
+                        addY -= 0.5f / height;
+                    }
+                    graphics->SetShaderParameter(PSP_SHADOWCUBEADJUST, Vector4(mulX, mulY, addX, addY));
+                }
+
+                {
+                    // Calculate shadow camera depth parameters for point light shadows and shadow fade parameters for
+                    //  directional light shadows, stored in the same uniform
+                    Camera* shadowCamera = lightQueue_->shadowSplits_[0].shadowCamera_;
+                    float nearClip = shadowCamera->GetNearClip();
+                    float farClip = shadowCamera->GetFarClip();
+                    float q = farClip / (farClip - nearClip);
+                    float r = -q * nearClip;
+
+                    const CascadeParameters& parameters = light->GetShadowCascade();
+                    float viewFarClip = camera_->GetFarClip();
+                    float shadowRange = parameters.GetShadowRange();
+                    float fadeStart = parameters.fadeStart_ * shadowRange / viewFarClip;
+                    float fadeEnd = shadowRange / viewFarClip;
+                    float fadeRange = fadeEnd - fadeStart;
+
+                    graphics->SetShaderParameter(PSP_SHADOWDEPTHFADE, Vector4(q, r, fadeStart, 1.0f / fadeRange));
+                }
+
                 {
-                    addX -= 0.5f / width;
-                    addY -= 0.5f / height;
+                    float intensity = light->GetShadowIntensity();
+                    float fadeStart = light->GetShadowFadeDistance();
+                    float fadeEnd = light->GetShadowDistance();
+                    if (fadeStart > 0.0f && fadeEnd > 0.0f && fadeEnd > fadeStart)
+                        intensity = Lerp(intensity, 1.0f, Clamp((light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 0.0f, 1.0f));
+                    float pcfValues = (1.0f - intensity);
+                    float samples = renderer->GetShadowQuality() >= SHADOWQUALITY_HIGH_16BIT ? 4.0f : 1.0f;
+
+                    graphics->SetShaderParameter(PSP_SHADOWINTENSITY, Vector4(pcfValues / samples, intensity, 0.0f, 0.0f));
                 }
-                graphics->SetShaderParameter(PSP_SHADOWCUBEADJUST, Vector4(mulX, mulY, addX, addY));
+
+                float sizeX = 1.0f / (float)shadowMap->GetWidth();
+                float sizeY = 1.0f / (float)shadowMap->GetHeight();
+                graphics->SetShaderParameter(PSP_SHADOWMAPINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
+
+                Vector4 lightSplits(M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE);
+                if (lightQueue_->shadowSplits_.Size() > 1)
+                    lightSplits.x_ = lightQueue_->shadowSplits_[0].farSplit_ / camera_->GetFarClip();
+                if (lightQueue_->shadowSplits_.Size() > 2)
+                    lightSplits.y_ = lightQueue_->shadowSplits_[1].farSplit_ / camera_->GetFarClip();
+                if (lightQueue_->shadowSplits_.Size() > 3)
+                    lightSplits.z_ = lightQueue_->shadowSplits_[2].farSplit_ / camera_->GetFarClip();
+
+                graphics->SetShaderParameter(PSP_SHADOWSPLITS, lightSplits);
             }
+        }
+        else if (lightQueue_->vertexLights_.Size() && graphics->HasShaderParameter(VSP_VERTEXLIGHTS) &&
+            graphics->NeedParameterUpdate(SP_LIGHT, lightQueue_))
+        {
+            Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
+            const PODVector<Light*>& lights = lightQueue_->vertexLights_;
             
+            for (unsigned i = 0; i < lights.Size(); ++i)
             {
-                // Calculate shadow camera depth parameters for point light shadows and shadow fade parameters for
-                //  directional light shadows, stored in the same uniform
-                Camera* shadowCamera = lightQueue_->shadowSplits_[0].shadowCamera_;
-                float nearClip = shadowCamera->GetNearClip();
-                float farClip = shadowCamera->GetFarClip();
-                float q = farClip / (farClip - nearClip);
-                float r = -q * nearClip;
+                Light* vertexLight = lights[i];
+                Node* vertexLightNode = vertexLight->GetNode();
+                LightType type = vertexLight->GetLightType();
                 
-                const CascadeParameters& parameters = light->GetShadowCascade();
-                float viewFarClip = camera_->GetFarClip();
-                float shadowRange = parameters.GetShadowRange();
-                float fadeStart = parameters.fadeStart_ * shadowRange / viewFarClip;
-                float fadeEnd = shadowRange / viewFarClip;
-                float fadeRange = fadeEnd - fadeStart;
+                // Attenuation
+                float invRange, cutoff, invCutoff;
+                if (type == LIGHT_DIRECTIONAL)
+                    invRange = 0.0f;
+                else
+                    invRange = 1.0f / Max(vertexLight->GetRange(), M_EPSILON);
+                if (type == LIGHT_SPOT)
+                {
+                    cutoff = Cos(vertexLight->GetFov() * 0.5f);
+                    invCutoff = 1.0f / (1.0f - cutoff);
+                }
+                else
+                {
+                    cutoff = -1.0f;
+                    invCutoff = 1.0f;
+                }
                 
-                graphics->SetShaderParameter(PSP_SHADOWDEPTHFADE, Vector4(q, r, fadeStart, 1.0f / fadeRange));
-            }
-            
-            {
-                float intensity = light->GetShadowIntensity();
-                float fadeStart = light->GetShadowFadeDistance();
-                float fadeEnd = light->GetShadowDistance();
-                if (fadeStart > 0.0f && fadeEnd > 0.0f && fadeEnd > fadeStart)
-                    intensity = Lerp(intensity, 1.0f, Clamp((light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 0.0f, 1.0f));
-                float pcfValues = (1.0f - intensity);
-                float samples = renderer->GetShadowQuality() >= SHADOWQUALITY_HIGH_16BIT ? 4.0f : 1.0f;
-
-                graphics->SetShaderParameter(PSP_SHADOWINTENSITY, Vector4(pcfValues / samples, intensity, 0.0f, 0.0f));
+                // Color
+                float fade = 1.0f;
+                float fadeEnd = vertexLight->GetDrawDistance();
+                float fadeStart = vertexLight->GetFadeDistance();
+                
+                // Do fade calculation for light if both fade & draw distance defined
+                if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
+                    fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
+                
+                Color color = vertexLight->GetEffectiveColor() * fade;
+                vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
+                
+                // Direction
+                vertexLights[i * 3 + 1] = Vector4(-(vertexLightNode->GetWorldDirection()), cutoff);
+                
+                // Position
+                vertexLights[i * 3 + 2] = Vector4(vertexLightNode->GetWorldPosition(), invCutoff);
             }
             
-            float sizeX = 1.0f / (float)shadowMap->GetWidth();
-            float sizeY = 1.0f / (float)shadowMap->GetHeight();
-            graphics->SetShaderParameter(PSP_SHADOWMAPINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
-            
-            Vector4 lightSplits(M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE);
-            if (lightQueue_->shadowSplits_.Size() > 1)
-                lightSplits.x_ = lightQueue_->shadowSplits_[0].farSplit_ / camera_->GetFarClip();
-            if (lightQueue_->shadowSplits_.Size() > 2)
-                lightSplits.y_ = lightQueue_->shadowSplits_[1].farSplit_ / camera_->GetFarClip();
-            if (lightQueue_->shadowSplits_.Size() > 3)
-                lightSplits.z_ = lightQueue_->shadowSplits_[2].farSplit_ / camera_->GetFarClip();
-            
-            graphics->SetShaderParameter(PSP_SHADOWSPLITS, lightSplits);
+            graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].Data(), lights.Size() * 3 * 4);
         }
     }
-    
+
     // Set material-specific shader parameters and textures
     if (material_)
     {
-        if (graphics->NeedParameterUpdate(SP_MATERIAL, (const void*)material_->GetShaderParameterHash()))
+        if (graphics->NeedParameterUpdate(SP_MATERIAL, reinterpret_cast<const void*>(material_->GetShaderParameterHash())))
         {
             const HashMap<StringHash, MaterialShaderParameter>& parameters = material_->GetShaderParameters();
             for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator i = parameters.Begin(); i != parameters.End(); ++i)
                 graphics->SetShaderParameter(i->first_, i->second_.value_);
         }
         
-        const SharedPtr<Texture>* textures = material_->GetTextures();
-        unsigned numTextures = material_->GetNumUsedTextureUnits();
-
-        for (unsigned i = 0; i < numTextures; ++i)
+        const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material_->GetTextures();
+        for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
         {
-            TextureUnit unit = (TextureUnit)i;
-            if (textures[i] && graphics->HasTextureUnit(unit))
-                graphics->SetTexture(i, textures[i]);
+            if (graphics->HasTextureUnit(i->first_))
+                graphics->SetTexture(i->first_, i->second_.Get());
         }
     }
     
@@ -596,8 +589,10 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     }
     
     // Set zone texture if necessary
+    #ifdef DESKTOP_GRAPHICS
     if (zone_ && graphics->HasTextureUnit(TU_ZONE))
         graphics->SetTexture(TU_ZONE, zone_->GetZoneTexture());
+    #endif
 }
 
 void Batch::Draw(View* view, bool allowDepthWrite) const
@@ -632,9 +627,9 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
     
     if (instances_.Size() && !geometry_->IsEmpty())
     {
-        // Draw as individual objects if instancing not supported
+        // Draw as individual objects if instancing not supported or could not fill the instancing buffer
         VertexBuffer* instanceBuffer = renderer->GetInstancingBuffer();
-        if (!instanceBuffer || geometryType_ != GEOM_INSTANCED)
+        if (!instanceBuffer || geometryType_ != GEOM_INSTANCED || startIndex_ == M_MAX_UNSIGNED)
         {
             Batch::Prepare(view, false, allowDepthWrite);
             
@@ -643,7 +638,7 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
             
             for (unsigned i = 0; i < instances_.Size(); ++i)
             {
-                if (graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, instances_[i].worldTransform_))
+                if (graphics->NeedParameterUpdate(SP_OBJECT, instances_[i].worldTransform_))
                     graphics->SetShaderParameter(VSP_MODEL, *instances_[i].worldTransform_);
                 
                 graphics->Draw(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
@@ -662,41 +657,10 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
             vertexBuffers.Push(SharedPtr<VertexBuffer>(instanceBuffer));
             elementMasks.Push(instanceBuffer->GetElementMask());
             
-            // No stream offset support, instancing buffer not pre-filled with transforms: have to fill now
-            if (startIndex_ == M_MAX_UNSIGNED)
-            {
-                unsigned startIndex = 0;
-                while (startIndex < instances_.Size())
-                {
-                    unsigned instances = instances_.Size() - startIndex;
-                    if (instances > instanceBuffer->GetVertexCount())
-                        instances = instanceBuffer->GetVertexCount();
-                    
-                    // Copy the transforms
-                    Matrix3x4* dest = (Matrix3x4*)instanceBuffer->Lock(0, instances, true);
-                    if (dest)
-                    {
-                        for (unsigned i = 0; i < instances; ++i)
-                            dest[i] = *instances_[i + startIndex].worldTransform_;
-                        instanceBuffer->Unlock();
-                        
-                        graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
-                        graphics->SetVertexBuffers(vertexBuffers, elementMasks);
-                        graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(),
-                            geometry_->GetIndexCount(), geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances);
-                    }
-                    
-                    startIndex += instances;
-                }
-            }
-            // Stream offset supported and instancing buffer has been already filled, so just draw
-            else
-            {
-                graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
-                graphics->SetVertexBuffers(vertexBuffers, elementMasks, startIndex_);
-                graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
-                    geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances_.Size());
-            }
+            graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
+            graphics->SetVertexBuffers(vertexBuffers, elementMasks, startIndex_);
+            graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
+                geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances_.Size());
             
             // Remove the instancing buffer & element mask now
             vertexBuffers.Pop();

+ 3 - 1
Source/Atomic/Graphics/Batch.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -275,6 +275,8 @@ struct LightBatchQueue
 {
     /// Per-pixel light.
     Light* light_;
+    /// Light negative flag.
+    bool negative_;
     /// Shadow map depth texture.
     Texture2D* shadowMap_;
     /// Lit geometry draw calls, base (replace blend mode)

+ 2 - 2
Source/Atomic/Graphics/ConstantBuffer.h

@@ -22,8 +22,8 @@
 
 #pragma once
 
-#if defined(URHO3D_OPENGL)
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLConstantBuffer.h"
-#elif defined(URHO3D_D3D11)
+#elif defined(ATOMIC_D3D11)
 #include "Direct3D11/D3D11ConstantBuffer.h"
 #endif

+ 27 - 3
Source/Atomic/Graphics/DebugRenderer.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -246,7 +246,27 @@ void DebugRenderer::AddSphere(const Sphere& sphere, const Color& color, bool dep
     }
 }
 
+void DebugRenderer::AddCylinder(const Vector3& position, float radius, float height, const Color& color, bool depthTest)
+{
+    Sphere sphere(position, radius);
+    Vector3 heightVec(0, height, 0);
+    Vector3 offsetXVec(radius, 0, 0);
+    Vector3 offsetZVec(0, 0, radius);
+    for (unsigned i = 0; i < 360; i += 45)
+    {
+        Vector3 p1 = PointOnSphere(sphere, i, 90);
+        Vector3 p2 = PointOnSphere(sphere, i + 45, 90);
+        AddLine(p1, p2, color, depthTest);
+        AddLine(p1 + heightVec, p2 + heightVec, color, depthTest);
+    }
+    AddLine(position + offsetXVec, position + heightVec + offsetXVec, color, depthTest);
+    AddLine(position - offsetXVec, position + heightVec - offsetXVec, color, depthTest);
+    AddLine(position + offsetZVec, position + heightVec + offsetZVec, color, depthTest);
+    AddLine(position - offsetZVec, position + heightVec - offsetZVec, color, depthTest);
+}
+
 #ifdef ATOMIC_3D
+
 void DebugRenderer::AddSkeleton(const Skeleton& skeleton, const Color& color, bool depthTest)
 {
     const Vector<Bone>& bones = skeleton.GetBones();
@@ -330,7 +350,7 @@ void DebugRenderer::AddTriangleMesh(const void* vertexData, unsigned vertexSize,
 
 void DebugRenderer::Render()
 {
-    if (lines_.Empty() && noDepthLines_.Empty() && triangles_.Empty() && noDepthTriangles_.Empty())
+    if (!HasContent())
         return;
 
     Graphics* graphics = GetSubsystem<Graphics>();
@@ -413,7 +433,6 @@ void DebugRenderer::Render()
     graphics->SetColorWrite(true);
     graphics->SetCullMode(CULL_NONE);
     graphics->SetDepthWrite(true);
-    graphics->SetDrawAntialiased(true);
     graphics->SetScissorTest(false);
     graphics->SetStencilTest(false);
     graphics->SetShaders(vs, ps);
@@ -461,6 +480,11 @@ bool DebugRenderer::IsInside(const BoundingBox& box) const
     return frustum_.IsInsideFast(box) == INSIDE;
 }
 
+bool DebugRenderer::HasContent() const
+{
+    return (lines_.Empty() && noDepthLines_.Empty() && triangles_.Empty() && noDepthTriangles_.Empty()) ? false : true;
+}
+
 void DebugRenderer::HandleEndFrame(StringHash eventType, VariantMap& eventData)
 {
     // When the amount of debug geometry is reduced, release memory

+ 5 - 1
Source/Atomic/Graphics/DebugRenderer.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -126,6 +126,8 @@ public:
     void AddPolyhedron(const Polyhedron& poly, const Color& color, bool depthTest = true);
     /// Add a sphere.
     void AddSphere(const Sphere& sphere, const Color& color, bool depthTest = true);
+    /// Add a cylinder
+    void AddCylinder(const Vector3& position, float radius, float height, const Color& color, bool depthTest = true);
     /// Add a skeleton.
     void AddSkeleton(const Skeleton& skeleton, const Color& color, bool depthTest = true);
     /// Add a triangle mesh.
@@ -141,6 +143,8 @@ public:
     const Frustum& GetFrustum() const { return frustum_; }
     /// Check whether a bounding box is inside the view frustum.
     bool IsInside(const BoundingBox& box) const;
+    /// Return whether has something to render.
+    bool HasContent() const;
     
 private:
     /// Handle end of frame. Clear debug geometry.

+ 26 - 20
Source/Atomic/Graphics/Drawable.cpp

@@ -1,6 +1,6 @@
 //
 
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -62,6 +62,9 @@ Drawable::Drawable(Context* context, unsigned char drawableFlags) :
     occluder_(false),
     occludee_(true),
     updateQueued_(false),
+    zoneDirty_(false),
+    octant_(0),
+    zone_(0),
     viewMask_(DEFAULT_VIEWMASK),
     lightMask_(DEFAULT_LIGHTMASK),
     shadowMask_(DEFAULT_SHADOWMASK),
@@ -77,10 +80,7 @@ Drawable::Drawable(Context* context, unsigned char drawableFlags) :
     lodBias_(1.0f),
     basePassFlags_(0),
     maxLights_(0),
-    octant_(0),
-    firstLight_(0),
-    zone_(0),
-    zoneDirty_(false)
+    firstLight_(0)
 {
 }
 
@@ -294,33 +294,30 @@ void Drawable::SetSortValue(float value)
     sortValue_ = value;
 }
 
-void Drawable::SetMinMaxZ(float minZ, float maxZ)
-{
-    minZ_ = minZ;
-    maxZ_ = maxZ;
-}
-
 void Drawable::MarkInView(const FrameInfo& frame)
 {
     if (frame.frameNumber_ != viewFrameNumber_)
     {
         viewFrameNumber_ = frame.frameNumber_;
-        viewCameras_.Clear();
+        viewCameras_.Resize(1);
+        viewCameras_[0] = frame.camera_;
     }
-    
-    viewCameras_.Insert(frame.camera_);
+    else
+        viewCameras_.Push(frame.camera_);
+
+    basePassFlags_ = 0;
+    firstLight_ = 0;
+    lights_.Clear();
+    vertexLights_.Clear();
 }
 
-void Drawable::MarkInView(unsigned frameNumber, Camera* camera)
+void Drawable::MarkInView(unsigned frameNumber)
 {
     if (frameNumber != viewFrameNumber_)
     {
         viewFrameNumber_ = frameNumber;
         viewCameras_.Clear();
     }
-    
-    if (camera)
-        viewCameras_.Insert(camera);
 }
 
 void Drawable::LimitLights()
@@ -339,13 +336,22 @@ void Drawable::LimitLights()
     lights_.Resize(maxLights_);
 }
 
-void Drawable::LimitVertexLights()
+void Drawable::LimitVertexLights(bool removeConvertedLights)
 {
+    if (removeConvertedLights)
+    {
+        for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i)
+        {
+            if (!vertexLights_[i]->GetPerVertex())
+                vertexLights_.Erase(i);
+        }
+    }
+
     if (vertexLights_.Size() <= MAX_VERTEX_LIGHTS)
         return;
 
     const BoundingBox& box = GetWorldBoundingBox();
-    for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i)
+    for (unsigned i = 0; i < vertexLights_.Size(); ++i)
         vertexLights_[i]->SetIntensitySortValue(box);
 
     Sort(vertexLights_.Begin(), vertexLights_.End(), CompareDrawables);

+ 22 - 28
Source/Atomic/Graphics/Drawable.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,6 @@
 #include "../Math/BoundingBox.h"
 #include "../Scene/Component.h"
 #include "../Graphics/GraphicsDefs.h"
-#include "../Container/HashSet.h"
 
 namespace Atomic
 {
@@ -201,15 +200,15 @@ public:
     /// Set sorting value.
     void SetSortValue(float value);
     /// Set view-space depth bounds.
-    void SetMinMaxZ(float minZ, float maxZ);
-    /// Mark in view.
+    void SetMinMaxZ(float minZ, float maxZ) { minZ_ = minZ; maxZ_ = maxZ; }
+    /// Mark in view. Also clear the light list.
     void MarkInView(const FrameInfo& frame);
-    /// Mark in view of a specific camera. Specify null camera to update just the frame number.
-    void MarkInView(unsigned frameNumber, Camera* camera);
+    /// Mark in view without specifying a camera. Used for shadow casters.
+    void MarkInView(unsigned frameNumber);
     /// Sort and limit per-pixel lights to maximum allowed. Convert extra lights into vertex lights.
     void LimitLights();
     /// Sort and limit per-vertex lights to maximum allowed.
-    void LimitVertexLights();
+    void LimitVertexLights(bool removeConvertedLights);
     /// Set base pass flag for a batch.
     void SetBasePass(unsigned batchIndex) { basePassFlags_ |= (1 << batchIndex); }
     /// Return octree octant.
@@ -239,21 +238,16 @@ public:
     /// Return the maximum view-space depth.
     float GetMaxZ() const { return maxZ_; }
     
-    // Clear the frame's light list.
-    void ClearLights()
-    {
-        basePassFlags_ = 0;
-        firstLight_ = 0;
-        lights_.Clear();
-        vertexLights_.Clear();
-    }
-
     // Add a per-pixel light affecting the object this frame.
     void AddLight(Light* light)
     {
-        if (lights_.Empty())
+        if (!firstLight_)
             firstLight_ = light;
-        lights_.Push(light);
+
+        // Need to store into the light list only if the per-pixel lights are being limited.
+        // Otherwise recording the first light is enough
+        if (maxLights_)
+            lights_.Push(light);
     }
 
     // Add a per-vertex light affecting the object this frame.
@@ -296,6 +290,12 @@ protected:
     bool occludee_;
     /// Octree update queued flag.
     bool updateQueued_;
+    /// Zone inconclusive or dirtied flag.
+    bool zoneDirty_;
+    /// Octree octant.
+    Octant* octant_;
+    /// Current zone.
+    Zone* zone_;
     /// View mask.
     unsigned viewMask_;
     /// Light mask.
@@ -322,24 +322,18 @@ protected:
     float maxZ_;
     /// LOD bias.
     float lodBias_;
-    /// Base pass flags.
+    /// Base pass flags, bit per batch.
     unsigned basePassFlags_;
-    /// Maximum lights.
+    /// Maximum per-pixel lights.
     unsigned maxLights_;
-    /// Octree octant.
-    Octant* octant_;
+    /// List of cameras from which is seen on the current frame.
+    PODVector<Camera*> viewCameras_;
     /// First per-pixel light added this frame.
     Light* firstLight_;
     /// Per-pixel lights affecting this drawable.
     PODVector<Light*> lights_;
     /// Per-vertex lights affecting this drawable.
     PODVector<Light*> vertexLights_;
-    /// Current zone.
-    Zone* zone_;
-    /// Zone inconclusive or dirtied flag.
-    bool zoneDirty_;
-    /// Set of cameras from which is seen on the current frame.
-    HashSet<Camera*> viewCameras_;
 };
 
 inline bool CompareDrawables(Drawable* lhs, Drawable* rhs)

+ 4 - 2
Source/Atomic/Graphics/GPUObject.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLGPUObject.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11GPUObject.h"
 #else
 #include "Direct3D9/D3D9GPUObject.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/Graphics.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLGraphics.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11Graphics.h"
 #else
 #include "Direct3D9/D3D9Graphics.h"
 #endif

+ 2 - 15
Source/Atomic/Graphics/GraphicsDefs.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -35,6 +35,7 @@ extern ATOMIC_API const StringHash VSP_AMBIENTENDCOLOR("AmbientEndColor");
 extern ATOMIC_API const StringHash VSP_BILLBOARDROT("BillboardRot");
 extern ATOMIC_API const StringHash VSP_CAMERAPOS("CameraPos");
 extern ATOMIC_API const StringHash VSP_CAMERAROT("CameraRot");
+extern ATOMIC_API const StringHash VSP_CLIPPLANE("ClipPlane");
 extern ATOMIC_API const StringHash VSP_NEARCLIP("NearClip");
 extern ATOMIC_API const StringHash VSP_FARCLIP("FarClip");
 extern ATOMIC_API const StringHash VSP_DEPTHMODE("DepthMode");
@@ -77,20 +78,6 @@ extern ATOMIC_API const StringHash PSP_SHADOWMAPINVSIZE("ShadowMapInvSize");
 extern ATOMIC_API const StringHash PSP_SHADOWSPLITS("ShadowSplits");
 extern ATOMIC_API const StringHash PSP_LIGHTMATRICES("LightMatricesPS");
 
-extern ATOMIC_API const StringHash PASS_BASE("base");
-extern ATOMIC_API const StringHash PASS_LITBASE("litbase");
-extern ATOMIC_API const StringHash PASS_LIGHT("light");
-extern ATOMIC_API const StringHash PASS_ALPHA("alpha");
-extern ATOMIC_API const StringHash PASS_LITALPHA("litalpha");
-extern ATOMIC_API const StringHash PASS_SHADOW("shadow");
-extern ATOMIC_API const StringHash PASS_DEFERRED("deferred");
-extern ATOMIC_API const StringHash PASS_PREPASS("prepass");
-extern ATOMIC_API const StringHash PASS_MATERIAL("material");
-extern ATOMIC_API const StringHash PASS_POSTOPAQUE("postopaque");
-extern ATOMIC_API const StringHash PASS_REFRACT("refract");
-extern ATOMIC_API const StringHash PASS_POSTALPHA("postalpha");
-extern ATOMIC_API const StringHash PASS_LIGHT2D("light2d");
-
 extern ATOMIC_API const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 
 }

+ 25 - 29
Source/Atomic/Graphics/GraphicsDefs.h

@@ -218,17 +218,16 @@ enum ShaderType
     PS,
 };
 
-/// Shader parameter groups for determining need to update.
+/// Shader parameter groups for determining need to update. On APIs that support constant buffers, these correspond to different constant buffers.
 enum ShaderParameterGroup
 {
     SP_FRAME = 0,
     SP_CAMERA,
-    SP_VIEWPORT,
     SP_ZONE,
     SP_LIGHT,
-    SP_VERTEXLIGHTS,
     SP_MATERIAL,
-    SP_OBJECTTRANSFORM,
+    SP_OBJECT,
+    SP_CUSTOM,
     MAX_SHADER_PARAMETER_GROUPS
 };
 
@@ -242,18 +241,30 @@ enum TextureUnit
     TU_SPECULAR = 2,
     TU_EMISSIVE = 3,
     TU_ENVIRONMENT = 4,
-    MAX_MATERIAL_TEXTURE_UNITS = 5,
+#ifdef DESKTOP_GRAPHICS
+    TU_VOLUMEMAP = 5,
+    TU_CUSTOM1 = 6,
+    TU_CUSTOM2 = 7,
+    TU_LIGHTRAMP = 8,
+    TU_LIGHTSHAPE = 9,
+    TU_SHADOWMAP = 10,
+    TU_FACESELECT = 11,
+    TU_INDIRECTION = 12,
+    TU_DEPTHBUFFER = 13,
+    TU_LIGHTBUFFER = 14,
+    TU_ZONE = 15,
+    MAX_MATERIAL_TEXTURE_UNITS = 8,
+    MAX_TEXTURE_UNITS = 16
+#else
+    #error JSBind has a enum collision as doesn't know about "DESKTOP_GRAPHICS
+    /*
     TU_LIGHTRAMP = 5,
     TU_LIGHTSHAPE = 6,
     TU_SHADOWMAP = 7,
-    TU_FACESELECT = 8,
-    TU_INDIRECTION = 9,
-    TU_DEPTHBUFFER = 10,
-    TU_LIGHTBUFFER = 11,
-    TU_VOLUMEMAP = 12,
-    TU_ZONE = 13,
-    MAX_NAMED_TEXTURE_UNITS = 14,
-    MAX_TEXTURE_UNITS = 16
+    MAX_MATERIAL_TEXTURE_UNITS = 5,
+    MAX_TEXTURE_UNITS = 8
+    */
+#endif
 };
 
 /// Billboard camera facing modes.
@@ -272,6 +283,7 @@ extern ATOMIC_API const StringHash VSP_AMBIENTENDCOLOR;
 extern ATOMIC_API const StringHash VSP_BILLBOARDROT;
 extern ATOMIC_API const StringHash VSP_CAMERAPOS;
 extern ATOMIC_API const StringHash VSP_CAMERAROT;
+extern ATOMIC_API const StringHash VSP_CLIPPLANE;
 extern ATOMIC_API const StringHash VSP_NEARCLIP;
 extern ATOMIC_API const StringHash VSP_FARCLIP;
 extern ATOMIC_API const StringHash VSP_DEPTHMODE;
@@ -314,21 +326,6 @@ extern ATOMIC_API const StringHash PSP_SHADOWMAPINVSIZE;
 extern ATOMIC_API const StringHash PSP_SHADOWSPLITS;
 extern ATOMIC_API const StringHash PSP_LIGHTMATRICES;
 
-// Inbuilt pass types
-extern ATOMIC_API const StringHash PASS_BASE;
-extern ATOMIC_API const StringHash PASS_LITBASE;
-extern ATOMIC_API const StringHash PASS_LIGHT;
-extern ATOMIC_API const StringHash PASS_ALPHA;
-extern ATOMIC_API const StringHash PASS_LITALPHA;
-extern ATOMIC_API const StringHash PASS_SHADOW;
-extern ATOMIC_API const StringHash PASS_DEFERRED;
-extern ATOMIC_API const StringHash PASS_PREPASS;
-extern ATOMIC_API const StringHash PASS_MATERIAL;
-extern ATOMIC_API const StringHash PASS_POSTOPAQUE;
-extern ATOMIC_API const StringHash PASS_REFRACT;
-extern ATOMIC_API const StringHash PASS_POSTALPHA;
-extern ATOMIC_API const StringHash PASS_LIGHT2D;
-
 // Scale calculation from bounding box diagonal.
 extern ATOMIC_API const Vector3 DOT_SCALE;
 
@@ -365,7 +362,6 @@ static const unsigned NO_ELEMENT = 0xffffffff;
 
 static const int MAX_RENDERTARGETS = 4;
 static const int MAX_VERTEX_STREAMS = 4;
-static const int MAX_SKIN_MATRICES = 64;
 static const int MAX_CONSTANT_REGISTERS = 256;
 
 static const int BITS_PER_COMPONENT = 8;

+ 0 - 5
Source/Atomic/Graphics/GraphicsEvents.h

@@ -44,11 +44,6 @@ EVENT(E_WINDOWPOS, WindowPos)
     PARAM(P_Y, Y);                          // int
 }
 
-/// Graphics features checked.
-EVENT(E_GRAPHICSFEATURES, GraphicsFeatures)
-{
-}
-
 /// Request for queuing autoupdated rendersurfaces.
 EVENT(E_RENDERSURFACEUPDATE, RenderSurfaceUpdate)
 {

+ 4 - 2
Source/Atomic/Graphics/GraphicsImpl.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLGraphicsImpl.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11GraphicsImpl.h"
 #else
 #include "Direct3D9/D3D9GraphicsImpl.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/IndexBuffer.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLIndexBuffer.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11IndexBuffer.h"
 #else
 #include "Direct3D9/D3D9IndexBuffer.h"
 #endif

+ 3 - 12
Source/Atomic/Graphics/Light.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -397,16 +397,7 @@ int Light::GetNumShadowSplits() const
         }
     }
 
-    ret = Min(ret, MAX_CASCADE_SPLITS);
-    // Shader Model 2 can only support 3 splits max. due to pixel shader instruction count limits
-    if (ret == 4)
-    {
-        Graphics* graphics = GetSubsystem<Graphics>();
-        if (graphics && !graphics->GetSM3Support())
-            --ret;
-    }
-
-    return ret;
+    return Min(ret, MAX_CASCADE_SPLITS);
 }
 
 const Matrix3x4& Light::GetVolumeTransform(Camera* camera)
@@ -557,7 +548,7 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
             float distance = lightRay.HitDistance(box);
             float normDistance = distance / range_;
             float att = Max(1.0f - normDistance * normDistance, M_EPSILON);
-            sortValue_ = 1.0f / (Max(color_.SumRGB(), 0.0f) * att + M_EPSILON);
+            sortValue_ = 1.0f / GetIntensityDivisor(att);
         }
         break;
     }

+ 65 - 24
Source/Atomic/Graphics/Material.cpp

@@ -36,6 +36,7 @@
 #include "../Core/StringUtils.h"
 #include "../Graphics/Technique.h"
 #include "../Graphics/Texture2D.h"
+#include "../Graphics/Texture3D.h"
 #include "../Graphics/TextureCube.h"
 #include "../Scene/ValueAnimation.h"
 #include "../Resource/XMLFile.h"
@@ -54,6 +55,10 @@ static const char* textureUnitNames[] =
     "specular",
     "emissive",
     "environment",
+#ifdef DESKTOP_GRAPHICS
+    "volume",
+    "custom1",
+    "custom2",
     "lightramp",
     "lightshape",
     "shadowmap",
@@ -61,9 +66,14 @@ static const char* textureUnitNames[] =
     "indirection",
     "depth",
     "light",
-    "volume",
     "zone",
     0
+#else
+    "lightramp",
+    "lightshape",
+    "shadowmap",
+    0
+#endif
 };
 
 static const char* cullModeNames[] =
@@ -74,6 +84,14 @@ static const char* cullModeNames[] =
     0
 };
 
+static const char* fillModeNames[] =
+{
+    "solid",
+    "wireframe",
+    "point",
+    0
+};
+
 TextureUnit ParseTextureUnitName(String name)
 {
     name = name.ToLower().Trimmed();
@@ -154,7 +172,6 @@ void ShaderParameterAnimationInfo::ApplyValue(const Variant& newValue)
 Material::Material(Context* context) :
     Resource(context),
     auxViewFrameNumber_(0),
-    numUsedTextureUnits_(0),
     shaderParameterHash_(0),
     occlusion_(true),
     specular_(false),
@@ -203,7 +220,17 @@ bool Material::BeginLoad(Deserializer& source)
                 // Detect cube maps by file extension: they are defined by an XML file
                 /// \todo Differentiate with 3D textures by actually reading the XML content
                 if (GetExtension(name) == ".xml")
-                    cache->BackgroundLoadResource<TextureCube>(name, true, this);
+                {
+                    #ifdef DESKTOP_GRAPHICS
+                    TextureUnit unit = TU_DIFFUSE;
+                    if (textureElem.HasAttribute("unit"))
+                        unit = ParseTextureUnitName(textureElem.GetAttribute("unit"));
+                    if (unit == TU_VOLUMEMAP)
+                        cache->BackgroundLoadResource<Texture3D>(name, true, this);
+                    else
+                    #endif
+                        cache->BackgroundLoadResource<TextureCube>(name, true, this);
+                }
                 else
                     cache->BackgroundLoadResource<Texture2D>(name, true, this);
                 textureElem = textureElem.GetNext("texture");
@@ -295,7 +322,14 @@ bool Material::Load(const XMLElement& source)
             // Detect cube maps by file extension: they are defined by an XML file
             /// \todo Differentiate with 3D textures by actually reading the XML content
             if (GetExtension(name) == ".xml")
-                SetTexture(unit, cache->GetResource<TextureCube>(name));
+            {
+                #ifdef DESKTOP_GRAPHICS
+                if (unit == TU_VOLUMEMAP)
+                    SetTexture(unit, cache->GetResource<Texture3D>(name));
+                else
+                #endif
+                    SetTexture(unit, cache->GetResource<TextureCube>(name));
+            }
             else
                 SetTexture(unit, cache->GetResource<Texture2D>(name));
         }
@@ -348,6 +382,10 @@ bool Material::Load(const XMLElement& source)
     if (shadowCullElem)
         SetShadowCullMode((CullMode)GetStringListIndex(shadowCullElem.GetAttribute("value").CString(), cullModeNames, CULL_CCW));
 
+    XMLElement fillElem = source.GetChild("fill");
+    if (fillElem)
+        SetFillMode((FillMode)GetStringListIndex(fillElem.GetAttribute("value").CString(), fillModeNames, FILL_SOLID));
+
     XMLElement depthBiasElem = source.GetChild("depthbias");
     if (depthBiasElem)
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
@@ -386,7 +424,7 @@ bool Material::Save(XMLElement& dest) const
         if (texture)
         {
             XMLElement textureElem = dest.CreateChild("texture");
-            textureElem.SetString("unit", j < MAX_NAMED_TEXTURE_UNITS ? textureUnitNames[j] : String(j).CString());
+            textureElem.SetString("unit", textureUnitNames[j]);
             textureElem.SetString("name", texture->GetName());
         }
     }
@@ -419,6 +457,10 @@ bool Material::Save(XMLElement& dest) const
     XMLElement shadowCullElem = dest.CreateChild("shadowcull");
     shadowCullElem.SetString("value", cullModeNames[shadowCullMode_]);
 
+    // Write fill mode
+    XMLElement fillElem = dest.CreateChild("fill");
+    fillElem.SetString("value", fillModeNames[fillMode_]);
+
     // Write depth bias
     XMLElement depthBiasElem = dest.CreateChild("depthbias");
     depthBiasElem.SetFloat("constant", depthBias_.constantBias_);
@@ -527,16 +569,10 @@ void Material::SetTexture(TextureUnit unit, Texture* texture)
 {
     if (unit < MAX_TEXTURE_UNITS)
     {
-        textures_[unit] = texture;
-
-        // Update the number of used texture units
-        if (texture && (unsigned)unit >= numUsedTextureUnits_)
-            numUsedTextureUnits_ = unit + 1;
-        else if (!texture && unit == numUsedTextureUnits_ - 1)
-        {
-            while (numUsedTextureUnits_ && !textures_[numUsedTextureUnits_ - 1])
-                --numUsedTextureUnits_;
-        }
+        if (texture)
+            textures_[unit] = texture;
+        else
+            textures_.Erase(unit);
     }
 }
 
@@ -583,6 +619,11 @@ void Material::SetShadowCullMode(CullMode mode)
     shadowCullMode_ = mode;
 }
 
+void Material::SetFillMode(FillMode mode)
+{
+    fillMode_ = mode;
+}
+
 void Material::SetDepthBias(const BiasParameters& parameters)
 {
     depthBias_ = parameters;
@@ -627,13 +668,12 @@ SharedPtr<Material> Material::Clone(const String& cloneName) const
     ret->SetName(cloneName);
     ret->techniques_ = techniques_;
     ret->shaderParameters_ = shaderParameters_;
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        ret->textures_[i] = textures_[i];
+    ret->textures_ = textures_;
     ret->occlusion_ = occlusion_;
     ret->specular_ = specular_;
     ret->cullMode_ = cullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
-    ret->numUsedTextureUnits_ = numUsedTextureUnits_;
+    ret->fillMode_ = fillMode_;
     ret->RefreshMemoryUse();
 
     return ret;
@@ -659,15 +699,16 @@ Technique* Material::GetTechnique(unsigned index) const
     return index < techniques_.Size() ? techniques_[index].technique_ : (Technique*)0;
 }
 
-Pass* Material::GetPass(unsigned index, StringHash passType) const
+Pass* Material::GetPass(unsigned index, const String& passName) const
 {
     Technique* tech = index < techniques_.Size() ? techniques_[index].technique_ : (Technique*)0;
-    return tech ? tech->GetPass(passType) : 0;
+    return tech ? tech->GetPass(passName) : 0;
 }
 
 Texture* Material::GetTexture(TextureUnit unit) const
 {
-    return unit < MAX_TEXTURE_UNITS ? textures_[unit] : (Texture*)0;
+    HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures_.Find(unit);
+    return i != textures_.End() ? i->second_.Get() : (Texture*)0;
 }
 
 const Variant& Material::GetShaderParameter(const String& name) const
@@ -722,7 +763,7 @@ void Material::CheckOcclusion()
         Technique* tech = techniques_[i].technique_;
         if (tech)
         {
-            Pass* pass = tech->GetPass(PASS_BASE);
+            Pass* pass = tech->GetPass("base");
             if (pass && pass->GetDepthWrite() && !pass->GetAlphaMask())
                 occlusion_ = true;
         }
@@ -738,8 +779,7 @@ void Material::ResetToDefaults()
     SetNumTechniques(1);
     SetTechnique(0, GetSubsystem<ResourceCache>()->GetResource<Technique>("Techniques/NoTexture.xml"));
 
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        textures_[i] = 0;
+    textures_.Clear();
 
     batchedParameterUpdate_ = true;
     shaderParameters_.Clear();
@@ -753,6 +793,7 @@ void Material::ResetToDefaults()
 
     cullMode_ = CULL_CCW;
     shadowCullMode_ = CULL_CCW;
+    fillMode_ = FILL_SOLID;
     depthBias_ = BiasParameters(0.0f, 0.0f);
 
     RefreshShaderParameterHash();

+ 16 - 8
Source/Atomic/Graphics/Material.h

@@ -90,6 +90,12 @@ private:
     String name_;
 };
 
+/// TextureUnit hash function.
+template<> inline unsigned MakeHash(const TextureUnit& value)
+{
+    return (unsigned)value;
+}
+
 /// Describes how to render 3D geometries.
 class ATOMIC_API Material : public Resource
 {
@@ -136,6 +142,8 @@ public:
     void SetCullMode(CullMode mode);
     /// Set culling mode for shadows.
     void SetShadowCullMode(CullMode mode);
+    /// Set polygon fill mode. Interacts with the camera's fill mode setting so that the "least filled" mode will be used.
+    void SetFillMode(FillMode mode);
     /// Set depth bias.
     void SetDepthBias(const BiasParameters& parameters);
     /// Associate the material with a scene to ensure that shader parameter animation happens in sync with scene update, respecting the scene time scale. If no scene is set, the global update events will be used.
@@ -159,12 +167,12 @@ public:
     const TechniqueEntry& GetTechniqueEntry(unsigned index) const;
     /// Return technique by index.
     Technique* GetTechnique(unsigned index) const;
-    /// Return pass by technique index and pass type.
-    Pass* GetPass(unsigned index, StringHash passType) const;
+    /// Return pass by technique index and pass name.
+    Pass* GetPass(unsigned index, const String& passName) const;
     /// Return texture by unit.
     Texture* GetTexture(TextureUnit unit) const;
    /// Return all textures.
-    const SharedPtr<Texture>* GetTextures() const { return &textures_[0]; }
+    const HashMap<TextureUnit, SharedPtr<Texture> >& GetTextures() const { return textures_; }
     /// Return shader parameter.
     const Variant& GetShaderParameter(const String& name) const;
     /// Return shader parameter animation.
@@ -179,6 +187,8 @@ public:
     CullMode GetCullMode() const { return cullMode_; }
     /// Return culling mode for shadows.
     CullMode GetShadowCullMode() const { return shadowCullMode_; }
+    /// Return polygon fill mode.
+    FillMode GetFillMode() const { return fillMode_; }
     /// Return depth bias.
     const BiasParameters& GetDepthBias() const { return depthBias_; }
     /// Return last auxiliary view rendered frame number.
@@ -189,8 +199,6 @@ public:
     bool GetSpecular() const { return specular_; }
     /// Return the scene associated with the material for shader parameter animation updates.
     Scene* GetScene() const;
-    /// Return the last non-null texture unit + 1. Used as an optimization when applying the material to render state.
-    unsigned GetNumUsedTextureUnits() const { return numUsedTextureUnits_; }
     /// Return shader parameter hash value. Used as an optimization to avoid setting shader parameters unnecessarily.
     unsigned GetShaderParameterHash() const { return shaderParameterHash_; }
 
@@ -218,7 +226,7 @@ private:
     /// Techniques.
     Vector<TechniqueEntry> techniques_;
     /// Textures.
-    SharedPtr<Texture> textures_[MAX_TEXTURE_UNITS];
+    HashMap<TextureUnit, SharedPtr<Texture> > textures_;
     /// %Shader parameters.
     HashMap<StringHash, MaterialShaderParameter> shaderParameters_;
     /// %Shader parameters animation infos.
@@ -227,12 +235,12 @@ private:
     CullMode cullMode_;
     /// Culling mode for shadow rendering.
     CullMode shadowCullMode_;
+    /// Polygon fill mode.
+    FillMode fillMode_;
     /// Depth bias parameters.
     BiasParameters depthBias_;
     /// Last auxiliary view rendered frame number.
     unsigned auxViewFrameNumber_;
-    /// Number of maximum non-null texture unit + 1.
-    unsigned numUsedTextureUnits_;
     /// Shader parameter hash value.
     unsigned shaderParameterHash_;
     /// Render occlusion flag.

+ 1 - 1
Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.cpp

@@ -27,7 +27,7 @@
 
 #include "../../DebugNew.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 

+ 2 - 2
Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.h

@@ -27,11 +27,11 @@
 #include "../../Container/ArrayPtr.h"
 #include "../../Core/Object.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 /// Hardware constant buffer.
-class URHO3D_API ConstantBuffer : public Object, public GPUObject
+class ATOMIC_API ConstantBuffer : public Object, public GPUObject
 {
     OBJECT(ConstantBuffer);
     

File diff suppressed because it is too large
+ 322 - 316
Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp


+ 61 - 42
Source/Atomic/Graphics/OpenGL/OGLGraphics.h

@@ -34,6 +34,7 @@
 namespace Atomic
 {
 
+class ConstantBuffer;
 class File;
 class Image;
 class IndexBuffer;
@@ -54,7 +55,6 @@ class VertexBuffer;
 typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
 
 static const unsigned NUM_SCREEN_BUFFERS = 2;
-static const unsigned NUM_TEMP_MATRICES = 8;
 
 /// CPU-side scratch buffer for vertex data updates.
 struct ScratchBuffer
@@ -108,6 +108,8 @@ public:
     void SetSRGB(bool enable);
     /// Set whether to flush the GPU command buffer to prevent multiple frames being queued and uneven frame timesteps. Not yet implemented on OpenGL.
     void SetFlushGPU(bool enable);
+    /// Set forced use of OpenGL 2 even if OpenGL 3 is available. Must be called before setting the screen mode for the first time. Default false.
+    void SetForceGL2(bool enable);
     /// Set allowed screen orientations as a space-separated list of "LandscapeLeft", "LandscapeRight", "Portrait" and "PortraitUpsideDown". Affects currently only iOS platform.
     void SetOrientations(const String& orientations);
     /// Toggle between full screen and windowed mode. Return true if successful.
@@ -172,8 +174,6 @@ public:
     void ClearParameterSources();
     /// Clear remembered transform shader parameter sources.
     void ClearTransformSources();
-    /// Clean up unused shader programs.
-    void CleanupShaderPrograms();
     /// Set texture.
     void SetTexture(unsigned index, Texture* texture);
     /// Bind texture unit 0 for update. Called by Texture.
@@ -212,8 +212,6 @@ public:
     void SetDepthTest(CompareMode mode);
     /// Set depth write on/off.
     void SetDepthWrite(bool enable);
-    /// Set antialiased drawing mode on/off. Default is on if the backbuffer is multisampled. Has no effect when backbuffer is not multisampled.
-    void SetDrawAntialiased(bool enable);
     /// Set polygon fill mode.
     void SetFillMode(FillMode mode);
     /// Set scissor test.
@@ -224,12 +222,6 @@ public:
     void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
     /// Set a custom clipping plane. The plane is specified in world space, but is dependent on the view and projection matrices.
     void SetClipPlane(bool enable, const Plane& clipPlane = Plane::UP, const Matrix3x4& view = Matrix3x4::IDENTITY, const Matrix4& projection = Matrix4::IDENTITY);
-    /// Set vertex buffer stream frequency. No-op on OpenGL.
-    void SetStreamFrequency(unsigned index, unsigned frequency);
-    /// Reset stream frequencies. No-op on OpenGL.
-    void ResetStreamFrequencies();
-    /// Set force Shader Model 2 flag. No-op on OpenGL.
-    void SetForceSM2(bool enable);
     /// Begin dumping shader variation names to an XML file for precaching.
     void BeginDumpShaders(const String& fileName);
     /// End dumping shader variations names.
@@ -245,6 +237,8 @@ public:
     void* GetExternalWindow() const { return externalWindow_; }
     /// Return window title.
     const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return graphics API name.
+    const String& GetApiName() const { return apiName_; }
     /// Return window position.
     IntVector2 GetWindowPosition() const;
     /// Return window width.
@@ -267,6 +261,8 @@ public:
     bool GetSRGB() const { return sRGB_; }
     /// Return whether the GPU command buffer is flushed each frame. Not yet implemented on OpenGL.
     bool GetFlushGPU() const { return false; }
+    /// Return whether OpenGL 2 use is forced.
+    bool GetForceGL2() const { return forceGL2_; }
     /// Return allowed screen orientations.
     const String& GetOrientations() const { return orientations_; }
     /// Return whether device is lost, and can not yet render.
@@ -281,8 +277,6 @@ public:
     unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
     /// Return 24-bit shadow map depth texture format, or 0 if not supported.
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
-    /// Return whether Shader Model 3 is supported. Has no meaning on OpenGL, so is assumed to be true.
-    bool GetSM3Support() const { return true; }
     /// Return whether hardware instancing is supported.
     bool GetInstancingSupport() const { return instancingSupport_; }
     /// Return whether light pre-pass rendering is supported.
@@ -295,8 +289,6 @@ public:
     bool GetHardwareShadowSupport() const { return true; }
     /// Return whether a readable hardware depth format is available.
     bool GetReadableDepthSupport() const { return GetReadableDepthFormat() != 0; }
-    /// Return whether stream offset is supported. Always true on OpenGL.
-    bool GetStreamOffsetSupport() const { return true; }
     /// Return whether sRGB conversion on texture sampling is supported.
     bool GetSRGBSupport() const { return sRGBSupport_; }
     /// Return whether sRGB conversion on rendertarget writing is supported.
@@ -355,8 +347,6 @@ public:
     CompareMode GetDepthTest() const { return depthTestMode_; }
     /// Return whether depth write is enabled.
     bool GetDepthWrite() const { return depthWrite_; }
-    /// Return whether antialiased drawing mode is enabled.
-    bool GetDrawAntialiased() const { return drawAntialiased_; }
     /// Return polygon fill mode.
     FillMode GetFillMode() const { return fillMode_; }
     /// Return whether stencil test is enabled.
@@ -381,12 +371,8 @@ public:
     unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
     /// Return whether a custom clipping plane is in use.
     bool GetUseClipPlane() const { return useClipPlane_; }
-    /// Return stream frequency by vertex buffer index. Always returns 0 on OpenGL.
-    unsigned GetStreamFrequency(unsigned index) const;
     /// Return rendertarget width and height.
     IntVector2 GetRenderTargetDimensions() const;
-    /// Return force Shader Model 2 flag. Always false on OpenGL.
-    bool GetForceSM2() const { return false; }
 
     /// Window was resized through user interaction. Called by Input subsystem.
     void WindowResized();
@@ -402,6 +388,12 @@ public:
     void FreeScratchBuffer(void* buffer);
     /// Clean up too large scratch buffers.
     void CleanupScratchBuffers();
+    /// Clean up a render surface from all FBOs.
+    void CleanupRenderSurface(RenderSurface* surface);
+    /// Clean up shader programs when a shader variation is released or destroyed.
+    void CleanupShaderPrograms(ShaderVariation* variation);
+    /// Reserve a constant buffer.
+    ConstantBuffer* GetOrCreateConstantBuffer(unsigned bindingIndex, unsigned size);
     /// Release/clear GPU objects and optionally close the window.
     void Release(bool clearGPUObjects, bool closeWindow);
     /// Restore GPU objects and reinitialize state. Requires an open window.
@@ -410,10 +402,12 @@ public:
     void Maximize();
     /// Minimize the Window.
     void Minimize();
-    /// Clean up a render surface from all FBOs.
-    void CleanupRenderSurface(RenderSurface* surface);
     /// Mark the FBO needing an update.
     void MarkFBODirty();
+    /// Bind a VBO, avoiding redundant operation.
+    void SetVBO(unsigned object);
+    /// Bind a UBO, avoiding redundant operation.
+    void SetUBO(unsigned object);
     
     /// Return the API-specific alpha texture format.
     static unsigned GetAlphaFormat();
@@ -449,22 +443,40 @@ public:
     static unsigned GetReadableDepthFormat();
     /// Return the API-specific texture format from a textual description, for example "rgb".
     static unsigned GetFormat(const String& formatName);
-    
+    /// Return UV offset required for pixel perfect rendering.
+    static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones();
+    /// Return whether is using an OpenGL 3 context.
+    static bool GetGL3Support() { return gl3Support; }
+
 private:
     /// Create the application window icon.
     void CreateWindowIcon();
     /// Check supported rendering features.
-    void CheckFeatureSupport(String& extensions);
-    /// Select FBO and commit changes.
-    void CommitFramebuffer();
-    /// Check FBO completeness.
-    bool CheckFramebuffer();
-    /// Cleanup unused and unbound FBO's.
-    void CleanupFramebuffers(bool force = false);
+    void CheckFeatureSupport();
+    /// Prepare for draw call. Update constant buffers and setup the FBO.
+    void PrepareDraw();
+    /// Clean up all framebuffers. Called when destroying the context.
+    void CleanupFramebuffers();
     /// Reset cached rendering state.
     void ResetCachedState();
     /// Initialize texture unit mappings.
     void SetTextureUnitMappings();
+    /// Create a framebuffer using either extension or core functionality.
+    unsigned CreateFramebuffer();
+    /// Delete a framebuffer using either extension or core functionality.
+    void DeleteFramebuffer(unsigned fbo);
+    /// Bind a framebuffer using either extension or core functionality.
+    void BindFramebuffer(unsigned fbo);
+    /// Bind a framebuffer color attachment using either extension or core functionality.
+    void BindColorAttachment(unsigned index, unsigned target, unsigned object);
+    /// Bind a framebuffer depth attachment using either extension or core functionality.
+    void BindDepthAttachment(unsigned object, bool isRenderBuffer);
+    /// Bind a framebuffer stencil attachment using either extension or core functionality.
+    void BindStencilAttachment(unsigned object, bool isRenderBuffer);
+    /// Check FBO completeness using either extension or core functionality.
+    bool CheckFramebuffer();
     
     /// Mutex for accessing the GPU objects vector from several threads.
     Mutex gpuObjectMutex_;
@@ -496,6 +508,8 @@ private:
     bool tripleBuffer_;
     /// sRGB conversion on write flag for the main window.
     bool sRGB_;
+    /// Force OpenGL 2 use flag.
+    bool forceGL2_;
     /// Instancing support flag.
     bool instancingSupport_;
     /// Light prepass support flag.
@@ -521,7 +535,7 @@ private:
     /// Largest scratch buffer request this frame.
     unsigned maxScratchBufferRequest_;
     /// GPU objects.
-    Vector<GPUObject*> gpuObjects_;
+    PODVector<GPUObject*> gpuObjects_;
     /// Scratch buffers.
     Vector<ScratchBuffer> scratchBuffers_;
     /// Shadow map dummy color texture format.
@@ -550,6 +564,12 @@ private:
     unsigned textureTypes_[MAX_TEXTURE_UNITS];
     /// Texture unit mappings.
     HashMap<String, TextureUnit> textureUnits_;
+    /// All constant buffers.
+    HashMap<unsigned, SharedPtr<ConstantBuffer> > constantBuffers_;
+    /// Currently bound constant buffers.
+    ConstantBuffer* currentConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
+    /// Dirty constant buffers.
+    PODVector<ConstantBuffer*> dirtyConstantBuffers_;
     /// Rendertargets in use.
     RenderSurface* renderTargets_[MAX_RENDERTARGETS];
     /// Depth-stencil surface in use.
@@ -578,6 +598,8 @@ private:
     IntRect scissorRect_;
     /// Scissor test enable flag.
     bool scissorTest_;
+    /// Current custom clip plane in post-projection space.
+    Vector4 clipPlane_;
     /// Stencil test compare mode.
     CompareMode stencilTestMode_;
     /// Stencil operation on pass.
@@ -596,22 +618,12 @@ private:
     bool stencilTest_;
     /// Custom clip plane enable flag.
     bool useClipPlane_;
-    /// Draw antialiased mode flag.
-    bool drawAntialiased_;
-    /// Releasing GPU objects flag.
-    bool releasingGPUObjects_;
     /// Last used instance data offset.
     unsigned lastInstanceOffset_;
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
     /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.
     HashMap<int, SharedPtr<Texture2D> > depthTextures_;
-    /// Remembered shader parameter sources.
-    const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
-    /// Temp matrices for transposing shader parameters.
-    Matrix3 tempMatrices3_[NUM_TEMP_MATRICES];
-    /// Temp matrices for transposing shader parameters.
-    Matrix4 tempMatrices4_[NUM_TEMP_MATRICES];
     /// Base directory for shaders.
     String shaderPath_;
     /// File extension for shaders.
@@ -624,6 +636,13 @@ private:
     SharedPtr<ShaderPrecache> shaderPrecache_;
     /// Allowed screen orientations.
     String orientations_;
+    /// Graphics API name.
+    String apiName_;
+
+    /// Pixel perfect UV offset.
+    static const Vector2 pixelUVOffset;
+    /// Flag for OpenGL 3 support.
+    static bool gl3Support;
 };
 
 /// Register Graphics library objects.

+ 5 - 3
Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -32,10 +32,12 @@ namespace Atomic
 GraphicsImpl::GraphicsImpl() :
     window_(0),
     context_(0),
-    systemFbo_(0),
+    systemFBO_(0),
     activeTexture_(0),
     enabledAttributes_(0),
-    boundFbo_(0),
+    boundFBO_(0),
+    boundVBO_(0),
+    boundUBO_(0),
     pixelFormat_(0),
     fboDirty_(false)
 {

+ 12 - 4
Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -39,6 +39,12 @@
 #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
 #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83f1
 #endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83f2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83f3
+#endif
 #ifndef GL_ETC1_RGB8_OES
 #define GL_ETC1_RGB8_OES 0x8d64
 #endif
@@ -85,8 +91,6 @@ struct FrameBufferObject
     unsigned readBuffers_;
     /// Draw buffer bits.
     unsigned drawBuffers_;
-    /// Use timer for cleaning up.
-    Timer useTimer_;
 };
 
 /// %Graphics subsystem implementation. Holds API-specific objects.
@@ -106,13 +110,17 @@ private:
     /// SDL OpenGL context.
     SDL_GLContext context_;
     /// IOS system framebuffer handle.
-    unsigned systemFbo_;
+    unsigned systemFBO_;
     /// Active texture unit.
     unsigned activeTexture_;
     /// Vertex attributes in use.
     unsigned enabledAttributes_;
     /// Currently bound frame buffer object.
-    unsigned boundFbo_;
+    unsigned boundFBO_;
+    /// Currently bound vertex buffer object.
+    unsigned boundVBO_;
+    /// Currently bound uniform buffer object.
+    unsigned boundUBO_;
     /// Current pixel format.
     int pixelFormat_;
     /// Map for FBO's per resolution and format.

+ 4 - 8
Source/Atomic/Graphics/OpenGL/OGLIndexBuffer.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -144,8 +144,7 @@ bool IndexBuffer::SetData(const void* data)
     {
         if (!graphics_->IsDeviceLost())
         {
-            graphics_->SetIndexBuffer(0);
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+            graphics_->SetIndexBuffer(this);
             glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
         }
         else
@@ -192,8 +191,7 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
     {
         if (!graphics_->IsDeviceLost())
         {
-            graphics_->SetIndexBuffer(0);
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+            graphics_->SetIndexBuffer(this);
             if (!discard || start != 0)
                 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start * indexSize_, count * indexSize_, data);
             else
@@ -334,8 +332,6 @@ bool IndexBuffer::Create()
             return true;
         }
         
-        graphics_->SetIndexBuffer(0);
-        
         if (!object_)
             glGenBuffers(1, &object_);
         if (!object_)
@@ -344,7 +340,7 @@ bool IndexBuffer::Create()
             return false;
         }
         
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+        graphics_->SetIndexBuffer(this);
         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
     

+ 185 - 11
Source/Atomic/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -21,23 +21,41 @@
 //
 
 #include "Precompiled.h"
+#include "../../Graphics/ConstantBuffer.h"
 #include "../../Graphics/Graphics.h"
 #include "../../Graphics/GraphicsImpl.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
+#include "../../IO/Log.h"
 
 #include "../../DebugNew.h"
 
 namespace Atomic
 {
 
+const char* shaderParameterGroups[] = {
+    "frame",
+    "camera",
+    "zone",
+    "light",
+    "material",
+    "object",
+    "custom"
+};
+
+unsigned ShaderProgram::globalFrameNumber = 0;
+const void* ShaderProgram::globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
+
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
     GPUObject(graphics),
     vertexShader_(vertexShader),
-    pixelShader_(pixelShader)
+    pixelShader_(pixelShader),
+    frameNumber_(0)
 {
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        parameterSources_[i] = (const void*)M_MAX_UNSIGNED;
 }
 
 ShaderProgram::~ShaderProgram()
@@ -51,7 +69,6 @@ void ShaderProgram::OnDeviceLost()
     
     if (graphics_ && graphics_->GetShaderProgram() == this)
         graphics_->SetShaders(0, 0);
-    
 
     linkerOutput_.Clear();
 }
@@ -77,6 +94,8 @@ void ShaderProgram::Release()
         
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
             useTextureUnit_[i] = false;
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+            constantBuffers_[i].Reset();
     }
 }
 
@@ -141,6 +160,75 @@ bool ShaderProgram::Link()
     glUseProgram(object_);
     glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &uniformCount);
     
+    // Check for constant buffers
+    #ifndef GL_ES_VERSION_2_0
+    HashMap<unsigned, unsigned> blockToBinding;
+
+    if (Graphics::GetGL3Support())
+    {
+        int numUniformBlocks = 0;
+
+        glGetProgramiv(object_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
+        for (int i = 0; i < numUniformBlocks; ++i)
+        {
+            int nameLength;
+            glGetActiveUniformBlockName(object_, i, MAX_PARAMETER_NAME_LENGTH, &nameLength, uniformName);
+
+            String name(uniformName, nameLength);
+
+            unsigned blockIndex = glGetUniformBlockIndex(object_, name.CString());
+            unsigned group = M_MAX_UNSIGNED;
+
+            // Try to recognize the use of the buffer from its name
+            for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j)
+            {
+                if (name.Contains(shaderParameterGroups[j], false))
+                {
+                    group = j;
+                    break;
+                }
+            }
+
+            // If name is not recognized, search for a digit in the name and use that as the group index
+            if (group == M_MAX_UNSIGNED)
+            {
+                for (unsigned j = 1; j < name.Length(); ++j)
+                {
+                    if (name[j] >= '0' && name[j] <= '5')
+                    {
+                        group = name[j] - '0';
+                        break;
+                    }
+                }
+            }
+
+            if (group >= MAX_SHADER_PARAMETER_GROUPS)
+            {
+                LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() +
+                    " " + pixelShader_->GetFullName());
+                continue;
+            }
+
+            // Find total constant buffer data size
+            int dataSize;
+            glGetActiveUniformBlockiv(object_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
+            if (!dataSize)
+                continue;
+
+            unsigned bindingIndex = group;
+            // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings
+            // from that point onward
+            if (name.Contains("PS", false))
+                bindingIndex += MAX_SHADER_PARAMETER_GROUPS;
+
+            glUniformBlockBinding(object_, blockIndex, bindingIndex);
+            blockToBinding[blockIndex] = bindingIndex;
+
+            constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(bindingIndex, dataSize);
+        }
+    }
+    #endif
+
     // Check for shader parameters and texture units
     for (int i = 0; i < uniformCount; ++i)
     {
@@ -150,10 +238,6 @@ bool ShaderProgram::Link()
         glGetActiveUniform(object_, i, MAX_PARAMETER_NAME_LENGTH, 0, &count, &type, uniformName);
         int location = glGetUniformLocation(object_, uniformName);
         
-        // Skip inbuilt or disabled uniforms
-        if (location < 0)
-            continue;
-        
         // Check for array index included in the name and strip it
         String name(uniformName);
         unsigned index = name.Find('[');
@@ -168,14 +252,31 @@ bool ShaderProgram::Link()
         
         if (name[0] == 'c')
         {
-            // Store the constant uniform mapping
+            // Store constant uniform
             String paramName = name.Substring(1);
             ShaderParameter newParam;
-            newParam.location_ = location;
             newParam.type_ = type;
-            shaderParameters_[StringHash(paramName)] = newParam;
+            newParam.location_ = location;
+
+            #ifndef GL_ES_VERSION_2_0
+            // If running OpenGL 3, the uniform may be inside a constant buffer
+            if (newParam.location_ < 0 && Graphics::GetGL3Support())
+            {
+                int blockIndex, blockOffset;
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset);
+                if (blockIndex >= 0)
+                {
+                    newParam.location_ = blockOffset;
+                    newParam.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]];
+                }
+            }
+            #endif
+
+            if (newParam.location_ >= 0)
+                shaderParameters_[StringHash(paramName)] = newParam;
         }
-        else if (name[0] == 's')
+        else if (location >= 0 && name[0] == 's')
         {
             // Set the samplers here so that they do not have to be set later
             int unit = graphics_->GetTextureUnit(name.Substring(1));
@@ -230,4 +331,77 @@ const ShaderParameter* ShaderProgram::GetParameter(StringHash param) const
         return 0;
 }
 
+bool ShaderProgram::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
+{
+    // If global framenumber has changed, invalidate all per-program parameter sources now
+    if (globalFrameNumber != frameNumber_)
+    {
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+            parameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+        frameNumber_ = globalFrameNumber;
+    }
+
+    // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
+    #ifndef GL_ES_VERSION_2_0
+    bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool needUpdate = false;
+
+    if (useBuffer && globalParameterSources[group] != source)
+    {
+        globalParameterSources[group] = source;
+        needUpdate = true;
+    }
+
+    if (useIndividual && parameterSources_[group] != source)
+    {
+        parameterSources_[group] = source;
+        needUpdate = true;
+    }
+
+    return needUpdate;
+    #else
+    if (parameterSources_[group] != source)
+    {
+        parameterSources_[group] = source;
+        return true;
+    }
+    else
+        return false;
+    #endif
+}
+
+void ShaderProgram::ClearParameterSource(ShaderParameterGroup group)
+{
+    // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
+    #ifndef GL_ES_VERSION_2_0
+    bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+
+    if (useBuffer)
+        globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
+    if (useIndividual)
+        parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+    #else
+    parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+    #endif
+}
+
+void ShaderProgram::ClearParameterSources()
+{
+    ++globalFrameNumber;
+    if (!globalFrameNumber)
+        ++globalFrameNumber;
+
+    #ifndef GL_ES_VERSION_2_0
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        globalParameterSources[i] = (const void*)M_MAX_UNSIGNED;
+    #endif
+}
+
+void ShaderProgram::ClearGlobalParameterSource(ShaderParameterGroup group)
+{
+    globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
+}
+
 }

+ 34 - 2
Source/Atomic/Graphics/OpenGL/OGLShaderProgram.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -30,16 +30,25 @@
 namespace Atomic
 {
 
+class ConstantBuffer;
 class Graphics;
 class ShaderVariation;
 
 /// %Shader parameter definition.
 struct ShaderParameter
 {
-    /// Uniform location.
+    /// Construct with defaults.
+    ShaderParameter() :
+        bufferPtr_(0)
+    {
+    }
+
+    /// Uniform location or byte offset in constant buffer.
     int location_;
     /// Element type.
     unsigned type_;
+    /// Constant buffer pointer.
+    ConstantBuffer* bufferPtr_;
 };
 
 /// Linked shader program on the GPU.
@@ -71,7 +80,19 @@ public:
     const ShaderParameter* GetParameter(StringHash param) const;
     /// Return linker output.
     const String& GetLinkerOutput() const { return linkerOutput_; }
+    /// Return all constant buffers.
+    const SharedPtr<ConstantBuffer>* GetConstantBuffers() const { return &constantBuffers_[0]; }
     
+    /// Check whether a shader parameter group needs update. Does not actually check whether parameters exist in the shaders.
+    bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
+    /// Clear a parameter source. Affects only the current shader program if appropriate.
+    void ClearParameterSource(ShaderParameterGroup group);
+
+    /// Clear all parameter sources from all shader programs by incrementing the global parameter source framenumber.
+    static void ClearParameterSources();
+    /// Clear a global parameter source when constant buffers change.
+    static void ClearGlobalParameterSource(ShaderParameterGroup group);
+
 private:
     /// Vertex shader.
     WeakPtr<ShaderVariation> vertexShader_;
@@ -81,8 +102,19 @@ private:
     HashMap<StringHash, ShaderParameter> shaderParameters_;
     /// Texture unit use.
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
+    /// Constant buffers by binding index.
+    SharedPtr<ConstantBuffer> constantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
+    /// Remembered shader parameter sources for individual uniform mode.
+    const void* parameterSources_[MAX_SHADER_PARAMETER_GROUPS];
     /// Shader link error string.
     String linkerOutput_;
+    /// Shader parameter source framenumber.
+    unsigned frameNumber_;
+
+    /// Global shader parameter source framenumber.
+    static unsigned globalFrameNumber;
+    /// Remembered global shader parameter sources for constant buffer mode.
+    static const void* globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
 };
 
 }

+ 10 - 5
Source/Atomic/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -50,9 +50,6 @@ void ShaderVariation::OnDeviceLost()
     GPUObject::OnDeviceLost();
 
     compilerOutput_.Clear();
-    
-    if (graphics_)
-        graphics_->CleanupShaderPrograms();
 }
 
 void ShaderVariation::Release()
@@ -79,7 +76,7 @@ void ShaderVariation::Release()
         }
         
         object_ = 0;
-        graphics_->CleanupShaderPrograms();
+        graphics_->CleanupShaderPrograms(this);
     }
     
     compilerOutput_.Clear();
@@ -125,10 +122,16 @@ bool ShaderVariation::Create()
             shaderCode += versionDefine + "\n";
         }
     }
+    // Force GLSL version 150 if no version define and GL3 is being used
+    if (!verEnd && Graphics::GetGL3Support())
+        shaderCode += "#version 150\n";
 
     // Distinguish between VS and PS compile in case the shader code wants to include/omit different things
     shaderCode += type_ == VS ? "#define COMPILEVS\n" : "#define COMPILEPS\n";
-    
+
+    // Add define for the maximum number of supported bones
+    shaderCode += "#define MAXBONES " + String(Graphics::GetMaxBones()) + "\n";
+
     // Prepend the defines to the shader code
     Vector<String> defineVec = defines_.Split(' ');
     for (unsigned i = 0; i < defineVec.Size(); ++i)
@@ -152,6 +155,8 @@ bool ShaderVariation::Create()
     #ifdef EMSCRIPTEN
     shaderCode += "#define WEBGL\n";
     #endif
+    if (Graphics::GetGL3Support())
+        shaderCode += "#define GL3\n";
 
     // When version define found, do not insert it a second time
     if (verEnd > 0)

+ 41 - 20
Source/Atomic/Graphics/OpenGL/OGLTexture.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@
 namespace Atomic
 {
 
-GLenum glWrapModes[] =
+static GLenum glWrapModes[] =
 {
     GL_REPEAT,
     GL_MIRRORED_REPEAT,
@@ -49,6 +49,16 @@ GLenum glWrapModes[] =
     #endif
 };
 
+#ifndef GL_ES_VERSION_2_0
+static GLenum gl3WrapModes[] =
+{
+    GL_REPEAT,
+    GL_MIRRORED_REPEAT,
+    GL_CLAMP_TO_EDGE,
+    GL_CLAMP_TO_BORDER
+};
+#endif
+
 static const char* addressModeNames[] =
 {
     "wrap",
@@ -68,6 +78,15 @@ static const char* filterModeNames[] =
     0
 };
 
+static GLenum GetWrapMode(TextureAddressMode mode)
+{
+    #ifndef GL_ES_VERSION_2_0
+    return Graphics::GetGL3Support() ? gl3WrapModes[mode] : glWrapModes[mode];
+    #else
+    return glWrapModes[mode];
+    #endif
+}
+
 Texture::Texture(Context* context) :
     Resource(context),
     GPUObject(GetSubsystem<Graphics>()),
@@ -171,10 +190,10 @@ void Texture::UpdateParameters()
         return;
     
     // Wrapping
-    glTexParameteri(target_, GL_TEXTURE_WRAP_S, glWrapModes[addressMode_[COORD_U]]);
-    glTexParameteri(target_, GL_TEXTURE_WRAP_T, glWrapModes[addressMode_[COORD_V]]);
+    glTexParameteri(target_, GL_TEXTURE_WRAP_S, GetWrapMode(addressMode_[COORD_U]));
+    glTexParameteri(target_, GL_TEXTURE_WRAP_T, GetWrapMode(addressMode_[COORD_V]));
     #ifndef GL_ES_VERSION_2_0
-    glTexParameteri(target_, GL_TEXTURE_WRAP_R, glWrapModes[addressMode_[COORD_W]]);
+    glTexParameteri(target_, GL_TEXTURE_WRAP_R, GetWrapMode(addressMode_[COORD_W]));
     #endif
     
     TextureFilterMode filterMode = filterMode_;
@@ -240,14 +259,10 @@ int Texture::GetMipsToSkip(int quality) const
 
 bool Texture::IsCompressed() const
 {
-    #ifndef GL_ES_VERSION_2_0
     return format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
-        format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
-    #else
-    return format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_ETC1_RGB8_OES ||
+        format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT || format_ == GL_ETC1_RGB8_OES ||
         format_ == COMPRESSED_RGB_PVRTC_4BPPV1_IMG || format_ == COMPRESSED_RGBA_PVRTC_4BPPV1_IMG ||
         format_ == COMPRESSED_RGB_PVRTC_2BPPV1_IMG || format_ == COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
-    #endif
 }
 
 int Texture::GetLevelWidth(unsigned level) const
@@ -307,14 +322,20 @@ unsigned Texture::GetRowDataSize(int width) const
         
     case GL_RGBA:
     #ifndef GL_ES_VERSION_2_0
-    case GL_LUMINANCE16F_ARB:
-    case GL_LUMINANCE32F_ARB:
     case GL_DEPTH24_STENCIL8_EXT:
     case GL_RG16:
+    case GL_R16F:
+    case GL_R32F:
     #endif
         return width * 4;
         
     #ifndef GL_ES_VERSION_2_0
+    case GL_R8:
+        return width;
+
+    case GL_RG8:
+        return width * 2;
+
     case GL_RGBA16:
         return width * 8;
         
@@ -326,11 +347,10 @@ unsigned Texture::GetRowDataSize(int width) const
     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
         return ((width + 3) >> 2) * 8;
         
-    #ifndef GL_ES_VERSION_2_0
     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
         return ((width + 3) >> 2) * 16;
-    #else
+
     case GL_ETC1_RGB8_OES:
         return ((width + 3) >> 2) * 8;
         
@@ -341,8 +361,7 @@ unsigned Texture::GetRowDataSize(int width) const
     case COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
     case COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
         return (width * 2 + 7) >> 3;
-    #endif
-        
+
     default:
         return 0;
     }
@@ -355,11 +374,13 @@ unsigned Texture::GetExternalFormat(unsigned format)
         return GL_DEPTH_COMPONENT;
     else if (format == GL_DEPTH24_STENCIL8_EXT)
         return GL_DEPTH_STENCIL_EXT;
-    else if (format == GL_LUMINANCE16F_ARB || format == GL_LUMINANCE32F_ARB || format == GL_SLUMINANCE_EXT)
+    else if (format == GL_SLUMINANCE_EXT)
         return GL_LUMINANCE;
     else if (format == GL_SLUMINANCE_ALPHA_EXT)
         return GL_LUMINANCE_ALPHA;
-    else if (format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
+    else if (format == GL_R8 || format == GL_R16F || format == GL_R32F)
+        return GL_RED;
+    else if (format == GL_RG8 || format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
         return GL_RG;
     else if (format == GL_RGBA16 || format == GL_RGBA16F_ARB || format == GL_RGBA32F_ARB || format == GL_SRGB_ALPHA_EXT)
         return GL_RGBA;
@@ -379,8 +400,8 @@ unsigned Texture::GetDataType(unsigned format)
         return GL_UNSIGNED_INT_24_8_EXT;
     else if (format == GL_RG16 || format == GL_RGBA16)
         return GL_UNSIGNED_SHORT;
-    else if (format == GL_LUMINANCE16F_ARB || format == GL_LUMINANCE32F_ARB || format == GL_RGBA16F_ARB ||
-        format == GL_RGBA32F_ARB || format == GL_RG16F || format == GL_RG32F)
+    else if (format == GL_RGBA16F_ARB || format == GL_RGBA32F_ARB || format == GL_RG16F || format == GL_RG32F || format == GL_R16F ||
+        format == GL_R32F)
         return GL_FLOAT;
     else
         return GL_UNSIGNED_BYTE;

+ 4 - 4
Source/Atomic/Graphics/OpenGL/OGLTexture.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -50,7 +50,7 @@ public:
     void SetFilterMode(TextureFilterMode filter);
     /// Set addressing mode by texture coordinate.
     void SetAddressMode(TextureCoordinate coord, TextureAddressMode address);
-    /// Set shadow compare mode, OpenGL only.
+    /// Set shadow compare mode.
     void SetShadowCompare(bool enable);
     /// Set border color for border addressing mode.
     void SetBorderColor(const Color& color);
@@ -85,7 +85,7 @@ public:
     TextureFilterMode GetFilterMode() const { return filterMode_; }
     /// Return addressing mode by texture coordinate.
     TextureAddressMode GetAddressMode(TextureCoordinate coord) const { return addressMode_[coord]; }
-    /// Return whether shadow compare is enabled, OpenGL only.
+    /// Return whether shadow compare is enabled.
     bool GetShadowCompare() const { return shadowCompare_; }
      /// Return border color.
     const Color& GetBorderColor() const { return borderColor_; }
@@ -143,7 +143,7 @@ protected:
     int height_;
     /// Texture depth.
     int depth_;
-    /// Shadow compare mode, OpenGL only.
+    /// Shadow compare mode.
     bool shadowCompare_;
     /// Parameters dirty flag.
     bool parametersDirty_;

+ 11 - 2
Source/Atomic/Graphics/OpenGL/OGLTexture2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -278,10 +278,19 @@ bool Texture2D::SetData(SharedPtr<Image> image, bool useAlpha)
     
     if (!image->IsCompressed())
     {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if (Graphics::GetGL3Support() && ((components == 1 && !useAlpha) || components == 2))
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
         unsigned char* levelData = image->GetData();
         int levelWidth = image->GetWidth();
         int levelHeight = image->GetHeight();
-        unsigned components = image->GetComponents();
         unsigned format = 0;
         
         // Discard unnecessary mip levels

+ 12 - 3
Source/Atomic/Graphics/OpenGL/OGLTexture3D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -285,10 +285,10 @@ bool Texture3D::SetData(unsigned level, int x, int y, int z, int width, int heig
     
     graphics_->SetTextureForUpdate(this);
     
+    #ifndef GL_ES_VERSION_2_0
     bool wholeLevel = x == 0 && y == 0 && z == 0 && width == levelWidth && height == levelHeight && depth == levelDepth;
     unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_;
     
-    #ifndef GL_ES_VERSION_2_0
     if (!IsCompressed())
     {
         if (wholeLevel)
@@ -326,11 +326,20 @@ bool Texture3D::SetData(SharedPtr<Image> image, bool useAlpha)
     
     if (!image->IsCompressed())
     {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if (Graphics::GetGL3Support() && ((components == 1 && !useAlpha) || components == 2))
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
         unsigned char* levelData = image->GetData();
         int levelWidth = image->GetWidth();
         int levelHeight = image->GetHeight();
         int levelDepth = image->GetDepth();
-        unsigned components = image->GetComponents();
         unsigned format = 0;
         
         // Discard unnecessary mip levels

+ 11 - 2
Source/Atomic/Graphics/OpenGL/OGLTextureCube.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -441,10 +441,19 @@ bool TextureCube::SetData(CubeMapFace face, SharedPtr<Image> image, bool useAlph
     
     if (!image->IsCompressed())
     {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if (Graphics::GetGL3Support() && ((components == 1 && !useAlpha) || components == 2))
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
         unsigned char* levelData = image->GetData();
         int levelWidth = image->GetWidth();
         int levelHeight = image->GetHeight();
-        unsigned components = image->GetComponents();
         unsigned format = 0;
         
         if (levelWidth != levelHeight)

+ 5 - 4
Source/Atomic/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -155,6 +155,7 @@ void VertexBuffer::Release()
                     graphics_->SetVertexBuffer(0);
             }
             
+            graphics_->SetVBO(0);
             glDeleteBuffers(1, &object_);
         }
         
@@ -218,7 +219,7 @@ bool VertexBuffer::SetData(const void* data)
     {
         if (!graphics_->IsDeviceLost())
         {
-            glBindBuffer(GL_ARRAY_BUFFER, object_);
+            graphics_->SetVBO(object_);
             glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
         }
         else
@@ -265,7 +266,7 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
     {
         if (!graphics_->IsDeviceLost())
         {
-            glBindBuffer(GL_ARRAY_BUFFER, object_);
+            graphics_->SetVBO(object_);
             if (!discard || start != 0)
                 glBufferSubData(GL_ARRAY_BUFFER, start * vertexSize_, count * vertexSize_, data);
             else
@@ -410,7 +411,7 @@ bool VertexBuffer::Create()
             return false;
         }
         
-        glBindBuffer(GL_ARRAY_BUFFER, object_);
+        graphics_->SetVBO(object_);
         glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
     

+ 42 - 14
Source/Atomic/Graphics/RenderPath.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -60,6 +60,8 @@ void RenderTargetInfo::Load(const XMLElement& element)
     tag_ = element.GetAttribute("tag");
     if (element.HasAttribute("enabled"))
         enabled_ = element.GetBool("enabled");
+    if (element.HasAttribute("cubemap"))
+        cubemap_ = element.GetBool("cubemap");
     
     String formatName = element.GetAttribute("format");
     format_ = Graphics::GetFormat(formatName);
@@ -170,9 +172,12 @@ void RenderPathCommand::Load(const XMLElement& element)
     }
     
     // By default use 1 output, which is the viewport
-    outputNames_.Push("viewport");
+    outputs_.Resize(1);
+    outputs_[0] = MakePair(String("viewport"), FACE_POSITIVE_X);
     if (element.HasAttribute("output"))
-        outputNames_[0] = element.GetAttribute("output");
+        outputs_[0].first_ = element.GetAttribute("output");
+    if (element.HasAttribute("face"))
+        outputs_[0].second_ = (CubeMapFace)element.GetInt("face");
     if (element.HasAttribute("depthstencil"))
         depthStencilName_ = element.GetAttribute("depthstencil");
     // Check for defining multiple outputs
@@ -182,9 +187,10 @@ void RenderPathCommand::Load(const XMLElement& element)
         unsigned index = outputElem.GetInt("index");
         if (index < MAX_RENDERTARGETS)
         {
-            if (index >= outputNames_.Size())
-                outputNames_.Resize(index + 1);
-            outputNames_[index] = outputElem.GetAttribute("name");
+            if (index >= outputs_.Size())
+                outputs_.Resize(index + 1);
+            outputs_[index].first_ = outputElem.GetAttribute("name");
+            outputs_[index].second_ = outputElem.HasAttribute("face") ? (CubeMapFace)outputElem.GetInt("face") : FACE_POSITIVE_X;
         }
         outputElem = outputElem.GetNext("output");
     }
@@ -224,17 +230,34 @@ void RenderPathCommand::RemoveShaderParameter(const String& name)
 void RenderPathCommand::SetNumOutputs(unsigned num)
 {
     num = Clamp((int)num, 1, MAX_RENDERTARGETS);
-    outputNames_.Resize(num);
+    outputs_.Resize(num);
+}
+
+void RenderPathCommand::SetOutput(unsigned index, const String& name, CubeMapFace face)
+{
+    if (index < outputs_.Size())
+        outputs_[index] = MakePair(name, face);
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(name, face));
 }
 
 void RenderPathCommand::SetOutputName(unsigned index, const String& name)
 {
-    if (index < outputNames_.Size())
-        outputNames_[index] = name;
-    else if (index == outputNames_.Size() && index < MAX_RENDERTARGETS)
-        outputNames_.Push(name);
+    if (index < outputs_.Size())
+        outputs_[index].first_ = name;
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(name, FACE_POSITIVE_X));
+}
+
+void RenderPathCommand::SetOutputFace(unsigned index, CubeMapFace face)
+{
+    if (index < outputs_.Size())
+        outputs_[index].second_ = face;
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(String::EMPTY, face));
 }
 
+
 void RenderPathCommand::SetDepthStencilName(const String& name)
 {
     depthStencilName_ = name;
@@ -253,7 +276,12 @@ const Variant& RenderPathCommand::GetShaderParameter(const String& name) const
 
 const String& RenderPathCommand::GetOutputName(unsigned index) const
 {
-    return index < outputNames_.Size() ? outputNames_[index] : String::EMPTY;
+    return index < outputs_.Size() ? outputs_[index].first_ : String::EMPTY;
+}
+
+CubeMapFace RenderPathCommand::GetOutputFace(unsigned index) const
+{
+    return index < outputs_.Size() ? outputs_[index].second_ : FACE_POSITIVE_X;
 }
 
 RenderPath::RenderPath()
@@ -264,9 +292,9 @@ RenderPath::~RenderPath()
 {
 }
 
-RenderPath *RenderPath::Clone()
+SharedPtr<RenderPath> RenderPath::Clone()
 {
-    RenderPath* newRenderPath = new RenderPath();
+    SharedPtr<RenderPath> newRenderPath(new RenderPath());
     newRenderPath->renderTargets_ = renderTargets_;
     newRenderPath->commands_ = commands_;
     return newRenderPath;

+ 16 - 5
Source/Atomic/Graphics/RenderPath.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -69,6 +69,7 @@ struct RenderTargetInfo
         size_(Vector2::ZERO),
         sizeMode_(SIZE_ABSOLUTE),
         enabled_(true),
+        cubemap_(false),
         filtered_(false),
         sRGB_(false),
         persistent_(false)
@@ -90,6 +91,8 @@ struct RenderTargetInfo
     RenderTargetSizeMode sizeMode_;
     /// Enabled flag.
     bool enabled_;
+    /// Cube map flag.
+    bool cubemap_;
     /// Filtering flag.
     bool filtered_;
     /// sRGB sampling/writing mode flag.
@@ -122,8 +125,12 @@ struct RenderPathCommand
     void RemoveShaderParameter(const String& name);
     /// Set number of output rendertargets.
     void SetNumOutputs(unsigned num);
+    /// Set output rendertarget name and face index for cube maps.
+    void SetOutput(unsigned index, const String& name, CubeMapFace face = FACE_POSITIVE_X);
     /// Set output rendertarget name.
     void SetOutputName(unsigned index, const String& name);
+    /// Set output rendertarget face index for cube maps.
+    void SetOutputFace(unsigned index, CubeMapFace face);
     /// Set depth-stencil output name. When empty, will assign a depth-stencil buffer automatically.
     void SetDepthStencilName(const String& name);
     
@@ -132,9 +139,11 @@ struct RenderPathCommand
     /// Return shader parameter.
     const Variant& GetShaderParameter(const String& name) const;
     /// Return number of output rendertargets.
-    unsigned GetNumOutputs() const { return outputNames_.Size(); }
+    unsigned GetNumOutputs() const { return outputs_.Size(); }
     /// Return output rendertarget name.
     const String& GetOutputName(unsigned index) const;
+    /// Return output rendertarget face index.
+    CubeMapFace GetOutputFace(unsigned index) const;
     /// Return depth-stencil output name.
     const String& GetDepthStencilName() const { return depthStencilName_; }
     
@@ -146,6 +155,8 @@ struct RenderPathCommand
     RenderCommandSortMode sortMode_;
     /// Scene pass name.
     String pass_;
+    /// Scene pass index. Filled by View.
+    unsigned passIndex_;
     /// Command/pass metadata.
     String metadata_;
     /// Vertex shader name.
@@ -160,8 +171,8 @@ struct RenderPathCommand
     String textureNames_[MAX_TEXTURE_UNITS];
     /// %Shader parameters.
     HashMap<StringHash, Variant> shaderParameters_;
-    /// Output rendertarget names.
-    Vector<String> outputNames_;
+    /// Output rendertarget names and faces.
+    Vector<Pair<String, CubeMapFace> > outputs_;
     /// Depth-stencil output name.
     String depthStencilName_;
     /// Clear flags.
@@ -194,7 +205,7 @@ public:
     ~RenderPath();
     
     /// Clone the rendering path.
-    RenderPath* Clone();
+    SharedPtr<RenderPath> Clone();
     /// Clear existing data and load from an XML file. Return true if successful.
     bool Load(XMLFile* file);
     /// Append data from an XML file. Return true if successful.

+ 4 - 2
Source/Atomic/Graphics/RenderSurface.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLRenderSurface.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11RenderSurface.h"
 #else
 #include "Direct3D9/D3D9RenderSurface.h"
 #endif

+ 54 - 56
Source/Atomic/Graphics/Renderer.cpp

@@ -261,7 +261,6 @@ Renderer::Renderer(Context* context) :
     shadowQuality_(SHADOWQUALITY_HIGH_16BIT),
     maxShadowMaps_(1),
     minInstances_(2),
-    maxInstanceTriangles_(500),
     maxSortedInstances_(1000),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
@@ -281,7 +280,6 @@ Renderer::Renderer(Context* context) :
     resetViews_(false)
 {
     SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
-    SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
     
     // Try to initialize right now, but skip if screen mode is not yet set
     Initialize();
@@ -439,11 +437,6 @@ void Renderer::SetMinInstances(int instances)
     minInstances_ = Max(instances, 2);
 }
 
-void Renderer::SetMaxInstanceTriangles(int triangles)
-{
-    maxInstanceTriangles_ = Max(triangles, 0);
-}
-
 void Renderer::SetMaxSortedInstances(int instances)
 {
     maxSortedInstances_ = Max(instances, 0);
@@ -662,7 +655,6 @@ void Renderer::Render()
     
     graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
     graphics_->SetTextureAnisotropy(textureAnisotropy_);
-    graphics_->ClearParameterSources();
     
     // If no views, just clear the screen
     if (views_.Empty())
@@ -673,7 +665,6 @@ void Renderer::Render()
         graphics_->SetScissorTest(false);
         graphics_->SetStencilTest(false);
         graphics_->ResetRenderTargets();
-
         graphics_->Clear(CLEAR_COLOR | CLEAR_DEPTH | CLEAR_STENCIL, defaultZone_->GetFogColor());
         
         numPrimitives_ = 0;
@@ -893,13 +884,12 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
         }
         else
         {
-            #ifdef ATOMIC_OPENGL
             #ifndef GL_ES_VERSION_2_0
-            // OpenGL (desktop): shadow compare mode needs to be specifically enabled for the shadow map
+            // OpenGL (desktop) and D3D11: shadow compare mode needs to be specifically enabled for the shadow map
             newShadowMap->SetFilterMode(FILTER_BILINEAR);
             newShadowMap->SetShadowCompare(true);
             #endif
-            #else
+            #ifndef ATOMIC_OPENGL
             // Direct3D9: when shadow compare must be done manually, use nearest filtering so that the filtering of point lights
             // and other shadowed lights matches
             newShadowMap->SetFilterMode(graphics_->GetHardwareShadowSupport() ? FILTER_BILINEAR : FILTER_NEAREST);
@@ -932,7 +922,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     return newShadowMap;
 }
 
-Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool filtered, bool srgb, unsigned persistentKey)
+Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb, unsigned persistentKey)
 {
     bool depthStencil = (format == Graphics::GetDepthStencilFormat()) || (format == Graphics::GetReadableDepthFormat());
     if (depthStencil)
@@ -941,11 +931,16 @@ Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, boo
         srgb = false;
     }
     
+    if (cubemap)
+        height = width;
+
     long long searchKey = ((long long)format << 32) | (width << 16) | height;
     if (filtered)
         searchKey |= 0x8000000000000000LL;
     if (srgb)
         searchKey |= 0x4000000000000000LL;
+    if (cubemap)
+        searchKey |= 0x2000000000000000LL;
     
     // Add persistent key if defined
     if (persistentKey)
@@ -962,32 +957,48 @@ Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, boo
     
     if (allocations >= screenBuffers_[searchKey].Size())
     {
-        SharedPtr<Texture2D> newBuffer(new Texture2D(context_));
+        SharedPtr<Texture> newBuffer;
+
+        if (!cubemap)
+        {
+            SharedPtr<Texture2D> newTex2D(new Texture2D(context_));
+            newTex2D->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET);
+
+            #ifdef ATOMIC_OPENGL
+            // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
+            // Otherwise eg. the AutoExposure post process will not work correctly
+            if (persistentKey && Texture::GetDataType(format) == GL_FLOAT)
+            {
+                // Note: this loses current rendertarget assignment
+                graphics_->ResetRenderTargets();
+                graphics_->SetRenderTarget(0, newTex2D);
+                graphics_->SetDepthStencil((RenderSurface*)0);
+                graphics_->SetViewport(IntRect(0, 0, width, height));
+                graphics_->Clear(CLEAR_COLOR);
+            }
+            #endif
+
+            newBuffer = StaticCast<Texture>(newTex2D);
+        }
+        else
+        {
+            SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
+            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET);
+
+            newBuffer = StaticCast<Texture>(newTexCube);
+        }
+        
         newBuffer->SetSRGB(srgb);
-        newBuffer->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET);
         newBuffer->SetFilterMode(filtered ? FILTER_BILINEAR : FILTER_NEAREST);
         newBuffer->ResetUseTimer();
         screenBuffers_[searchKey].Push(newBuffer);
-        #ifdef ATOMIC_OPENGL
-        // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
-        // Otherwise eg. the AutoExposure post process will not work correctly
-        if (persistentKey && Texture::GetDataType(format) == GL_FLOAT)
-        {
-            // Note: this loses current rendertarget assignment
-            graphics_->ResetRenderTargets();
-            graphics_->SetRenderTarget(0, newBuffer);
-            graphics_->SetDepthStencil((RenderSurface*)0);
-            graphics_->SetViewport(IntRect(0, 0, width, height));
-            graphics_->Clear(CLEAR_COLOR);
-        }
-        #endif
-        
+
         LOGDEBUG("Allocated new screen buffer size " + String(width) + "x" + String(height) + " format " + String(format));
         return newBuffer;
     }
     else
     {
-        Texture2D* buffer = screenBuffers_[searchKey][allocations];
+        Texture* buffer = screenBuffers_[searchKey][allocations];
         buffer->ResetUseTimer();
         return buffer;
     }
@@ -1000,7 +1011,10 @@ RenderSurface* Renderer::GetDepthStencil(int width, int height)
     if (width == graphics_->GetWidth() && height == graphics_->GetHeight() && graphics_->GetMultiSample() <= 1)
         return 0;
     else
-        return GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), false, false)->GetRenderSurface();
+    {
+        return static_cast<Texture2D*>(GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), false, false, false))->
+            GetRenderSurface();
+    }
 }
 
 OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
@@ -1053,7 +1067,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     {
         // First release all previous shaders, then load
         pass->ReleaseShaders();
-        LoadPassShaders(tech, pass->GetType());
+        LoadPassShaders(pass);
     }
     
     // Make sure shaders are loaded now
@@ -1061,10 +1075,8 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     {
         bool heightFog = batch.zone_ && batch.zone_->GetHeightFog();
         
-        // If instancing is not supported, but was requested, or the object is too large to be instanced,
-        // choose static geometry vertex shader instead
-        if (batch.geometryType_ == GEOM_INSTANCED && (!GetDynamicInstancing() || batch.geometry_->GetIndexCount() >
-            (unsigned)maxInstanceTriangles_ * 3))
+        // If instancing is not supported, but was requested, choose static geometry vertex shader instead
+        if (batch.geometryType_ == GEOM_INSTANCED && !GetDynamicInstancing())
             batch.geometryType_ = GEOM_STATIC;
         
         if (batch.geometryType_ == GEOM_STATIC_NOINSTANCING)
@@ -1373,13 +1385,13 @@ void Renderer::RemoveUnusedBuffers()
         }
     }
     
-    for (HashMap<long long, Vector<SharedPtr<Texture2D> > >::Iterator i = screenBuffers_.Begin(); i != screenBuffers_.End();)
+    for (HashMap<long long, Vector<SharedPtr<Texture> > >::Iterator i = screenBuffers_.Begin(); i != screenBuffers_.End();)
     {
-        HashMap<long long, Vector<SharedPtr<Texture2D> > >::Iterator current = i++;
-        Vector<SharedPtr<Texture2D> >& buffers = current->second_;
+        HashMap<long long, Vector<SharedPtr<Texture> > >::Iterator current = i++;
+        Vector<SharedPtr<Texture> >& buffers = current->second_;
         for (unsigned j = buffers.Size() - 1; j < buffers.Size(); --j)
         {
-            Texture2D* buffer = buffers[j];
+            Texture* buffer = buffers[j];
             if (buffer->GetUseTimer() > MAX_BUFFER_AGE)
             {
                 LOGDEBUG("Removed unused screen buffer size " + String(buffer->GetWidth()) + "x" + String(buffer->GetHeight()) + " format " + String(buffer->GetFormat()));
@@ -1468,12 +1480,8 @@ void Renderer::LoadShaders()
     shadersDirty_ = false;
 }
 
-void Renderer::LoadPassShaders(Technique* tech, StringHash type)
+void Renderer::LoadPassShaders(Pass* pass)
 {
-    Pass* pass = tech->GetPass(type);
-    if (!pass)
-        return;
-    
     PROFILE(LoadPassShaders);
     
     unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
@@ -1697,11 +1705,8 @@ void Renderer::CreateInstancingBuffer()
         return;
     }
     
-    // If must lock the buffer for each batch group, set a smaller size
-    unsigned defaultSize = graphics_->GetStreamOffsetSupport() ? INSTANCING_BUFFER_DEFAULT_SIZE : INSTANCING_BUFFER_DEFAULT_SIZE / 4;
-    
     instancingBuffer_ = new VertexBuffer(context_);
-    if (!instancingBuffer_->SetSize(defaultSize, INSTANCING_BUFFER_MASK, true))
+    if (!instancingBuffer_->SetSize(INSTANCING_BUFFER_DEFAULT_SIZE, INSTANCING_BUFFER_MASK, true))
     {
         instancingBuffer_.Reset();
         dynamicInstancing_ = false;
@@ -1730,13 +1735,6 @@ void Renderer::HandleScreenMode(StringHash eventType, VariantMap& eventData)
         resetViews_ = true;
 }
 
-void Renderer::HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData)
-{
-    // Reinitialize if already initialized
-    if (initialized_)
-        Initialize();
-}
-
 void Renderer::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
     using namespace RenderUpdate;

+ 8 - 15
Source/Atomic/Graphics/Renderer.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -45,6 +45,7 @@ class RenderSurface;
 class ResourceCache;
 class Skeleton;
 class OcclusionBuffer;
+class Texture;
 class Texture2D;
 class TextureCube;
 class View;
@@ -173,15 +174,15 @@ public:
     void SetTextureAnisotropy(int level);
     /// Set texture filtering.
     void SetTextureFilterMode(TextureFilterMode mode);
-    /// Set texture quality level.
+    /// Set texture quality level. See the QUALITY constants in GraphicsDefs.h.
     void SetTextureQuality(int quality);
-    /// Set material quality level.
+    /// Set material quality level. See the QUALITY constants in GraphicsDefs.h.
     void SetMaterialQuality(int quality);
     /// Set shadows on/off.
     void SetDrawShadows(bool enable);
     /// Set shadow map resolution.
     void SetShadowMapSize(int size);
-    /// Set shadow quality (amount of samples and bit depth.)
+    /// Set shadow quality mode. See the SHADOWQUALITY constants in GraphicsDefs.h.
     void SetShadowQuality(int quality);
     /// Set reuse of shadow maps. Default is true. If disabled, also transparent geometry can be shadowed.
     void SetReuseShadowMaps(bool enable);
@@ -191,8 +192,6 @@ public:
     void SetDynamicInstancing(bool enable);
     /// Set minimum number of instances required in a batch group to render as instanced.
     void SetMinInstances(int instances);
-    /// Set maximum number of triangles per object for instancing.
-    void SetMaxInstanceTriangles(int triangles);
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
     void SetMaxSortedInstances(int instances);
     /// Set maximum number of occluder trianges.
@@ -240,8 +239,6 @@ public:
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     /// Return minimum number of instances required in a batch group to render as instanced.
     int GetMinInstances() const { return minInstances_; }
-    /// Return maximum number of triangles per object for instancing.
-    int GetMaxInstanceTriangles() const { return maxInstanceTriangles_; }
     /// Return maximum number of sorted instances per batch group.
     int GetMaxSortedInstances() const { return maxSortedInstances_; }
     /// Return maximum number of occluder triangles.
@@ -303,7 +300,7 @@ public:
     /// Allocate a shadow map. If shadow map reuse is disabled, a different map is returned each time.
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
     /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
-    Texture2D* GetScreenBuffer(int width, int height, unsigned format, bool filtered, bool srgb, unsigned persistentKey = 0);
+    Texture* GetScreenBuffer(int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb, unsigned persistentKey = 0);
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
     RenderSurface* GetDepthStencil(int width, int height);
     /// Allocate an occlusion buffer.
@@ -335,7 +332,7 @@ private:
     /// Reload shaders.
     void LoadShaders();
     /// Reload shaders for a material pass.
-    void LoadPassShaders(Technique* tech, StringHash passType);
+    void LoadPassShaders(Pass* pass);
     /// Release shaders used in materials.
     void ReleaseMaterialShaders();
     /// Reload textures.
@@ -360,8 +357,6 @@ private:
     void ResetBuffers();
     /// Handle screen mode event.
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
-    /// Handle graphics features (re)check event. Event only sent by D3D9Graphics class.
-    void HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData);
     /// Handle render update event.
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     
@@ -400,7 +395,7 @@ private:
     /// Shadow map allocations by resolution.
     HashMap<int, PODVector<Light*> > shadowMapAllocations_;
     /// Screen buffers by resolution and format.
-    HashMap<long long, Vector<SharedPtr<Texture2D> > > screenBuffers_;
+    HashMap<long long, Vector<SharedPtr<Texture> > > screenBuffers_;
     /// Current screen buffer allocations by resolution and format.
     HashMap<long long, unsigned> screenBufferAllocations_;
     /// Saved status of screen buffer allocations for restoring.
@@ -439,8 +434,6 @@ private:
     int maxShadowMaps_;
     /// Minimum number of instances required in a batch group to render as instanced.
     int minInstances_;
-    /// Maximum triangles per object for instancing.
-    int maxInstanceTriangles_;
     /// Maximum sorted instances per batch group.
     int maxSortedInstances_;
     /// Maximum occluder triangles.

+ 6 - 2
Source/Atomic/Graphics/ShaderProgram.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLShaderProgram.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11ShaderProgram.h"
+#else
+#include "Direct3D9/D3D9ShaderProgram.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/ShaderVariation.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLShaderVariation.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11ShaderVariation.h"
 #else
 #include "Direct3D9/D3D9ShaderVariation.h"
 #endif

+ 120 - 57
Source/Atomic/Graphics/Technique.cpp

@@ -70,21 +70,23 @@ static const char* lightingModeNames[] =
     0
 };
 
-Pass::Pass(StringHash type) :
-    type_(type),
+Pass::Pass(const String& name) :
     blendMode_(BLEND_REPLACE),
     depthTestMode_(CMP_LESSEQUAL),
     lightingMode_(LIGHTING_UNLIT),
     shadersLoadedFrameNumber_(0),
     depthWrite_(true),
     alphaMask_(false),
-    isSM3_(false),
     isDesktop_(false)
 {
+    name_ = name.ToLower();
+    index_ = Technique::GetPassIndex(name_);
+
     // Guess default lighting mode from pass name
-    if (type == PASS_BASE || type == PASS_ALPHA || type == PASS_MATERIAL || type == PASS_DEFERRED)
+    if (index_ == Technique::basePassIndex || index_ == Technique::alphaPassIndex || index_ == Technique::materialPassIndex ||
+        index_ == Technique::deferredPassIndex)
         lightingMode_ = LIGHTING_PERVERTEX;
-    else if (type == PASS_LIGHT || type == PASS_LITBASE || type == PASS_LITALPHA)
+    else if (index_ == Technique::lightPassIndex || index_ == Technique::litBasePassIndex || index_ == Technique::litAlphaPassIndex)
         lightingMode_ = LIGHTING_PERPIXEL;
 }
 
@@ -117,11 +119,6 @@ void Pass::SetAlphaMask(bool enable)
     alphaMask_ = enable;
 }
 
-void Pass::SetIsSM3(bool enable)
-{
-    isSM3_ = enable;
-}
-
 void Pass::SetIsDesktop(bool enable)
 {
     isDesktop_ = enable;
@@ -162,15 +159,20 @@ void Pass::MarkShadersLoaded(unsigned frameNumber)
     shadersLoadedFrameNumber_ = frameNumber;
 }
 
+unsigned Technique::basePassIndex = 0;
+unsigned Technique::alphaPassIndex = 0;
+unsigned Technique::materialPassIndex = 0;
+unsigned Technique::deferredPassIndex = 0;
+unsigned Technique::lightPassIndex = 0;
+unsigned Technique::litBasePassIndex = 0;
+unsigned Technique::litAlphaPassIndex = 0;
+unsigned Technique::shadowPassIndex = 0;
+HashMap<String, unsigned> Technique::passIndices;
+
 Technique::Technique(Context* context) :
     Resource(context),
-    isSM3_(false),
-    isDesktop_(false),
-    numPasses_(0)
+    isDesktop_(false)
 {
-    Graphics* graphics = GetSubsystem<Graphics>();
-    sm3Support_ = graphics ? graphics->GetSM3Support() : true;
-    
     #ifdef DESKTOP_GRAPHICS
     desktopSupport_ = true;
     #else
@@ -191,7 +193,6 @@ bool Technique::BeginLoad(Deserializer& source)
 {
     passes_.Clear();
 
-    numPasses_ = 0;
     SetMemoryUse(sizeof(Technique));
     
     SharedPtr<XMLFile> xml(new XMLFile(context_));
@@ -199,8 +200,6 @@ bool Technique::BeginLoad(Deserializer& source)
         return false;
     
     XMLElement rootElem = xml->GetRoot();
-    if (rootElem.HasAttribute("sm3"))
-        isSM3_ = rootElem.GetBool("sm3");
     if (rootElem.HasAttribute("desktop"))
         isDesktop_ = rootElem.GetBool("desktop");
     
@@ -222,12 +221,8 @@ bool Technique::BeginLoad(Deserializer& source)
     {
         if (passElem.HasAttribute("name"))
         {
-            StringHash nameHash(passElem.GetAttribute("name"));
-            
-            Pass* newPass = CreatePass(nameHash);
-            
-            if (passElem.HasAttribute("sm3"))
-                newPass->SetIsSM3(passElem.GetBool("sm3"));
+            Pass* newPass = CreatePass(passElem.GetAttribute("name"));
+
             if (passElem.HasAttribute("desktop"))
                 newPass->SetIsDesktop(passElem.GetBool("desktop"));
             
@@ -292,11 +287,6 @@ bool Technique::BeginLoad(Deserializer& source)
     return true;
 }
 
-void Technique::SetIsSM3(bool enable)
-{
-    isSM3_ = enable;
-}
-
 void Technique::SetIsDesktop(bool enable)
 {
     isDesktop_ = enable;
@@ -304,55 +294,128 @@ void Technique::SetIsDesktop(bool enable)
 
 void Technique::ReleaseShaders()
 {
-    PODVector<SharedPtr<Pass>*> allPasses = passes_.Values();
-    
-    for (unsigned i = 0; i < allPasses.Size(); ++i)
-        allPasses[i]->Get()->ReleaseShaders();
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            pass->ReleaseShaders();
+    }
 }
 
-Pass* Technique::CreatePass(StringHash type)
+Pass* Technique::CreatePass(const String& name)
 {
-    Pass* oldPass = GetPass(type);
+    Pass* oldPass = GetPass(name);
     if (oldPass)
         return oldPass;
     
-    SharedPtr<Pass> newPass(new Pass(type));
-    passes_.Insert(type.Value(), newPass);
+    SharedPtr<Pass> newPass(new Pass(name));
+    unsigned passIndex = newPass->GetIndex();
+    if (passIndex >= passes_.Size())
+        passes_.Resize(passIndex + 1);
+    passes_[passIndex] = newPass;
     
     // Calculate memory use now
-    SetMemoryUse(sizeof(Technique) + ++numPasses_ * sizeof(Pass));
+    SetMemoryUse(sizeof(Technique) + GetNumPasses() * sizeof(Pass));
 
     return newPass;
 }
 
-void Technique::RemovePass(StringHash type)
+void Technique::RemovePass(const String& name)
+{
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    if (i == passIndices.End())
+        return;
+    else if (i->second_ < passes_.Size() && passes_[i->second_].Get())
+    {
+        passes_[i->second_].Reset();
+        SetMemoryUse(sizeof(Technique) + GetNumPasses() * sizeof(Pass));
+    }
+}
+
+bool Technique::HasPass(const String& name) const
+{
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? HasPass(i->second_) : false;
+}
+
+Pass* Technique::GetPass(const String& name) const
 {
-    if (passes_.Erase(type.Value()))
-        SetMemoryUse(sizeof(Technique) + --numPasses_ * sizeof(Pass));
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? GetPass(i->second_) : 0;
 }
 
-Vector<StringHash> Technique::GetPassTypes() const
+Pass* Technique::GetSupportedPass(const String& name) const
 {
-    // Convert PODVector<unsigned> to Vector<StringHash>
-    PODVector<unsigned> vectorIn = passes_.Keys();
-    Vector<StringHash> vectorOut;
-    vectorOut.Reserve(vectorIn.Size());
-    for (unsigned i = 0; i < vectorIn.Size(); ++i)
-        vectorOut.Push(StringHash(vectorIn[i]));
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? GetSupportedPass(i->second_) : 0;
+}
+
+unsigned Technique::GetNumPasses() const
+{
+    unsigned ret = 0;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        if (i->Get())
+            ++ret;
+    }
+
+    return ret;
+}
+
+Vector<String> Technique::GetPassNames() const
+{
+    Vector<String> ret;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            ret.Push(pass->GetName());
+    }
 
-    return vectorOut;
+    return ret;
 }
 
 PODVector<Pass*> Technique::GetPasses() const
 {
-    // Convert PODVector<SharedPtr<Pass>*> to PODVector<Pass*>
-    PODVector<SharedPtr<Pass>*> vectorIn = passes_.Values();
-    PODVector<Pass*> vectorOut;
-    vectorOut.Reserve(vectorIn.Size());
-    for (unsigned i = 0; i < vectorIn.Size(); ++i)
-        vectorOut.Push(vectorIn[i]->Get());
+    PODVector<Pass*> ret;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            ret.Push(pass);
+    }
+
+    return ret;
+}
+
+unsigned Technique::GetPassIndex(const String& passName)
+{
+    // Initialize built-in pass indices on first call
+    if (passIndices.Empty())
+    {
+        basePassIndex = passIndices["base"] = 0;
+        alphaPassIndex = passIndices["alpha"] = 1;
+        materialPassIndex = passIndices["material"] = 2;
+        deferredPassIndex = passIndices["deferred"] = 3;
+        lightPassIndex = passIndices["light"] = 4;
+        litBasePassIndex = passIndices["litbase"] = 5;
+        litAlphaPassIndex = passIndices["litalpha"] = 6;
+        shadowPassIndex = passIndices["shadow"] = 7;
+    }
 
-    return vectorOut;
+    String nameLower = passName.ToLower();
+    HashMap<String, unsigned>::Iterator i = passIndices.Find(nameLower);
+    if (i != passIndices.End())
+        return i->second_;
+    else
+    {
+        unsigned newPassIndex = passIndices.Size();
+        passIndices[nameLower] = newPassIndex;
+        return newPassIndex;
+    }
 }
 
 }

+ 53 - 40
Source/Atomic/Graphics/Technique.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +23,6 @@
 #pragma once
 
 #include "../Graphics/GraphicsDefs.h"
-#include "../Container/HashTable.h"
 #include "../Resource/Resource.h"
 
 namespace Atomic
@@ -44,7 +43,7 @@ class ATOMIC_API Pass : public RefCounted
 {
 public:
     /// Construct.
-    Pass(StringHash type);
+    Pass(const String& passName);
     /// Destruct.
     ~Pass();
     
@@ -58,8 +57,6 @@ public:
     void SetDepthWrite(bool enable);
     /// Set alpha masking hint. Completely opaque draw calls will be performed before alpha masked.
     void SetAlphaMask(bool enable);
-    /// Set whether requires %Shader %Model 3.
-    void SetIsSM3(bool enable);
     /// Set whether requires desktop level hardware.
     void SetIsDesktop(bool enable);
     /// Set vertex shader name.
@@ -75,8 +72,10 @@ public:
     /// Mark shaders loaded this frame.
     void MarkShadersLoaded(unsigned frameNumber);
     
-    /// Return pass type.
-    const StringHash& GetType() const { return type_; }
+    /// Return pass name.
+    const String& GetName() const { return name_; }
+    /// Return pass index. This is used for optimal render-time pass queries that avoid map lookups.
+    unsigned GetIndex() const { return index_; }
     /// Return blend mode.
     BlendMode GetBlendMode() const { return blendMode_; }
     /// Return depth compare mode.
@@ -89,8 +88,6 @@ public:
     bool GetDepthWrite() const { return depthWrite_; }
     /// Return alpha masking hint.
     bool GetAlphaMask() const { return alphaMask_; }
-    /// Return whether requires %Shader %Model 3.
-    bool IsSM3() const { return isSM3_; }
     /// Return whether requires desktop level hardware.
     bool IsDesktop() const { return isDesktop_; }
     /// Return vertex shader name.
@@ -107,8 +104,8 @@ public:
     Vector<SharedPtr<ShaderVariation> >& GetPixelShaders() { return pixelShaders_; }
     
 private:
-    /// Pass type.
-    StringHash type_;
+    /// Pass index.
+    unsigned index_;
     /// Blend mode.
     BlendMode blendMode_;
     /// Depth compare mode.
@@ -137,6 +134,8 @@ private:
     Vector<SharedPtr<ShaderVariation> > vertexShaders_;
     /// Pixel shaders.
     Vector<SharedPtr<ShaderVariation> > pixelShaders_;
+    /// Pass name.
+    String name_;
 };
 
 /// %Material technique. Consists of several passes.
@@ -157,61 +156,75 @@ public:
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);
     
-    /// Set whether requires %Shader %Model 3.
-    void SetIsSM3(bool enable);
     /// Set whether requires desktop level hardware.
     void SetIsDesktop(bool enable);
     /// Create a new pass.
-    Pass* CreatePass(StringHash type);
+    Pass* CreatePass(const String& passName);
     /// Remove a pass.
-    void RemovePass(StringHash type);
+    void RemovePass(const String& passName);
     /// Reset shader pointers in all passes.
     void ReleaseShaders();
     
-    /// Return whether requires %Shader %Model 3.
-    bool IsSM3() const { return isSM3_; }
     /// Return whether requires desktop level hardware.
     bool IsDesktop() const { return isDesktop_; }
     /// Return whether technique is supported by the current hardware.
-    bool IsSupported() const { return (!isSM3_ || sm3Support_) && (!isDesktop_ || desktopSupport_); }
+    bool IsSupported() const { return !isDesktop_ || desktopSupport_; }
     /// Return whether has a pass.
-    bool HasPass(StringHash type) const { return  passes_.Find(type.Value()) != 0; }
-    
+    bool HasPass(unsigned passIndex) const { return passIndex < passes_.Size() && passes_[passIndex].Get() != 0; }
+    /// Return whether has a pass by name. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    bool HasPass(const String& passName) const;
     /// Return a pass, or null if not found.
-    Pass* GetPass(StringHash type) const
-    {
-        SharedPtr<Pass>* passPtr = passes_.Find(type.Value());
-        return passPtr ? passPtr->Get() : 0;
-    }
+    Pass* GetPass(unsigned passIndex) const { return passIndex < passes_.Size() ? passes_[passIndex].Get() : 0; }
+    /// Return a pass by name, or null if not found. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    Pass* GetPass(const String& passName) const;
     
     /// Return a pass that is supported for rendering, or null if not found.
-    Pass* GetSupportedPass(StringHash type) const
+    Pass* GetSupportedPass(unsigned passIndex) const
     {
-        SharedPtr<Pass>* passPtr = passes_.Find(type.Value());
-        Pass* pass = passPtr ? passPtr->Get() : 0;
-        return pass && (!pass->IsSM3() || sm3Support_) && (!pass->IsDesktop() || desktopSupport_) ? pass : 0;
+        Pass* pass = passIndex < passes_.Size() ? passes_[passIndex].Get() : 0;
+        return pass && (!pass->IsDesktop() || desktopSupport_) ? pass : 0;
     }
+
+    /// Return a supported pass by name. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    Pass* GetSupportedPass(const String& passName) const;
     
     /// Return number of passes.
-    unsigned GetNumPasses() const { return numPasses_; }
-    /// Return all the pass types in the hash table. The returned collection is not guaranteed to be in the same order as the hash table insertion order.
-    Vector<StringHash> GetPassTypes() const;
-    /// Return all the passes in the hash table. The returned collection is not guaranteed to be in the same order as the hash table insertion order.
+    unsigned GetNumPasses() const;
+    /// Return all pass names.
+    Vector<String> GetPassNames() const;
+    /// Return all passes.
     PODVector<Pass*> GetPasses() const;
 
+    /// Return a pass type index by name. Allocate new if not used yet.
+    static unsigned GetPassIndex(const String& passName);
+
+    /// Index for base pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned basePassIndex;
+    /// Index for alpha pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned alphaPassIndex;
+    /// Index for prepass material pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned materialPassIndex;
+    /// Index for deferred G-buffer pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned deferredPassIndex;
+    /// Index for per-pixel light pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned lightPassIndex;
+    /// Index for lit base pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned litBasePassIndex;
+    /// Index for lit alpha pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned litAlphaPassIndex;
+    /// Index for shadow pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned shadowPassIndex;
+
 private:
-    /// Require %Shader %Model 3 flag.
-    bool isSM3_;
-    /// Cached %Shader %Model 3 support flag.
-    bool sm3Support_;
     /// Require desktop GPU flag.
     bool isDesktop_;
     /// Cached desktop GPU support flag.
     bool desktopSupport_;
     /// Passes.
-    HashTable<SharedPtr<Pass>, 16> passes_;
-    /// Number of passes.
-    unsigned numPasses_;
+    Vector<SharedPtr<Pass> > passes_;
+
+    /// Pass index assignments.
+    static HashMap<String, unsigned> passIndices;
 };
 
 }

+ 4 - 2
Source/Atomic/Graphics/Texture.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLTexture.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11Texture.h"
 #else
 #include "Direct3D9/D3D9Texture.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/Texture2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLTexture2D.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11Texture2D.h"
 #else
 #include "Direct3D9/D3D9Texture2D.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/Texture3D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLTexture3D.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11Texture3D.h"
 #else
 #include "Direct3D9/D3D9Texture3D.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/TextureCube.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLTextureCube.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11TextureCube.h"
 #else
 #include "Direct3D9/D3D9TextureCube.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/VertexBuffer.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef ATOMIC_OPENGL
+#if defined(ATOMIC_OPENGL)
 #include "OpenGL/OGLVertexBuffer.h"
+#elif defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11VertexBuffer.h"
 #else
 #include "Direct3D9/D3D9VertexBuffer.h"
 #endif

+ 4 - 2
Source/Atomic/Graphics/VertexDeclaration.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
 
 #pragma once
 
-#ifndef ATOMIC_OPENGL
+#if defined(ATOMIC_D3D11)
+#include "Direct3D11/D3D11VertexDeclaration.h"
+#else
 #include "Direct3D9/D3D9VertexDeclaration.h"
 #endif

File diff suppressed because it is too large
+ 252 - 302
Source/Atomic/Graphics/View.cpp


+ 35 - 23
Source/Atomic/Graphics/View.h

@@ -44,6 +44,7 @@ class Renderer;
 class RenderPath;
 class RenderSurface;
 class Technique;
+class Texture;
 class Texture2D;
 class Viewport;
 class Zone;
@@ -65,7 +66,7 @@ struct LightQueryResult
     unsigned shadowCasterBegin_[MAX_LIGHT_SPLITS];
     /// Shadow caster end indices.
     unsigned shadowCasterEnd_[MAX_LIGHT_SPLITS];
-    /// Combined bounding box of shadow casters in light view or projection space.
+    /// Combined bounding box of shadow casters in light projection space. Only used for focused spot lights.
     BoundingBox shadowCasterBox_[MAX_LIGHT_SPLITS];
     /// Shadow camera near splits (directional lights only.)
     float shadowNearSplits_[MAX_LIGHT_SPLITS];
@@ -78,8 +79,8 @@ struct LightQueryResult
 /// Scene render pass info.
 struct ScenePassInfo
 {
-    /// Pass name hash.
-    StringHash pass_;
+    /// Pass index.
+    unsigned passIndex_;
     /// Allow instancing flag.
     bool allowInstancing_;
     /// Mark to stencil flag.
@@ -164,6 +165,12 @@ private:
     void GetDrawables();
     /// Construct batches from the drawable objects.
     void GetBatches();
+    /// Get lit geometries and shadowcasters for visible lights.
+    void ProcessLights();
+    /// Get batches from lit geometries and shadowcasters.
+    void GetLightBatches();
+    /// Get unlit batches.
+    void GetBaseBatches();
     /// Update geometries and sort batches.
     void UpdateGeometries();
     /// Get pixel lit batches for a certain light and drawable.
@@ -187,7 +194,7 @@ private:
     /// Allocate needed screen buffers.
     void AllocateScreenBuffers();
     /// Blit the viewport from one surface to another.
-    void BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool depthWrite);
+    void BlitFramebuffer(Texture* source, RenderSurface* destination, bool depthWrite);
     /// Draw a fullscreen quad. Shaders and renderstates must have been set beforehand.
     void DrawFullscreenQuad(bool nearQuad);
     /// Query for occluders as seen from a camera.
@@ -226,7 +233,11 @@ private:
     void RenderShadowMap(const LightBatchQueue& queue);
     /// Return the proper depth-stencil surface to use for a rendertarget.
     RenderSurface* GetDepthStencil(RenderSurface* renderTarget);
-    
+    /// Helper function to get the render surface from a texture. 2D textures will always return the first face only.
+    RenderSurface* GetRenderSurfaceFromTexture(Texture* texture, CubeMapFace face = FACE_POSITIVE_X);
+    /// Get a named texture from the rendertarget list or from the resource cache, to be either used as a rendertarget or texture binding.
+    Texture* FindNamedTexture(const String& name, bool isRenderTarget, bool isVolumeMap = false);
+
     /// Return the drawable's zone, or camera zone if it has override mode enabled.
     Zone* GetZone(Drawable* drawable)
     {
@@ -280,13 +291,13 @@ private:
     /// Substitute rendertarget for deferred rendering. Allocated if necessary.
     RenderSurface* substituteRenderTarget_;
     /// Texture(s) for sampling the viewport contents. Allocated if necessary.
-    Texture2D* viewportTextures_[MAX_VIEWPORT_TEXTURES];
+    Texture* viewportTextures_[MAX_VIEWPORT_TEXTURES];
     /// Color rendertarget active for the current renderpath command.
     RenderSurface* currentRenderTarget_;
     /// Texture containing the latest viewport texture.
-    Texture2D* currentViewportTexture_;
+    Texture* currentViewportTexture_;
     /// Dummy texture for D3D9 depth only rendering.
-    Texture2D* depthOnlyDummyTexture_;
+    Texture* depthOnlyDummyTexture_;
     /// Viewport rectangle.
     IntRect viewRect_;
     /// Viewport size.
@@ -341,10 +352,11 @@ private:
     PODVector<Drawable*> occluders_;
     /// Lights.
     PODVector<Light*> lights_;
+    
     /// Drawables that limit their maximum light count.
     HashSet<Drawable*> maxLightsDrawables_;
     /// Rendertargets defined by the renderpath.
-    HashMap<StringHash, Texture2D*> renderTargets_;
+    HashMap<StringHash, Texture*> renderTargets_;
     /// Intermediate light processing results.
     Vector<LightQueryResult> lightQueryResults_;
     /// Info for scene render passes defined by the renderpath.
@@ -353,20 +365,20 @@ private:
     Vector<LightBatchQueue> lightQueues_;
     /// Per-vertex light queues.
     HashMap<unsigned long long, LightBatchQueue> vertexLightQueues_;
-    /// Batch queues.
-    HashMap<StringHash, BatchQueue> batchQueues_;
-    /// Hash of the GBuffer pass, or null if none.
-    StringHash gBufferPassName_;
-    /// Hash of the opaque forward base pass.
-    StringHash basePassName_;
-    /// Hash of the alpha pass.
-    StringHash alphaPassName_;
-    /// Hash of the forward light pass.
-    StringHash lightPassName_;
-    /// Hash of the litbase pass.
-    StringHash litBasePassName_;
-    /// Hash of the litalpha pass.
-    StringHash litAlphaPassName_;
+    /// Batch queues by pass index.
+    HashMap<unsigned, BatchQueue> batchQueues_;
+    /// Index of the GBuffer pass.
+    unsigned gBufferPassIndex_;
+    /// Index of the opaque forward base pass.
+    unsigned basePassIndex_;
+    /// Index of the alpha pass.
+    unsigned alphaPassIndex_;
+    /// Index of the forward light pass.
+    unsigned lightPassIndex_;
+    /// Index of the litbase pass.
+    unsigned litBasePassIndex_;
+    /// Index of the litalpha pass.
+    unsigned litAlphaPassIndex_;
     /// Pointer to the light volume command if any.
     const RenderPathCommand* lightVolumeCommand_;
 };

+ 1 - 2
Source/Atomic/Input/Input.cpp

@@ -20,6 +20,7 @@
 // THE SOFTWARE.
 //
 
+#include "Precompiled.h"
 #include "../Core/Context.h"
 #include "../Core/CoreEvents.h"
 #include "../IO/FileSystem.h"
@@ -1988,8 +1989,6 @@ void Input::HandleScreenMode(StringHash eventType, VariantMap& eventData)
     else
         lastMousePosition_ = GetMousePosition();
 
-    }
-
     if (graphics_->GetFullscreen())
         focusedThisFrame_ = true;
 

+ 5 - 5
Source/Atomic/Navigation/CrowdAgent.cpp

@@ -34,12 +34,12 @@
 #include "../Scene/Serializable.h"
 #include "../Core/Variant.h"
 
-#include <Detour/DetourCommon.h>
-#include <DetourCrowd/DetourCrowd.h>
+#include <Detour/include/DetourCommon.h>
+#include <DetourCrowd/include/DetourCrowd.h>
 
 #include "../DebugNew.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 extern const char* NAVIGATION_CATEGORY;
@@ -352,7 +352,7 @@ Vector3 CrowdAgent::GetActualVelocity() const
     return Vector3::ZERO;
 }
 
-Urho3D::CrowdAgentState CrowdAgent::GetAgentState() const
+Atomic::CrowdAgentState CrowdAgent::GetAgentState() const
 {
     if (crowdManager_ && inCrowd_)
     {
@@ -364,7 +364,7 @@ Urho3D::CrowdAgentState CrowdAgent::GetAgentState() const
     return CROWD_AGENT_INVALID;
 }
 
-Urho3D::CrowdTargetState CrowdAgent::GetTargetState() const
+Atomic::CrowdTargetState CrowdAgent::GetTargetState() const
 {
     if (crowdManager_ && inCrowd_)
     {

+ 2 - 2
Source/Atomic/Navigation/CrowdAgent.h

@@ -25,7 +25,7 @@
 #include "../Scene/Component.h"
 #include "../Navigation/DetourCrowdManager.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 enum CrowdTargetState
@@ -48,7 +48,7 @@ enum CrowdAgentState
 };
 
 /// DetourCrowd Agent, requires a DetourCrowdManager in the scene. Agent's radius and height is set through the navigation mesh.
-class URHO3D_API CrowdAgent : public Component
+class ATOMIC_API CrowdAgent : public Component
 {
     OBJECT(CrowdAgent);
     friend class DetourCrowdManager;

+ 3 - 3
Source/Atomic/Navigation/DetourCrowdManager.cpp

@@ -39,12 +39,12 @@
 #include "../Physics/PhysicsEvents.h"
 #endif
 
-#include <DetourCrowd/DetourCrowd.h>
-#include <Recast/Recast.h>
+#include <DetourCrowd/include/DetourCrowd.h>
+#include <Recast/include/Recast.h>
 
 #include "../DebugNew.h"
 
-namespace Urho3D
+namespace Atomic
 {
     
 extern const char* NAVIGATION_CATEGORY;

+ 2 - 2
Source/Atomic/Navigation/DetourCrowdManager.h

@@ -28,7 +28,7 @@ class dtCrowd;
 struct dtCrowdAgent;
 struct dtCrowdAgentDebugInfo;
 
-namespace Urho3D
+namespace Atomic
 {
 
 class CrowdAgent;
@@ -50,7 +50,7 @@ enum NavigationPushiness
 
 
 /// Detour Crowd Simulation Scene Component. Should be added only to the root scene node. Agent's radius and height is set through the navigation mesh. \todo support multiple agent's radii and heights.
-class URHO3D_API DetourCrowdManager : public Component
+class ATOMIC_API DetourCrowdManager : public Component
 {
     OBJECT(DetourCrowdManager);
     friend class CrowdAgent;

+ 8 - 8
Source/Atomic/Navigation/DynamicNavigationMesh.cpp

@@ -38,20 +38,20 @@
 
 #include <LZ4/lz4.h>
 #include <cfloat>
-#include <Detour/DetourNavMesh.h>
-#include <Detour/DetourNavMeshBuilder.h>
-#include <Detour/DetourNavMeshQuery.h>
-#include <DetourTileCache/DetourTileCache.h>
-#include <DetourTileCache/DetourTileCacheBuilder.h>
-#include <Recast/Recast.h>
-#include <Recast/RecastAlloc.h>
+#include <Detour/include/DetourNavMesh.h>
+#include <Detour/include/DetourNavMeshBuilder.h>
+#include <Detour/include/DetourNavMeshQuery.h>
+#include <DetourTileCache/include/DetourTileCache.h>
+#include <DetourTileCache/include/DetourTileCacheBuilder.h>
+#include <Recast/include/Recast.h>
+#include <Recast/include/RecastAlloc.h>
 
 //DebugNew is deliberately not used because the macro 'free' conflicts DetourTileCache's LinearAllocator interface
 //#include "../DebugNew.h"
 
 #define TILECACHE_MAXLAYERS 128
 
-namespace Urho3D
+namespace Atomic
 {
     
 extern const char* NAVIGATION_CATEGORY;

+ 2 - 2
Source/Atomic/Navigation/DynamicNavigationMesh.h

@@ -32,13 +32,13 @@ struct dtTileCacheLayer;
 struct dtTileCacheContourSet;
 struct dtTileCachePolyMesh;
 
-namespace Urho3D
+namespace Atomic
 {
 
 class OffMeshConnection;
 class Obstacle;
 
-class URHO3D_API DynamicNavigationMesh : public NavigationMesh
+class ATOMIC_API DynamicNavigationMesh : public NavigationMesh
 {
     OBJECT(DynamicNavigationMesh)
     friend class Obstacle;

+ 1 - 1
Source/Atomic/Navigation/NavArea.cpp

@@ -28,7 +28,7 @@
 #include "../Scene/Node.h"
 #include "../Container/Str.h"
 
-namespace Urho3D
+namespace Atomic
 {
     static const unsigned MAX_NAV_AREA_ID = 255;
     static const Vector3 DEFAULT_BOUNDING_BOX_MIN(-10.0f, -10.0f, -10.0f);

+ 2 - 2
Source/Atomic/Navigation/NavArea.h

@@ -25,9 +25,9 @@
 #include "../Scene/Component.h"
 #include "../Math/BoundingBox.h"
 
-namespace Urho3D
+namespace Atomic
 {
-    class URHO3D_API NavArea : public Component
+    class ATOMIC_API NavArea : public Component
     {
         OBJECT(NavArea);
 

+ 7 - 7
Source/Atomic/Navigation/NavBuildData.cpp

@@ -22,14 +22,14 @@
 
 #include "../Navigation/NavBuildData.h"
 
-#include <Recast/Recast.h>
-#include <Detour/DetourNavMesh.h>
-#include <Detour/DetourNavMeshBuilder.h>
-#include <Detour/DetourNavMeshQuery.h>
-#include <DetourTileCache/DetourTileCache.h>
-#include <DetourTileCache/DetourTileCacheBuilder.h>
+#include <Recast/include/Recast.h>
+#include <Detour/include/DetourNavMesh.h>
+#include <Detour/include/DetourNavMeshBuilder.h>
+#include <Detour/include/DetourNavMeshQuery.h>
+#include <DetourTileCache/include/DetourTileCache.h>
+#include <DetourTileCache/include/DetourTileCacheBuilder.h>
 
-namespace Urho3D
+namespace Atomic
 {
 
 NavBuildData::NavBuildData() :

+ 3 - 3
Source/Atomic/Navigation/NavBuildData.h

@@ -38,11 +38,11 @@ struct dtTileCacheContourSet;
 struct dtTileCachePolyMesh;
 struct dtTileCacheAlloc;
 
-namespace Urho3D
+namespace Atomic
 {
 
 /// Navigation area stub.
-struct URHO3D_API NavAreaStub
+struct ATOMIC_API NavAreaStub
 {
     /// Area bounding box.
     BoundingBox bounds_;
@@ -51,7 +51,7 @@ struct URHO3D_API NavAreaStub
 };
 
 /// Navigation build data.
-struct URHO3D_API NavBuildData
+struct ATOMIC_API NavBuildData
 {
     /// Constructor.
     NavBuildData();

+ 1 - 1
Source/Atomic/Navigation/NavigationEvents.h

@@ -24,7 +24,7 @@
 
 #include "../Core/Object.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 /// Complete rebuild of navigation mesh.

+ 1 - 0
Source/Atomic/Navigation/NavigationMesh.h

@@ -68,6 +68,7 @@ struct NavigationGeometryInfo
 };
 
 /// Navigation mesh component. Collects the navigation geometry from child nodes with the Navigable component and responds to path queries.
+class ATOMIC_API NavigationMesh : public Component
 {
     OBJECT(NavigationMesh);
     friend class DetourCrowdManager;

+ 1 - 1
Source/Atomic/Navigation/Obstacle.cpp

@@ -31,7 +31,7 @@
 
 #include "../DebugNew.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 extern const char* NAVIGATION_CATEGORY;

+ 2 - 2
Source/Atomic/Navigation/Obstacle.h

@@ -25,13 +25,13 @@
 #include "../Container/Ptr.h"
 #include "../Scene/Component.h"
 
-namespace Urho3D
+namespace Atomic
 {
 
 class DynamicNavigationMesh;
 
 /// Obstacle for dynamic navigation mesh.
-class URHO3D_API Obstacle : public Component
+class ATOMIC_API Obstacle : public Component
 {
     OBJECT(Obstacle)
     friend class DynamicNavigationMesh;

+ 54 - 37
Source/Atomic/Network/Connection.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -62,6 +62,7 @@ PackageUpload::PackageUpload() :
 
 Connection::Connection(Context* context, bool isClient, kNet::SharedPtr<kNet::MessageConnection> connection) :
     Object(context),
+    timeStamp_(0),
     connection_(connection),
     sendMode_(OPSM_NONE),
     isClient_(isClient),
@@ -272,11 +273,14 @@ void Connection::SendClientUpdate()
     msg_.WriteFloat(controls_.yaw_);
     msg_.WriteFloat(controls_.pitch_);
     msg_.WriteVariantMap(controls_.extraData_);
+    msg_.WriteUByte(timeStamp_);
     if (sendMode_ >= OPSM_POSITION)
         msg_.WriteVector3(position_);
     if (sendMode_ >= OPSM_POSITION_ROTATION)
         msg_.WritePackedQuaternion(rotation_);
     SendMessage(MSG_CONTROLS, false, false, msg_, CONTROLS_CONTENT_ID);
+
+    ++timeStamp_;
 }
 
 void Connection::SendRemoteEvents()
@@ -374,8 +378,8 @@ void Connection::ProcessPendingLatestData()
         {
             MemoryBuffer msg(current->second_);
             msg.ReadNetID(); // Skip the component ID
-            component->ReadLatestDataUpdate(msg);
-            component->ApplyAttributes();
+            if (component->ReadLatestDataUpdate(msg))
+                component->ApplyAttributes();
             componentLatestData_.Erase(current);
         }
     }
@@ -681,8 +685,8 @@ void Connection::ProcessSceneUpdate(int msgID, MemoryBuffer& msg)
             Component* component = scene_->GetComponent(componentID);
             if (component)
             {
-                component->ReadLatestDataUpdate(msg);
-                component->ApplyAttributes();
+                if (component->ReadLatestDataUpdate(msg))
+                    component->ApplyAttributes();
             }
             else
             {
@@ -880,6 +884,8 @@ void Connection::ProcessControls(int msgID, MemoryBuffer& msg)
     newControls.extraData_ = msg.ReadVariantMap();
     
     SetControls(newControls);
+    timeStamp_ = msg.ReadUByte();
+
     // Client may or may not send observer position & rotation for interest management
     if (!msg.IsEof())
         position_ = msg.ReadVector3();
@@ -1012,6 +1018,42 @@ float Connection::GetDownloadProgress() const
     return 1.0f;
 }
 
+void Connection::SendPackageToClient(PackageFile* package)
+{
+    if (!scene_)
+        return;
+
+    if (!IsClient())
+    {
+        LOGERROR("SendPackageToClient can be called on the server only");
+        return;
+    }
+    if (!package)
+    {
+        LOGERROR("Null package specified for SendPackageToClient");
+        return;
+    }
+
+    msg_.Clear();
+
+    String filename = GetFileNameAndExtension(package->GetName());
+    msg_.WriteString(filename);
+    msg_.WriteUInt(package->GetTotalSize());
+    msg_.WriteUInt(package->GetChecksum());
+    SendMessage(MSG_PACKAGEINFO, true, true, msg_);
+}
+
+void Connection::ConfigureNetworkSimulator(int latencyMs, float packetLoss)
+{
+    if (connection_)
+    {
+        kNet::NetworkSimulator& simulator = connection_->NetworkSendSimulator();
+        simulator.enabled = latencyMs > 0 || packetLoss > 0.0f;
+        simulator.constantPacketSendDelay = (float)latencyMs;
+        simulator.packetLossRate = packetLoss;
+    }
+}
+
 void Connection::HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData)
 {
     sceneLoaded_ = true;
@@ -1082,7 +1124,7 @@ void Connection::ProcessNewNode(Node* node)
     node->AddReplicationState(&nodeState);
     
     // Write node's attributes
-    node->WriteInitialDeltaUpdate(msg_);
+    node->WriteInitialDeltaUpdate(msg_, timeStamp_);
     
     // Write node's user variables
     const VariantMap& vars = node->GetVars();
@@ -1111,7 +1153,7 @@ void Connection::ProcessNewNode(Node* node)
         
         msg_.WriteStringHash(component->GetType());
         msg_.WriteNetID(component->GetID());
-        component->WriteInitialDeltaUpdate(msg_);
+        component->WriteInitialDeltaUpdate(msg_, timeStamp_);
     }
     
     SendMessage(MSG_CREATENODE, true, true, msg_);
@@ -1162,7 +1204,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         {
             msg_.Clear();
             msg_.WriteNetID(node->GetID());
-            node->WriteLatestDataUpdate(msg_);
+            node->WriteLatestDataUpdate(msg_, timeStamp_);
             
             SendMessage(MSG_NODELATESTDATA, true, false, msg_, node->GetID());
         }
@@ -1172,7 +1214,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         {
             msg_.Clear();
             msg_.WriteNetID(node->GetID());
-            node->WriteDeltaUpdate(msg_, nodeState.dirtyAttributes_);
+            node->WriteDeltaUpdate(msg_, nodeState.dirtyAttributes_, timeStamp_);
             
             // Write changed variables
             msg_.WriteVLE(nodeState.dirtyVars_.Size());
@@ -1240,7 +1282,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 {
                     msg_.Clear();
                     msg_.WriteNetID(component->GetID());
-                    component->WriteLatestDataUpdate(msg_);
+                    component->WriteLatestDataUpdate(msg_, timeStamp_);
                     
                     SendMessage(MSG_COMPONENTLATESTDATA, true, false, msg_, component->GetID());
                 }
@@ -1250,7 +1292,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 {
                     msg_.Clear();
                     msg_.WriteNetID(component->GetID());
-                    component->WriteDeltaUpdate(msg_, componentState.dirtyAttributes_);
+                    component->WriteDeltaUpdate(msg_, componentState.dirtyAttributes_, timeStamp_);
                     
                     SendMessage(MSG_COMPONENTDELTAUPDATE, true, true, msg_);
                     
@@ -1285,7 +1327,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 msg_.WriteNetID(node->GetID());
                 msg_.WriteStringHash(component->GetType());
                 msg_.WriteNetID(component->GetID());
-                component->WriteInitialDeltaUpdate(msg_);
+                component->WriteInitialDeltaUpdate(msg_, timeStamp_);
                 
                 SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
             }
@@ -1452,31 +1494,6 @@ void Connection::OnPackagesReady()
     }
 }
 
-void Connection::SendPackageToClient(PackageFile* package)
-{
-    if (!scene_)
-        return;
-
-    if (!IsClient())
-    {
-        LOGERROR("SendPackageToClient can be called on the server only");
-        return;
-    }
-    if (!package)
-    {
-        LOGERROR("Null package specified for SendPackageToClient");
-        return;
-    }
-    
-    msg_.Clear();
-
-    String filename = GetFileNameAndExtension(package->GetName());
-    msg_.WriteString(filename);
-    msg_.WriteUInt(package->GetTotalSize());
-    msg_.WriteUInt(package->GetChecksum());
-    SendMessage(MSG_PACKAGEINFO, true, true, msg_);
-}
-
 void Connection::ProcessPackageInfo(int msgID, MemoryBuffer& msg)
 {
     if (!scene_)

+ 7 - 0
Source/Atomic/Network/Connection.h

@@ -157,6 +157,8 @@ public:
     Scene* GetScene() const;
     /// Return the client controls of this connection.
     const Controls& GetControls() const { return controls_; }
+    /// Return the controls timestamp, sent from client to server along each control update.
+    unsigned char GetTimeStamp() const { return timeStamp_; }
     /// Return the observer position sent by the client for interest management.
     const Vector3& GetPosition() const { return position_; }
     /// Return the observer rotation sent by the client for interest management.
@@ -186,8 +188,13 @@ public:
     /// Trigger client connection to download a package file from the server. Can be used to download additional resource packages when client is already joined in a scene. The package must have been added as a requirement to the scene the client is joined in, or else the eventual download will fail.
     void SendPackageToClient(PackageFile* package);
 
+    /// Set network simulation parameters. Called by Network.
+    void ConfigureNetworkSimulator(int latencyMs, float packetLoss);
+
     /// Current controls.
     Controls controls_;
+    /// Controls timestamp. Incremented after each sent update.
+    unsigned char timeStamp_;
     /// Identity map.
     VariantMap identity_;
     

+ 9 - 1
Source/Atomic/Scene/ReplicationState.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -124,6 +124,12 @@ struct ATOMIC_API DirtyBits
 /// Per-object attribute state for network replication, allocated on demand.
 struct ATOMIC_API NetworkState
 {
+    /// Construct with defaults.
+    NetworkState() :
+        interceptMask_(0)
+    {
+    }
+
     /// Cached network attribute infos.
     const Vector<AttributeInfo>* attributes_;
     /// Current network attribute values.
@@ -134,6 +140,8 @@ struct ATOMIC_API NetworkState
     PODVector<ReplicationState*> replicationStates_;
     /// Previous user variables.
     VariantMap previousVars_;
+    /// Bitmask for intercepting network messages. Used on the client only.
+    unsigned long long interceptMask_;
 };
 
 /// Base class for per-user network replication states.

+ 11 - 1
Source/Atomic/Scene/SceneEvents.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -170,4 +170,14 @@ EVENT(E_TEMPORARYCHANGED, TemporaryChanged)
     PARAM(P_SERIALIZABLE, Serializable);    // Serializable pointer
 }
 
+/// A network attribute update from the server has been intercepted.
+EVENT(E_INTERCEPTNETWORKUPDATE, InterceptNetworkUpdate)
+{
+    PARAM(P_SERIALIZABLE, Serializable);    // Serializable pointer
+    PARAM(P_TIMESTAMP, TimeStamp);          // unsigned (0-255)
+    PARAM(P_INDEX, Index);                  // unsigned
+    PARAM(P_NAME, Name);                    // String
+    PARAM(P_VALUE, Value);                  // Variant
+}
+
 }

+ 117 - 10
Source/Atomic/Scene/Serializable.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -35,6 +35,24 @@
 namespace Atomic
 {
 
+static unsigned RemapAttributeIndex(const Vector<AttributeInfo>* attributes, const AttributeInfo& netAttr, unsigned netAttrIndex)
+{
+    if (!attributes)
+        return netAttrIndex; // Could not remap
+
+    for (unsigned i = 0; i < attributes->Size(); ++i)
+    {
+        const AttributeInfo& attr = attributes->At(i);
+        // Compare either the accessor or offset to avoid name string compare
+        if (attr.accessor_.Get() && attr.accessor_.Get() == netAttr.accessor_.Get())
+            return i;
+        else if (!attr.accessor_.Get() && attr.offset_ == netAttr.offset_)
+            return i;
+    }
+
+    return netAttrIndex; // Could not remap
+}
+
 Serializable::Serializable(Context* context) :
     Object(context),
     networkState_(0),
@@ -520,6 +538,28 @@ void Serializable::SetTemporary(bool enable)
     }
 }
 
+void Serializable::SetInterceptNetworkUpdate(const String& attributeName, bool enable)
+{
+    const Vector<AttributeInfo>* attributes = GetNetworkAttributes();
+    if (!attributes)
+        return;
+
+    AllocateNetworkState();
+
+    for (unsigned i = 0; i < attributes->Size(); ++i)
+    {
+        const AttributeInfo& attr = attributes->At(i);
+        if (!attr.name_.Compare(attributeName, true))
+        {
+            if (enable)
+                networkState_->interceptMask_ |= 1ULL << i;
+            else
+                networkState_->interceptMask_ &= ~(1ULL << i);
+            break;
+        }
+    }
+}
+
 void Serializable::AllocateNetworkState()
 {
     if (!networkState_)
@@ -530,7 +570,7 @@ void Serializable::AllocateNetworkState()
     }
 }
 
-void Serializable::WriteInitialDeltaUpdate(Serializer& dest)
+void Serializable::WriteInitialDeltaUpdate(Serializer& dest, unsigned char timeStamp)
 {
     if (!networkState_)
     {
@@ -554,6 +594,7 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest)
     }
 
     // First write the change bitfield, then attribute data for non-default attributes
+    dest.WriteUByte(timeStamp);
     dest.Write(attributeBits.data_, (numAttributes + 7) >> 3);
 
     for (unsigned i = 0; i < numAttributes; ++i)
@@ -563,7 +604,7 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest)
     }
 }
 
-void Serializable::WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits)
+void Serializable::WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits, unsigned char timeStamp)
 {
     if (!networkState_)
     {
@@ -579,6 +620,7 @@ void Serializable::WriteDeltaUpdate(Serializer& dest, const DirtyBits& attribute
 
     // First write the change bitfield, then attribute data for changed attributes
     // Note: the attribute bits should not contain LATESTDATA attributes
+    dest.WriteUByte(timeStamp);
     dest.Write(attributeBits.data_, (numAttributes + 7) >> 3);
 
     for (unsigned i = 0; i < numAttributes; ++i)
@@ -588,7 +630,7 @@ void Serializable::WriteDeltaUpdate(Serializer& dest, const DirtyBits& attribute
     }
 }
 
-void Serializable::WriteLatestDataUpdate(Serializer& dest)
+void Serializable::WriteLatestDataUpdate(Serializer& dest, unsigned char timeStamp)
 {
     if (!networkState_)
     {
@@ -602,6 +644,8 @@ void Serializable::WriteLatestDataUpdate(Serializer& dest)
 
     unsigned numAttributes = attributes->Size();
 
+    dest.WriteUByte(timeStamp);
+
     for (unsigned i = 0; i < numAttributes; ++i)
     {
         if (attributes->At(i).mode_ & AM_LATESTDATA)
@@ -609,15 +653,18 @@ void Serializable::WriteLatestDataUpdate(Serializer& dest)
     }
 }
 
-void Serializable::ReadDeltaUpdate(Deserializer& source)
+bool Serializable::ReadDeltaUpdate(Deserializer& source)
 {
     const Vector<AttributeInfo>* attributes = GetNetworkAttributes();
     if (!attributes)
-        return;
+        return false;
 
     unsigned numAttributes = attributes->Size();
     DirtyBits attributeBits;
+    bool changed = false;
 
+    unsigned long long interceptMask = networkState_ ? networkState_->interceptMask_ : 0;
+    unsigned char timeStamp = source.ReadUByte();
     source.Read(attributeBits.data_, (numAttributes + 7) >> 3);
 
     for (unsigned i = 0; i < numAttributes && !source.IsEof(); ++i)
@@ -625,25 +672,67 @@ void Serializable::ReadDeltaUpdate(Deserializer& source)
         if (attributeBits.IsSet(i))
         {
             const AttributeInfo& attr = attributes->At(i);
-            OnSetAttribute(attr, source.ReadVariant(attr.type_));
+            if (!(interceptMask & (1ULL << i)))
+            {
+                OnSetAttribute(attr, source.ReadVariant(attr.type_));
+                changed = true;
+            }
+            else
+            {
+                using namespace InterceptNetworkUpdate;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_SERIALIZABLE] = this;
+                eventData[P_TIMESTAMP] = (unsigned)timeStamp;
+                eventData[P_INDEX] = RemapAttributeIndex(GetAttributes(), attr, i);
+                eventData[P_NAME] = attr.name_;
+                eventData[P_VALUE] = source.ReadVariant(attr.type_);
+                SendEvent(E_INTERCEPTNETWORKUPDATE, eventData);
+            }
         }
     }
+
+    return changed;
 }
 
-void Serializable::ReadLatestDataUpdate(Deserializer& source)
+bool Serializable::ReadLatestDataUpdate(Deserializer& source)
 {
     const Vector<AttributeInfo>* attributes = GetNetworkAttributes();
     if (!attributes)
-        return;
+        return false;
 
     unsigned numAttributes = attributes->Size();
+    bool changed = false;
+
+    unsigned long long interceptMask = networkState_ ? networkState_->interceptMask_ : 0;
+    unsigned char timeStamp = source.ReadUByte();
 
     for (unsigned i = 0; i < numAttributes && !source.IsEof(); ++i)
     {
         const AttributeInfo& attr = attributes->At(i);
         if (attr.mode_ & AM_LATESTDATA)
-            OnSetAttribute(attr, source.ReadVariant(attr.type_));
+        {
+            if (!(interceptMask & (1ULL << i)))
+            {
+                OnSetAttribute(attr, source.ReadVariant(attr.type_));
+                changed = true;
+            }
+            else
+            {
+                using namespace InterceptNetworkUpdate;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_SERIALIZABLE] = this;
+                eventData[P_TIMESTAMP] = (unsigned)timeStamp;
+                eventData[P_INDEX] = RemapAttributeIndex(GetAttributes(), attr, i);
+                eventData[P_NAME] = attr.name_;
+                eventData[P_VALUE] = source.ReadVariant(attr.type_);
+                SendEvent(E_INTERCEPTNETWORKUPDATE, eventData);
+            }
+        }
     }
+
+    return changed;
 }
 
 Variant Serializable::GetAttribute(unsigned index) const
@@ -745,6 +834,24 @@ unsigned Serializable::GetNumNetworkAttributes() const
     return attributes ? attributes->Size() : 0;
 }
 
+bool Serializable::GetInterceptNetworkUpdate(const String& attributeName) const
+{
+    const Vector<AttributeInfo>* attributes = GetNetworkAttributes();
+    if (!attributes)
+        return false;
+
+    unsigned long long interceptMask = networkState_ ? networkState_->interceptMask_ : 0;
+
+    for (unsigned i = 0; i < attributes->Size(); ++i)
+    {
+        const AttributeInfo& attr = attributes->At(i);
+        if (!attr.name_.Compare(attributeName, true))
+            return interceptMask & (1ULL << i) ? true : false;
+    }
+
+    return false;
+}
+
 void Serializable::SetInstanceDefault(const String& name, const Variant& defaultValue)
 {
     // Allocate the instance level default value

+ 14 - 8
Source/Atomic/Scene/Serializable.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -83,18 +83,20 @@ public:
     void RemoveInstanceDefault();
     /// Set temporary flag. Temporary objects will not be saved.
     void SetTemporary(bool enable);
+    /// Enable interception of an attribute from network updates. Intercepted attributes are sent as events instead of applying directly. This can be used to implement client side prediction.
+    void SetInterceptNetworkUpdate(const String& attributeName, bool enable);
     /// Allocate network attribute state.
     void AllocateNetworkState();
     /// Write initial delta network update.
-    void WriteInitialDeltaUpdate(Serializer& dest);
+    void WriteInitialDeltaUpdate(Serializer& dest, unsigned char timeStamp);
     /// Write a delta network update according to dirty attribute bits.
-    void WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits);
+    void WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits, unsigned char timeStamp);
     /// Write a latest data network update.
-    void WriteLatestDataUpdate(Serializer& dest);
-    /// Read and apply a network delta update.
-    void ReadDeltaUpdate(Deserializer& source);
-    /// Read and apply a network latest data update.
-    void ReadLatestDataUpdate(Deserializer& source);
+    void WriteLatestDataUpdate(Serializer& dest, unsigned char timeStamp);
+    /// Read and apply a network delta update. Return true if attributes were changed.
+    bool ReadDeltaUpdate(Deserializer& source);
+    /// Read and apply a network latest data update. Return true if attributes were changed.
+    bool ReadLatestDataUpdate(Deserializer& source);
 
     /// Return attribute value by index. Return empty if illegal index.
     Variant GetAttribute(unsigned index) const;
@@ -110,6 +112,10 @@ public:
     unsigned GetNumNetworkAttributes() const;
     /// Return whether is temporary.
     bool IsTemporary() const { return temporary_; }
+    /// Return whether an attribute's network updates are being intercepted.
+    bool GetInterceptNetworkUpdate(const String& attributeName) const;
+    /// Return the network attribute state, if allocated.
+    NetworkState* GetNetworkState() const { return networkState_; }
 
 protected:
     /// Network attribute state.

+ 3 - 4
Source/Atomic/UI/UI.cpp

@@ -183,8 +183,7 @@ void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigne
     graphics_->SetColorWrite(true);
     graphics_->SetCullMode(CULL_NONE);
     graphics_->SetDepthTest(CMP_ALWAYS);
-    graphics_->SetDepthWrite(false);
-    graphics_->SetDrawAntialiased(false);
+    graphics_->SetDepthWrite(false);    
     graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetStencilTest(false);
 
@@ -229,7 +228,7 @@ void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigne
         }
 
         graphics_->SetShaders(vs, ps);
-        if (graphics_->NeedParameterUpdate(SP_OBJECTTRANSFORM, this))
+        if (graphics_->NeedParameterUpdate(SP_OBJECT, this))
             graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
         if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
@@ -259,7 +258,7 @@ void UI::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
 }
 
 
-void UI::Render()
+void UI::Render(bool resetRenderTargets)
 {
     SetVertexData(vertexBuffer_, vertexData_);
     Render(vertexBuffer_, batches_, 0, batches_.Size());

+ 1 - 1
Source/Atomic/UI/UI.h

@@ -33,7 +33,7 @@ public:
     void SetKeyboardDisabled(bool disabled) {keyboardDisabled_ = disabled; }
     void SetInputDisabled(bool disabled) { inputDisabled_ = disabled; }
 
-    void Render();
+    void Render(bool resetRenderTargets = true);
     void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
     void SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData);
 

+ 2 - 0
Source/ThirdParty/CMakeLists.txt

@@ -18,6 +18,8 @@ if (NOT EMSCRIPTEN)
 	add_subdirectory(Recast)
 	add_subdirectory(Civetweb)
 	add_subdirectory(Detour)
+        add_subdirectory(DetourCrowd)
+        add_subdirectory(DetourTileCache)
 	add_subdirectory(kNet)
 endif()
 

+ 3 - 32
Source/ThirdParty/DetourCrowd/CMakeLists.txt

@@ -1,36 +1,7 @@
-#
-# Copyright (c) 2008-2015 the Urho3D project.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
 
-# Define target name
-set (TARGET_NAME DetourCrowd)
+include_directories(include ${CMAKE_SOURCE_DIR}/Source/ThirdParty ${CMAKE_SOURCE_DIR}/Source/ThirdParty/Detour/include )
 
-# Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
 
-# Define dependency libs
-set (INCLUDE_DIRS include ../Detour/include)
+file (GLOB SOURCE_FILES source/*.cpp include/*.h)
 
-# Setup target
-setup_library ()
-
-# Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourCrowd FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+add_library(DetourCrowd ${SOURCE_FILES})

+ 2 - 2
Source/ThirdParty/DetourCrowd/include/DetourCrowd.h

@@ -19,7 +19,7 @@
 #ifndef DETOURCROWD_H
 #define DETOURCROWD_H
 
-#include "DetourNavMeshQuery.h"
+#include <Detour/include/DetourNavMeshQuery.h>
 #include "DetourObstacleAvoidance.h"
 #include "DetourLocalBoundary.h"
 #include "DetourPathCorridor.h"
@@ -447,4 +447,4 @@ This value is often based on the agent radius. E.g. radius * 30
 A higher value will result in agents trying to stay farther away from each other at 
 the cost of more difficult steering in tight spaces.
 
-*/
+*/

+ 1 - 1
Source/ThirdParty/DetourCrowd/include/DetourLocalBoundary.h

@@ -19,7 +19,7 @@
 #ifndef DETOURLOCALBOUNDARY_H
 #define DETOURLOCALBOUNDARY_H
 
-#include "DetourNavMeshQuery.h"
+#include <Detour/include/DetourNavMeshQuery.h>
 
 
 class dtLocalBoundary

+ 1 - 1
Source/ThirdParty/DetourCrowd/include/DetourPathCorridor.h

@@ -19,7 +19,7 @@
 #ifndef DETOUTPATHCORRIDOR_H
 #define DETOUTPATHCORRIDOR_H
 
-#include "DetourNavMeshQuery.h"
+#include <Detour/include/DetourNavMeshQuery.h>
 
 /// Represents a dynamic polygon corridor used to plan agent movement.
 /// @ingroup crowd, detour

+ 2 - 2
Source/ThirdParty/DetourCrowd/include/DetourPathQueue.h

@@ -19,8 +19,8 @@
 #ifndef DETOURPATHQUEUE_H
 #define DETOURPATHQUEUE_H
 
-#include "DetourNavMesh.h"
-#include "DetourNavMeshQuery.h"
+#include <Detour/include/DetourNavMesh.h>
+#include <Detour/include/DetourNavMeshQuery.h>
 
 static const unsigned int DT_PATHQ_INVALID = 0;
 

+ 3 - 34
Source/ThirdParty/DetourTileCache/CMakeLists.txt

@@ -1,36 +1,5 @@
-#
-# Copyright (c) 2008-2015 the Urho3D project.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
+include_directories(include ${CMAKE_SOURCE_DIR}/Source/ThirdParty ${CMAKE_SOURCE_DIR}/Source/ThirdParty/Detour/include )
 
-# Define target name
-set (TARGET_NAME DetourTileCache)
+file (GLOB SOURCE_FILES source/*.cpp include/*.h)
 
-# Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
-
-# Define dependency libs
-set (INCLUDE_DIRS include ../Detour/include)
-
-# Setup target
-setup_library ()
-
-# Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourTileCache FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+add_library(DetourTileCache ${SOURCE_FILES})

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