Browse Source

Fixed vertex lighting shader variation names.

Lasse Öörni 14 years ago
parent
commit
a69b8b7568
2 changed files with 1791 additions and 1787 deletions
  1. 1397 1395
      Engine/Graphics/Renderer.cpp
  2. 394 392
      Engine/Graphics/Renderer.h

+ 1397 - 1395
Engine/Graphics/Renderer.cpp

@@ -1,1395 +1,1397 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2011 Lasse Öörni
-//
-// 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 "Precompiled.h"
-#include "Camera.h"
-#include "CoreEvents.h"
-#include "DebugRenderer.h"
-#include "Geometry.h"
-#include "Graphics.h"
-#include "GraphicsEvents.h"
-#include "GraphicsImpl.h"
-#include "IndexBuffer.h"
-#include "Light.h"
-#include "Log.h"
-#include "Material.h"
-#include "OcclusionBuffer.h"
-#include "Octree.h"
-#include "Profiler.h"
-#include "Renderer.h"
-#include "ResourceCache.h"
-#include "Scene.h"
-#include "Shader.h"
-#include "ShaderVariation.h"
-#include "Technique.h"
-#include "Texture2D.h"
-#include "TextureCube.h"
-#include "VertexBuffer.h"
-#include "View.h"
-#include "XMLFile.h"
-#include "Zone.h"
-
-#include "DebugNew.h"
-
-static const float pointLightVertexData[] =
-{
-    -0.423169f, -1.000000f, 0.423169f,
-    -0.423169f, -1.000000f, -0.423169f,
-    0.423169f, -1.000000f, -0.423169f,
-    0.423169f, -1.000000f, 0.423169f,
-    0.423169f, 1.000000f, -0.423169f,
-    -0.423169f, 1.000000f, -0.423169f,
-    -0.423169f, 1.000000f, 0.423169f,
-    0.423169f, 1.000000f, 0.423169f,
-    -1.000000f, 0.423169f, -0.423169f,
-    -1.000000f, -0.423169f, -0.423169f,
-    -1.000000f, -0.423169f, 0.423169f,
-    -1.000000f, 0.423169f, 0.423169f,
-    0.423169f, 0.423169f, -1.000000f,
-    0.423169f, -0.423169f, -1.000000f,
-    -0.423169f, -0.423169f, -1.000000f,
-    -0.423169f, 0.423169f, -1.000000f,
-    1.000000f, 0.423169f, 0.423169f,
-    1.000000f, -0.423169f, 0.423169f,
-    1.000000f, -0.423169f, -0.423169f,
-    1.000000f, 0.423169f, -0.423169f,
-    0.423169f, -0.423169f, 1.000000f,
-    0.423169f, 0.423169f, 1.000000f,
-    -0.423169f, 0.423169f, 1.000000f,
-    -0.423169f, -0.423169f, 1.000000f
-};
-
-static const unsigned short pointLightIndexData[] =
-{
-    0, 1, 2,
-    0, 2, 3,
-    4, 5, 6,
-    4, 6, 7,
-    8, 9, 10,
-    8, 10, 11,
-    12, 13, 14,
-    12, 14, 15,
-    16, 17, 18,
-    16, 18, 19,
-    20, 21, 22,
-    20, 22, 23,
-    0, 10, 9,
-    0, 9, 1,
-    13, 2, 1,
-    13, 1, 14,
-    23, 0, 3,
-    23, 3, 20,
-    17, 3, 2,
-    17, 2, 18,
-    21, 7, 6,
-    21, 6, 22,
-    7, 16, 19,
-    7, 19, 4,
-    5, 8, 11,
-    5, 11, 6,
-    4, 12, 15,
-    4, 15, 5,
-    22, 11, 10,
-    22, 10, 23,
-    8, 15, 14,
-    8, 14, 9,
-    12, 19, 18,
-    12, 18, 13,
-    16, 21, 20,
-    16, 20, 17,
-    0, 23, 10,
-    1, 9, 14,
-    2, 13, 18,
-    3, 17, 20,
-    6, 11, 22,
-    5, 15, 8,
-    4, 19, 12,
-    7, 21, 16
-};
-
-static const float spotLightVertexData[] =
-{
-    0.001f, 0.001f, 0.001f,
-    0.001f, -0.001f, 0.001f,
-    -0.001f, -0.001f, 0.001f,
-    -0.001f, 0.001f, 0.001f,
-    1.0f, 1.0f, 1.0f,
-    1.0f, -1.0f, 1.0f,
-    -1.0f,  -1.0f, 1.0f,
-    -1.0f, 1.0f, 1.0f,
-};
-
-static const unsigned short spotLightIndexData[] =
-{
-    3, 0, 1,
-    3, 1, 2,
-    0, 4, 5,
-    0, 5, 1,
-    3, 7, 4,
-    3, 4, 0,
-    7, 3, 2,
-    7, 2, 6,
-    6, 2, 1,
-    6, 1, 5,
-    7, 5, 4,
-    7, 6, 5
-};
-
-static const String shadowVariations[] =
-{
-    // No specific hardware shadow compare variation on OpenGL, it is always supported
-    #ifdef USE_OPENGL
-    "LQ",
-    "LQ",
-    "",
-    ""
-    #else
-    "",
-    "LQHW",
-    "",
-    "HW"
-    #endif
-};
-
-static const String fallbackVariations[] =
-{
-    "",
-    "FB"
-};
-
-static const String geometryVSVariations[] =
-{
-    "",
-    "Skinned",
-    "Instanced",
-    "Billboard"
-};
-
-static const String lightVSVariations[] =
-{
-    "Dir",
-    "Spot",
-    "Point",
-    "DirSpec",
-    "SpotSpec",
-    "PointSpec",
-    "DirShadow",
-    "SpotShadow",
-    "PointShadow",
-    "DirSpecShadow",
-    "SpotSpecShadow",
-    "PointSpecShadow"
-};
-
-static const String vertexLightVSVariations[] =
-{
-    "",
-    "1VL",
-    "2VL",
-    "3VL",
-    "4VL"
-};
-
-static const String lightPSVariations[] = 
-{
-    "Dir",
-    "Spot",
-    "Point",
-    "PointMask",
-    "DirSpec",
-    "SpotSpec",
-    "PointSpec",
-    "PointMaskSpec",
-    "DirShadow",
-    "SpotShadow",
-    "PointShadow",
-    "PointMaskShadow",
-    "DirSpecShadow",
-    "SpotSpecShadow",
-    "PointSpecShadow",
-    "PointMaskSpecShadow"
-};
-
-static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3;
-static const Viewport noViewport;
-
-OBJECTTYPESTATIC(Renderer);
-
-Renderer::Renderer(Context* context) :
-    Object(context),
-    defaultZone_(new Zone(context)),
-    numViews_(0),
-    numShadowCameras_(0),
-    numOcclusionBuffers_(0),
-    textureAnisotropy_(4),
-    textureFilterMode_(FILTER_TRILINEAR),
-    textureQuality_(QUALITY_HIGH),
-    materialQuality_(QUALITY_HIGH),
-    shadowMapSize_(1024),
-    shadowQuality_(SHADOWQUALITY_HIGH_16BIT),
-    maxShadowMaps_(1),
-    maxShadowCascades_(MAX_CASCADE_SPLITS),
-    maxInstanceTriangles_(500),
-    maxOccluderTriangles_(5000),
-    occlusionBufferSize_(256),
-    occluderSizeThreshold_(0.1f),
-    shadersChangedFrameNumber_(M_MAX_UNSIGNED),
-    specularLighting_(true),
-    drawShadows_(true),
-    reuseShadowMaps_(true),
-    lightStencilMasking_(true),
-    dynamicInstancing_(true),
-    shadersDirty_(true),
-    initialized_(false)
-{
-    SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
-    SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
-    SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate));
-    
-    // Try to initialize right now, but skip if screen mode is not yet set
-    Initialize();
-}
-
-Renderer::~Renderer()
-{
-}
-
-void Renderer::SetNumViewports(unsigned num)
-{
-    viewports_.Resize(num);
-}
-
-void Renderer::SetViewport(unsigned index, const Viewport& viewport)
-{
-    if (index >= viewports_.Size())
-    {
-        LOGERROR("Viewport index out of bounds");
-        return;
-    }
-    
-    viewports_[index] = viewport;
-}
-
-void Renderer::SetSpecularLighting(bool enable)
-{
-    specularLighting_ = enable;
-}
-
-void Renderer::SetTextureAnisotropy(int level)
-{
-    textureAnisotropy_ = Max(level, 1);
-}
-
-void Renderer::SetTextureFilterMode(TextureFilterMode mode)
-{
-    textureFilterMode_ = mode;
-}
-
-void Renderer::SetTextureQuality(int quality)
-{
-    quality = Clamp(quality, QUALITY_LOW, QUALITY_HIGH);
-    
-    if (quality != textureQuality_)
-    {
-        textureQuality_ = quality;
-        ReloadTextures();
-    }
-}
-
-void Renderer::SetMaterialQuality(int quality)
-{
-    materialQuality_ = Clamp(quality, QUALITY_LOW, QUALITY_MAX);
-    shadersDirty_ = true;
-    ResetViews();
-}
-
-void Renderer::SetDrawShadows(bool enable)
-{
-    if (!graphics_)
-        return;
-    
-    drawShadows_ = enable;
-    if (!drawShadows_)
-        ResetShadowMaps();
-}
-
-void Renderer::SetShadowMapSize(int size)
-{
-    if (!graphics_)
-        return;
-    
-    size = NextPowerOfTwo(Max(size, SHADOW_MIN_PIXELS));
-    if (size != shadowMapSize_)
-    {
-        shadowMapSize_ = size;
-        ResetShadowMaps();
-    }
-}
-
-void Renderer::SetShadowQuality(int quality)
-{
-    if (!graphics_)
-        return;
-    
-    quality &= SHADOWQUALITY_HIGH_24BIT;
-    
-    // If no hardware PCF, do not allow to select one-sample quality
-    if (!graphics_->GetHardwareShadowSupport())
-        quality |= SHADOWQUALITY_HIGH_16BIT;
-    if (!graphics_->GetHiresShadowSupport())
-        quality &= SHADOWQUALITY_HIGH_16BIT;
-    if (graphics_->GetFallback())
-        quality = SHADOWQUALITY_LOW_16BIT;
-    
-    if (quality != shadowQuality_)
-    {
-        shadowQuality_ = quality;
-        shadersDirty_ = true;
-        ResetShadowMaps();
-    }
-}
-
-void Renderer::SetReuseShadowMaps(bool enable)
-{
-    if (enable == reuseShadowMaps_)
-        return;
-    
-    reuseShadowMaps_ = enable;
-}
-
-void Renderer::SetMaxShadowMaps(int shadowMaps)
-{
-    if (shadowMaps < 1)
-        return;
-    
-    maxShadowMaps_ = shadowMaps;
-    for (HashMap<int, Vector<SharedPtr<Texture2D> > >::Iterator i = shadowMaps_.Begin(); i != shadowMaps_.End(); ++i)
-    {
-        if ((int)i->second_.Size() > maxShadowMaps_)
-            i->second_.Resize(maxShadowMaps_);
-    }
-}
-
-void Renderer::SetMaxShadowCascades(int cascades)
-{
-    cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
-    
-    if (cascades != maxShadowCascades_)
-    {
-        maxShadowCascades_ = cascades;
-        ResetShadowMaps();
-    }
-}
-
-void Renderer::SetLightStencilMasking(bool enable)
-{
-    lightStencilMasking_ = enable;
-}
-
-void Renderer::SetDynamicInstancing(bool enable)
-{
-    if (!instancingBuffer_)
-        enable = false;
-    
-    dynamicInstancing_ = enable;
-}
-
-void Renderer::SetMaxInstanceTriangles(int triangles)
-{
-    maxInstanceTriangles_ = Max(triangles, 0);
-}
-
-void Renderer::SetMaxOccluderTriangles(int triangles)
-{
-    maxOccluderTriangles_ = Max(triangles, 0);
-}
-
-void Renderer::SetOcclusionBufferSize(int size)
-{
-    occlusionBufferSize_ = Max(size, 1);
-    occlusionBuffers_.Clear();
-}
-
-void Renderer::SetOccluderSizeThreshold(float screenSize)
-{
-    occluderSizeThreshold_ = Max(screenSize, 0.0f);
-}
-
-const Viewport& Renderer::GetViewport(unsigned index) const
-{
-    return index < viewports_.Size() ? viewports_[index] : noViewport;
-}
-
-ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const
-{
-    return GetShader(name, vsFormat_, checkExists);
-}
-
-ShaderVariation* Renderer::GetPixelShader(const String& name, bool checkExists) const
-{
-    return GetShader(name, psFormat_, checkExists);
-}
-
-unsigned Renderer::GetNumGeometries(bool allViews) const
-{
-    unsigned numGeometries = 0;
-    unsigned lastView = allViews ? numViews_ : 1;
-    
-    for (unsigned i = 0; i < lastView; ++i)
-        numGeometries += views_[i]->GetGeometries().Size();
-    
-    return numGeometries;
-}
-
-unsigned Renderer::GetNumLights(bool allViews) const
-{
-    unsigned numLights = 0;
-    unsigned lastView = allViews ? numViews_ : 1;
-    
-    for (unsigned i = 0; i < lastView; ++i)
-        numLights += views_[i]->GetLights().Size();
-    
-    return numLights;
-}
-
-unsigned Renderer::GetNumShadowMaps(bool allViews) const
-{
-    unsigned numShadowMaps = 0;
-    unsigned lastView = allViews ? numViews_ : 1;
-    
-    for (unsigned i = 0; i < lastView; ++i)
-    {
-        const List<LightBatchQueue>& lightQueues = views_[i]->GetLightQueues();
-        
-        for (List<LightBatchQueue>::ConstIterator i = lightQueues.Begin(); i != lightQueues.End(); ++i)
-        {
-            if (i->shadowMap_)
-                ++numShadowMaps;
-        }
-    }
-    
-    return numShadowMaps;
-}
-
-unsigned Renderer::GetNumOccluders(bool allViews) const
-{
-    unsigned numOccluders = 0;
-    unsigned lastView = allViews ? numViews_ : 1;
-    
-    for (unsigned i = 0; i < lastView; ++i)
-        numOccluders += views_[i]->GetOccluders().Size();
-    
-    return numOccluders;
-}
-
-void Renderer::Update(float timeStep)
-{
-    PROFILE(UpdateViews);
-    
-    numViews_ = 0;
-    
-    // If device lost, do not perform update. This is because any dynamic vertex/index buffer updates happen already here,
-    // and if the device is lost, the updates queue up, causing memory use to rise constantly
-    if (!graphics_ || !graphics_->IsInitialized() || graphics_->IsDeviceLost())
-        return;
-    
-    // Advance frame number & time, set up the frameinfo structure, and reset views & stats
-    frame_.frameNumber_ = GetSubsystem<Time>()->GetFrameNumber();
-    frame_.timeStep_ = timeStep;
-    frame_.camera_ = 0;
-    numShadowCameras_ = 0;
-    numOcclusionBuffers_ = 0;
-    updateOctrees_.Clear();
-    
-    // Reload shaders if needed
-    if (shadersDirty_)
-        LoadShaders();
-    
-    // Process all viewports. Use reverse order, because during rendering the order will be reversed again to handle auxiliary
-    // view dependencies correctly
-    for (unsigned i = viewports_.Size() - 1; i < viewports_.Size(); --i)
-    {
-        unsigned mainView = numViews_;
-        Viewport& viewport = viewports_[i];
-        if (!AddView(0, viewport))
-            continue;
-        
-        // Update octree (perform early update for nodes which need that, and reinsert moved nodes.)
-        // However, if the same scene is viewed from multiple cameras, update the octree only once
-        Octree* octree = viewport.scene_->GetComponent<Octree>();
-        DebugRenderer* debug = viewport.scene_->GetComponent<DebugRenderer>();
-        if (!updateOctrees_.Contains(octree))
-        {
-            frame_.camera_ = viewport.camera_;
-            frame_.viewSize_ = IntVector2(viewport.rect_.right_ - viewport.rect_.left_, viewport.rect_.bottom_ - viewport.rect_.top_);
-            if (frame_.viewSize_ == IntVector2::ZERO)
-                frame_.viewSize_ = IntVector2(graphics_->GetWidth(), graphics_->GetHeight());
-            octree->Update(frame_);
-            updateOctrees_.Insert(octree);
-            
-            // Set also the view for the debug graphics already here, so that it can use culling
-            /// \todo May result in incorrect debug geometry culling if the same scene is drawn from multiple viewports
-            if (debug)
-                debug->SetView(viewport.camera_);
-        }
-        
-        // Update the viewport's main view and any auxiliary views it creates
-        for (unsigned i = mainView; i < numViews_; ++i)
-            views_[i]->Update(frame_);
-    }
-    
-    return;
-}
-
-void Renderer::Render()
-{
-    if (!graphics_)
-        return;
-    
-    PROFILE(RenderViews);
-    
-    graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
-    graphics_->SetTextureAnisotropy(textureAnisotropy_);
-    
-    // If no views, just clear the screen
-    if (!numViews_)
-    {
-        numPrimitives_ = 0;
-        numBatches_ = 0;
-        graphics_->SetAlphaTest(false);
-        graphics_->SetBlendMode(BLEND_REPLACE);
-        graphics_->SetColorWrite(true);
-        graphics_->SetDepthWrite(true);
-        graphics_->SetFillMode(FILL_SOLID);
-        graphics_->SetScissorTest(false);
-        graphics_->SetStencilTest(false);
-        graphics_->Clear(CLEAR_COLOR | CLEAR_DEPTH | CLEAR_STENCIL);
-        return;
-    }
-    
-    // Render views from last to first (each main view is rendered after the auxiliary views it depends on)
-    for (unsigned i = numViews_ - 1; i < numViews_; --i)
-        views_[i]->Render();
-    
-    // Copy the number of batches & primitives from Graphics so that we can account for 3D geometry only
-    numPrimitives_ = graphics_->GetNumPrimitives();
-    numBatches_ = graphics_->GetNumBatches();
-}
-
-void Renderer::DrawDebugGeometry(bool depthTest)
-{
-    PROFILE(RendererDrawDebug);
-    
-    /// \todo Because debug geometry is per-scene, if two cameras show views of the same area, occlusion is not shown correctly
-    HashSet<Drawable*> processedGeometries;
-    HashSet<Light*> processedLights;
-    
-    for (unsigned i = 0; i < numViews_; ++i)
-    {
-        // Make sure it's a main view, and process each node only once
-        View* view = views_[i];
-        if (view->GetRenderTarget())
-            continue;
-        Octree* octree = view->GetOctree();
-        if (!octree)
-            continue;
-        Scene* scene = static_cast<Scene*>(octree->GetNode());
-        if (!scene)
-            continue;
-        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
-        if (!debug)
-            continue;
-        
-        const PODVector<Drawable*>& geometries = view->GetGeometries();
-        const PODVector<Light*>& lights = view->GetLights();
-        
-        for (unsigned i = 0; i < geometries.Size(); ++i)
-        {
-            if (!processedGeometries.Contains(geometries[i]))
-            {
-                geometries[i]->DrawDebugGeometry(debug, depthTest);
-                processedGeometries.Insert(geometries[i]);
-            }
-        }
-        for (unsigned i = 0; i < lights.Size(); ++i)
-        {
-            if (!processedLights.Contains(lights[i]))
-            {
-                lights[i]->DrawDebugGeometry(debug, depthTest);
-                processedLights.Insert(lights[i]);
-            }
-        }
-    }
-}
-
-bool Renderer::AddView(RenderSurface* renderTarget, const Viewport& viewport)
-{
-    // If using a render target texture, make sure it will not be rendered to multiple times
-    if (renderTarget)
-    {
-        for (unsigned i = 0; i < numViews_; ++i)
-        {
-            if (views_[i]->GetRenderTarget() == renderTarget)
-                return false;
-        }
-    }
-    
-    if (views_.Size() <= numViews_)
-        views_.Resize(numViews_ + 1);
-    if (!views_[numViews_])
-        views_[numViews_] = new View(context_);
-    
-    if (views_[numViews_]->Define(renderTarget, viewport))
-    {
-        ++numViews_;
-        return true;
-    }
-    else
-        return false;
-}
-
-Geometry* Renderer::GetLightGeometry(Light* light)
-{
-    LightType type = light->GetLightType();
-    if (type == LIGHT_SPOT)
-        return spotLightGeometry_;
-    else if (type == LIGHT_POINT)
-        return pointLightGeometry_;
-    else
-        return 0;
-}
-
-Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight)
-{
-    LightType type = light->GetLightType();
-    const FocusParameters& parameters = light->GetShadowFocus();
-    float size = (float)shadowMapSize_ * light->GetShadowResolution();
-    // Automatically reduce shadow map size when far away
-    if (parameters.autoSize_ && type != LIGHT_DIRECTIONAL)
-    {
-        Matrix3x4 view(camera->GetInverseWorldTransform());
-        Matrix4 projection(camera->GetProjection());
-        BoundingBox lightBox;
-        float lightPixels;
-        
-        if (type == LIGHT_POINT)
-        {
-            // Calculate point light pixel size from the projection of its diagonal
-            Vector3 center = view * light->GetWorldPosition();
-            float extent = 0.58f * light->GetRange();
-            lightBox.Define(center + Vector3(extent, extent, extent), center - Vector3(extent, extent, extent));
-        }
-        else
-        {
-            // Calculate spot light pixel size from the projection of its frustum far vertices
-            Frustum lightFrustum = light->GetFrustum().Transformed(view);
-            lightBox.Define(&lightFrustum.vertices_[4], 4);
-        }
-        
-        Vector2 projectionSize = lightBox.Projected(projection).Size();
-        lightPixels = Max(0.5f * (float)viewWidth * projectionSize.x_, 0.5f * (float)viewHeight * projectionSize.y_);
-        
-        // Clamp pixel amount to a sufficient minimum to avoid self-shadowing artifacts due to loss of precision
-        if (lightPixels < SHADOW_MIN_PIXELS)
-            lightPixels = SHADOW_MIN_PIXELS;
-        
-        size = Min(size, lightPixels);
-    }
-    
-    /// \todo Allow to specify maximum shadow maps per resolution, as smaller shadow maps take less memory
-    int width = NextPowerOfTwo((unsigned)size);
-    int height = width;
-    
-    // Adjust the size for directional or point light shadow map atlases
-    if (type == LIGHT_DIRECTIONAL)
-    {
-        if (maxShadowCascades_ > 1)
-            width *= 2;
-        if (maxShadowCascades_ > 2)
-            height *= 2;
-    }
-    else if (type == LIGHT_POINT)
-    {
-        width *= 2;
-        height *= 3;
-    }
-    
-    int searchKey = (width << 16) | height;
-    if (shadowMaps_.Contains(searchKey))
-    {
-        // If shadow maps are reused, always return the first
-        if (reuseShadowMaps_)
-            return shadowMaps_[searchKey][0];
-        else
-        {
-            // If not reused, check allocation count and return existing shadow map if possible
-            unsigned allocated = shadowMapAllocations_[searchKey].Size();
-            if (allocated < shadowMaps_[searchKey].Size())
-            {
-                shadowMapAllocations_[searchKey].Push(light);
-                return shadowMaps_[searchKey][allocated];
-            }
-            else if ((int)allocated >= maxShadowMaps_)
-                return 0;
-        }
-    }
-    
-    unsigned shadowMapFormat = (shadowQuality_ & SHADOWQUALITY_LOW_24BIT) ? graphics_->GetHiresShadowMapFormat() :
-        graphics_->GetShadowMapFormat();
-    unsigned dummyColorFormat = graphics_->GetDummyColorFormat();
-    if (!shadowMapFormat)
-        return 0;
-    
-    SharedPtr<Texture2D> newShadowMap(new Texture2D(context_));
-    int retries = 3;
-    
-    #ifdef USE_OPENGL
-    // Create shadow map only. Color rendertarget is not needed
-    while (retries)
-    {
-        if (!newShadowMap->SetSize(width, height, shadowMapFormat, TEXTURE_DEPTHSTENCIL))
-        {
-            width >>= 1;
-            height >>= 1;
-            --retries;
-        }
-        else
-        {
-            newShadowMap->SetFilterMode(FILTER_BILINEAR);
-            newShadowMap->SetShadowCompare(true);
-            break;
-        }
-    }
-    #else
-    // Create shadow map and dummy color rendertarget
-    bool fallback = graphics_->GetFallback();
-    while (retries)
-    {
-        if (!newShadowMap->SetSize(width, height, shadowMapFormat, fallback ? TEXTURE_RENDERTARGET : TEXTURE_DEPTHSTENCIL))
-        {
-            width >>= 1;
-            height >>= 1;
-            --retries;
-        }
-        else
-        {
-            newShadowMap->SetFilterMode(FILTER_BILINEAR);
-            if (!fallback)
-            {
-                // If no dummy color rendertarget for this size exists yet, create one now
-                if (!colorShadowMaps_.Contains(searchKey))
-                {
-                    colorShadowMaps_[searchKey] = new Texture2D(context_);
-                    colorShadowMaps_[searchKey]->SetSize(width, height, dummyColorFormat, TEXTURE_RENDERTARGET);
-                }
-                // Link the color rendertarget to the shadow map
-                newShadowMap->GetRenderSurface()->SetLinkedRenderTarget(colorShadowMaps_[searchKey]->GetRenderSurface());
-            }
-            else
-            {
-                // In fallback mode link the shared shadow map depth stencil to the shadow map instead.
-                // Create it first if not created yet, and resize larger if necessary
-                if (!shadowDepthStencil_)
-                    shadowDepthStencil_ = new Texture2D(context_);
-                if (shadowDepthStencil_->GetWidth() < width || shadowDepthStencil_->GetHeight() < height)
-                    shadowDepthStencil_->SetSize(width, height, D3DFMT_D16, TEXTURE_DEPTHSTENCIL);
-                newShadowMap->GetRenderSurface()->SetLinkedDepthBuffer(shadowDepthStencil_->GetRenderSurface());
-            }
-            break;
-        }
-    }
-    #endif
-    // If failed to set size, store a null pointer so that we will not retry
-    if (!retries)
-        newShadowMap.Reset();
-    
-    shadowMaps_[searchKey].Push(newShadowMap);
-    if (!reuseShadowMaps_)
-        shadowMapAllocations_[searchKey].Push(light);
-    
-    return newShadowMap;
-}
-
-OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
-{
-    if (numOcclusionBuffers_ >= occlusionBuffers_.Size())
-    {
-        SharedPtr<OcclusionBuffer> newBuffer(new OcclusionBuffer(context_));
-        occlusionBuffers_.Push(newBuffer);
-    }
-    
-    int width = occlusionBufferSize_;
-    int height = (int)((float)occlusionBufferSize_ / camera->GetAspectRatio() + 0.5f);
-    
-    OcclusionBuffer* buffer = occlusionBuffers_[numOcclusionBuffers_];
-    buffer->SetSize(width, height);
-    buffer->SetView(camera);
-    
-    ++numOcclusionBuffers_;
-    return buffer;
-}
-
-Camera* Renderer::GetShadowCamera()
-{
-    MutexLock lock(rendererMutex_);
-    
-    if (numShadowCameras_ >= shadowCameraNodes_.Size())
-    {
-        SharedPtr<Node> newNode(new Node(context_));
-        newNode->CreateComponent<Camera>();
-        shadowCameraNodes_.Push(newNode);
-    }
-    
-    Camera* camera = shadowCameraNodes_[numShadowCameras_]->GetComponent<Camera>();
-    camera->SetOrthographic(false);
-    camera->SetZoom(1.0f);
-    
-    ++numShadowCameras_;
-    return camera;
-}
-
-ShaderVariation* Renderer::GetShader(const String& name, const String& extension, bool checkExists) const
-{
-    String shaderName = shaderPath_;
-    String variationName;
-    
-    unsigned split = name.Find('_');
-    if (split != String::NPOS)
-    {
-        shaderName += name.Substring(0, split) + extension;
-        variationName = name.Substring(split + 1);
-    }
-    else
-        shaderName += name + extension;
-    
-    if (checkExists)
-    {
-        if (!cache_->Exists(shaderName))
-            return 0;
-    }
-    
-    Shader* shader = cache_->GetResource<Shader>(shaderName);
-    if (shader)
-        return shader->GetVariation(variationName);
-    else
-        return 0;
-}
-
-void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows)
-{
-    // Check if shaders are unloaded or need reloading
-    Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
-    Vector<SharedPtr<ShaderVariation> >& pixelShaders = pass->GetPixelShaders();
-    if (!vertexShaders.Size() || !pixelShaders.Size() || technique->GetShadersLoadedFrameNumber() !=
-        shadersChangedFrameNumber_)
-    {
-        // First release all previous shaders, then load
-        technique->ReleaseShaders();
-        LoadMaterialShaders(technique);
-    }
-    
-    // Make sure shaders are loaded now
-    if (vertexShaders.Size() && pixelShaders.Size())
-    {
-        GeometryType geomType = batch.geometryType_;
-        // If instancing is not supported, but was requested, or the object is too large to be instanced,
-        // choose static geometry vertex shader instead
-        if (geomType == GEOM_INSTANCED && (!GetDynamicInstancing() || batch.geometry_->GetIndexCount() >
-            (unsigned)maxInstanceTriangles_ * 3))
-            geomType = GEOM_STATIC;
-        
-        //  Check whether is a lit pass. If not, there is only one pixel shader
-        PassType type = pass->GetType();
-        if (type == PASS_LIGHT || type == PASS_LITBASE)
-        {
-            LightBatchQueue* lightQueue = batch.lightQueue_;
-            if (!lightQueue)
-            {
-                // Do not log error, as it would result in a lot of spam
-                batch.vertexShader_ = 0;
-                batch.pixelShader_ = 0;
-                return;
-            }
-            
-            Light* light = lightQueue->light_;
-            unsigned vsi = 0;
-            unsigned psi = 0;
-            vsi = geomType * MAX_LIGHT_VS_VARIATIONS;
-            
-            bool materialHasSpecular = batch.material_ ? batch.material_->GetSpecular() : true;
-            if (specularLighting_ && light->GetSpecularIntensity() > 0.0f && materialHasSpecular)
-            {
-                vsi += LVS_SPEC;
-                psi += LPS_SPEC;
-            }
-            if (allowShadows && lightQueue->shadowMap_)
-            {
-                vsi += LVS_SHADOW;
-                psi += LPS_SHADOW;
-            }
-            
-            switch (light->GetLightType())
-            {
-            case LIGHT_DIRECTIONAL:
-                vsi += LVS_DIR;
-                break;
-                
-            case LIGHT_POINT:
-                if (light->GetShapeTexture())
-                    psi += LPS_POINTMASK;
-                else
-                    psi += LPS_POINT;
-                vsi += LVS_POINT;
-                break;
-                
-            case LIGHT_SPOT:
-                psi += LPS_SPOT;
-                vsi += LVS_SPOT;
-                break;
-            }
-            
-            batch.vertexShader_ = vertexShaders[vsi];
-            batch.pixelShader_ = pixelShaders[psi];
-            
-            // If shadow or specular variations do not exist, try without them
-            if ((!batch.vertexShader_ || !batch.pixelShader_) && (vsi >= LVS_SHADOW))
-            {
-                vsi -= LVS_SHADOW;
-                psi -= LPS_SHADOW;
-                batch.vertexShader_ = vertexShaders[vsi];
-                batch.pixelShader_ = pixelShaders[psi];
-            }
-            if ((!batch.vertexShader_ || !batch.pixelShader_) && (vsi >= LVS_SPEC))
-            {
-                vsi -= LVS_SPEC;
-                psi -= LPS_SPEC;
-                batch.vertexShader_ = vertexShaders[vsi];
-                batch.pixelShader_ = pixelShaders[psi];
-            }
-        }
-        else
-        {
-            if (type == PASS_BASE)
-            {
-                unsigned numVertexLights = 0;
-                if (batch.lightQueue_)
-                    numVertexLights = batch.lightQueue_->vertexLights_.Size();
-                
-                unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS + numVertexLights;
-                batch.vertexShader_ = vertexShaders[vsi];
-                // If vertex lights variations do not exist, try without them
-                if (!batch.vertexShader_)
-                {
-                    unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS;
-                    batch.vertexShader_ = vertexShaders[vsi];
-                }
-            }
-            else
-            {
-                unsigned vsi = geomType;
-                batch.vertexShader_ = vertexShaders[vsi];
-            }
-            
-            batch.pixelShader_ = pixelShaders[0];
-        }
-    }
-    
-    // Log error if shaders could not be assigned, but only once per technique
-    if (!batch.vertexShader_ || !batch.pixelShader_)
-    {
-        if (!shaderErrorDisplayed_.Contains(technique))
-        {
-            shaderErrorDisplayed_.Insert(technique);
-            LOGERROR("Technique " + technique->GetName() + " has missing shaders");
-        }
-    }
-}
-
-bool Renderer::ResizeInstancingBuffer(unsigned numInstances)
-{
-    if (!instancingBuffer_ || !dynamicInstancing_)
-        return false;
-    
-    unsigned oldSize = instancingBuffer_->GetVertexCount();
-    if (numInstances <= oldSize)
-        return true;
-    
-    unsigned newSize = INSTANCING_BUFFER_DEFAULT_SIZE;
-    while (newSize < numInstances)
-        newSize <<= 1;
-    
-    if (!instancingBuffer_->SetSize(newSize, INSTANCING_BUFFER_MASK, true))
-    {
-        LOGERROR("Failed to resize instancing buffer to " + String(newSize));
-        // If failed, try to restore the old size
-        instancingBuffer_->SetSize(oldSize, INSTANCING_BUFFER_MASK, true);
-        return false;
-    }
-    
-    LOGDEBUG("Resized instancing buffer to " + String(newSize));
-    return true;
-}
-
-void Renderer::ResetShadowMapAllocations()
-{
-    for (HashMap<int, PODVector<Light*> >::Iterator i = shadowMapAllocations_.Begin(); i != shadowMapAllocations_.End(); ++i)
-        i->second_.Clear();
-}
-
-void Renderer::Initialize()
-{
-    Graphics* graphics = GetSubsystem<Graphics>();
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    if (!graphics || !graphics->IsInitialized() || !cache)
-        return;
-    
-    PROFILE(InitRenderer);
-    
-    graphics_ = graphics;
-    cache_ = cache;
-    
-    // Check shader model support
-    #ifndef USE_OPENGL
-    if (graphics_->GetSM3Support())
-    {
-        shaderPath_ = "Shaders/SM3/";
-        vsFormat_ = ".vs3";
-        psFormat_ = ".ps3";
-    }
-    else
-    {
-        shaderPath_ = "Shaders/SM2/";
-        vsFormat_ = ".vs2";
-        psFormat_ = ".ps2";
-    }
-    
-    #else
-    {
-        shaderPath_ = "Shaders/GLSL/";
-        vsFormat_ = ".vert";
-        psFormat_ = ".frag";
-    }
-    #endif
-    
-    defaultLightRamp_ = cache->GetResource<Texture2D>("Textures/Ramp.png");
-    defaultLightSpot_ = cache->GetResource<Texture2D>("Textures/Spot.png");
-    defaultMaterial_ = cache->GetResource<Material>("Materials/Default.xml");
-    
-    CreateGeometries();
-    CreateInstancingBuffer();
-    
-    viewports_.Resize(1);
-    ResetViews();
-    ResetShadowMaps();
-    
-    shadersDirty_ = true;
-    initialized_ = true;
-    
-    LOGINFO("Initialized renderer");
-}
-
-void Renderer::ResetViews()
-{
-    views_.Clear();
-    numViews_ = 0;
-}
-
-void Renderer::LoadShaders()
-{
-    LOGINFO("Reloading shaders");
-    
-    // Release old material shaders, mark them for reload
-    ReleaseMaterialShaders();
-    shadersChangedFrameNumber_ = GetSubsystem<Time>()->GetFrameNumber();
-    
-    // Load inbuilt shaders
-    stencilVS_ = GetVertexShader("Stencil");
-    stencilPS_ = GetPixelShader("Stencil");
-    
-    shadersDirty_ = false;
-}
-
-void Renderer::LoadMaterialShaders(Technique* technique)
-{
-    LoadPassShaders(technique, PASS_BASE);
-    LoadPassShaders(technique, PASS_LITBASE);
-    LoadPassShaders(technique, PASS_LIGHT);
-    LoadPassShaders(technique, PASS_PREALPHA);
-    LoadPassShaders(technique, PASS_POSTALPHA);
-    LoadPassShaders(technique, PASS_SHADOW);
-}
-
-void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowShadows)
-{
-    Pass* pass = technique->GetPass(type);
-    if (!pass)
-        return;
-    
-    String vertexShaderName = pass->GetVertexShaderName();
-    String pixelShaderName = pass->GetPixelShaderName();
-    
-    // Check if the shader name is already a variation in itself
-    if (vertexShaderName.Find('_') == String::NPOS)
-        vertexShaderName += "_";
-    if (pixelShaderName.Find('_') == String::NPOS)
-        pixelShaderName += "_";
-    
-    unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
-    unsigned fallback = graphics_->GetFallback() ? 1 : 0;
-    if (fallback)
-        shadows = SHADOWQUALITY_HIGH_16BIT;
-    
-    if (type == PASS_SHADOW)
-    {
-        vertexShaderName += fallbackVariations[fallback];
-        pixelShaderName += fallbackVariations[fallback];
-    }
-    
-    Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
-    Vector<SharedPtr<ShaderVariation> >& pixelShaders = pass->GetPixelShaders();
-    
-    // Forget all the old shaders
-    vertexShaders.Clear();
-    pixelShaders.Clear();
-    
-    if (type == PASS_LIGHT || type == PASS_LITBASE)
-    {
-        // If ambient pass is transparent, and shadow maps are reused, do not load shadow variations
-        if (reuseShadowMaps_)
-        {
-            if (!technique->HasPass(PASS_BASE) || technique->GetPass(PASS_BASE)->GetBlendMode() != BLEND_REPLACE)
-                allowShadows = false;
-        }
-        
-        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
-        pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS);
-        
-        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
-        {
-            unsigned g = j / MAX_LIGHT_VS_VARIATIONS;
-            unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
-            if (l < LVS_SHADOW || allowShadows)
-                vertexShaders[j] = GetVertexShader(vertexShaderName + lightVSVariations[l] + geometryVSVariations[g], g != 0);
-            else
-                vertexShaders[j].Reset();
-        }
-        for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS; ++j)
-        {
-            if (j & LPS_SHADOW)
-            {
-                if (allowShadows)
-                {
-                    pixelShaders[j] = GetPixelShader(pixelShaderName + lightPSVariations[j] + shadowVariations[shadows] +
-                        fallbackVariations[fallback]);
-                }
-                else
-                    pixelShaders[j].Reset();
-            }
-            else
-                pixelShaders[j] = GetPixelShader(pixelShaderName + lightPSVariations[j]);
-        }
-    }
-    else
-    {
-        if (type == PASS_BASE)
-        {
-            vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
-            {
-                unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
-                unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
-                vertexShaders[j] = GetVertexShader(vertexShaderName + vertexLightVSVariations[l] + geometryVSVariations[g], g != 0 || l != 0);
-            }
-        }
-        else
-        {
-            vertexShaders.Resize(MAX_GEOMETRYTYPES);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
-                vertexShaders[j] = GetVertexShader(vertexShaderName + geometryVSVariations[j], j != 0);
-        }
-        
-        pixelShaders.Resize(1);
-        pixelShaders[0] = GetPixelShader(pixelShaderName);
-    }
-    
-    technique->MarkShadersLoaded(shadersChangedFrameNumber_);
-}
-
-void Renderer::ReleaseMaterialShaders()
-{
-    PODVector<Material*> materials;
-    cache_->GetResources<Material>(materials);
-    
-    for (unsigned i = 0; i < materials.Size(); ++i)
-        materials[i]->ReleaseShaders();
-}
-
-void Renderer::ReloadTextures()
-{
-    PODVector<Resource*> textures;
-    
-    cache_->GetResources(textures, Texture2D::GetTypeStatic());
-    for (unsigned i = 0; i < textures.Size(); ++i)
-        cache_->ReloadResource(textures[i]);
-    
-    cache_->GetResources(textures, TextureCube::GetTypeStatic());
-    for (unsigned i = 0; i < textures.Size(); ++i)
-        cache_->ReloadResource(textures[i]);
-}
-
-void Renderer::CreateGeometries()
-{
-    SharedPtr<VertexBuffer> plvb(new VertexBuffer(context_));
-    plvb->SetSize(24, MASK_POSITION);
-    plvb->SetData(pointLightVertexData);
-    
-    SharedPtr<IndexBuffer> plib(new IndexBuffer(context_));
-    plib->SetSize(132, false);
-    plib->SetData(pointLightIndexData);
-    
-    pointLightGeometry_ = new Geometry(context_);
-    pointLightGeometry_->SetVertexBuffer(0, plvb);
-    pointLightGeometry_->SetIndexBuffer(plib);
-    pointLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, plib->GetIndexCount());
-    
-    SharedPtr<VertexBuffer> slvb(new VertexBuffer(context_));
-    slvb->SetSize(8, MASK_POSITION);
-    slvb->SetData(spotLightVertexData);
-    
-    SharedPtr<IndexBuffer> slib(new IndexBuffer(context_));
-    slib->SetSize(36, false);
-    slib->SetData(spotLightIndexData);
-    
-    spotLightGeometry_ = new Geometry(context_);
-    spotLightGeometry_->SetVertexBuffer(0, slvb);
-    spotLightGeometry_->SetIndexBuffer(slib);
-    spotLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, slib->GetIndexCount());
-    
-    faceSelectCubeMap_ = new TextureCube(context_);
-    faceSelectCubeMap_->SetNumLevels(1);
-    faceSelectCubeMap_->SetSize(1, graphics_->GetRGBAFormat());
-    faceSelectCubeMap_->SetFilterMode(FILTER_NEAREST);
-    
-    unsigned char data[256 * 256 * 4];
-    
-    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
-    {
-        unsigned axis = i / 2;
-        data[0] = (axis == 0) ? 255 : 0;
-        data[1] = (axis == 1) ? 255 : 0;
-        data[2] = (axis == 2) ? 255 : 0;
-        data[3] = 0;
-        faceSelectCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 1, 1, data);
-    }
-    
-    indirectionCubeMap_ = new TextureCube(context_);
-    indirectionCubeMap_->SetNumLevels(1);
-    indirectionCubeMap_->SetSize(256, graphics_->GetRGBAFormat());
-    indirectionCubeMap_->SetFilterMode(FILTER_BILINEAR);
-    indirectionCubeMap_->SetAddressMode(COORD_U, ADDRESS_CLAMP);
-    indirectionCubeMap_->SetAddressMode(COORD_V, ADDRESS_CLAMP);
-    indirectionCubeMap_->SetAddressMode(COORD_W, ADDRESS_CLAMP);
-    
-    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
-    {
-        unsigned char faceX = (i & 1) * 255;
-        unsigned char faceY = (i / 2) * 255 / 3;
-        unsigned char* dest = data;
-        for (unsigned y = 0; y < 256; ++y)
-        {
-            for (unsigned x = 0; x < 256; ++x)
-            {
-                #ifdef USE_OPENGL
-                *dest++ = x;
-                *dest++ = 255 - y;
-                *dest++ = faceX;
-                *dest++ = 255 * 2 / 3 - faceY;
-                #else
-                *dest++ = x;
-                *dest++ = y;
-                *dest++ = faceX;
-                *dest++ = faceY;
-                #endif
-            }
-        }
-        
-        indirectionCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 256, 256, data);
-    }
-}
-
-void Renderer::CreateInstancingBuffer()
-{
-    // Do not create buffer if instancing not supported
-    if (!graphics_->GetSM3Support())
-    {
-        instancingBuffer_.Reset();
-        dynamicInstancing_ = false;
-        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))
-    {
-        instancingBuffer_.Reset();
-        dynamicInstancing_ = false;
-    }
-}
-
-void Renderer::ResetShadowMaps()
-{
-    shadowMaps_.Clear();
-    colorShadowMaps_.Clear();
-    shadowDepthStencil_.Reset();
-}
-
-void Renderer::HandleScreenMode(StringHash eventType, VariantMap& eventData)
-{
-    if (!initialized_)
-        Initialize();
-    else
-    {
-        // When screen mode changes, purge old views
-        ResetViews();
-    }
-}
-
-void Renderer::HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData)
-{
-    // Reinitialize if already initialized
-    if (initialized_)
-        Initialize();
-}
-
-void Renderer::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
-{
-    if (initialized_)
-    {
-        using namespace RenderUpdate;
-        
-        Update(eventData[P_TIMESTEP].GetFloat());
-    }
-}
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse ��rni
+//
+// 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 "Precompiled.h"
+#include "Camera.h"
+#include "CoreEvents.h"
+#include "DebugRenderer.h"
+#include "Geometry.h"
+#include "Graphics.h"
+#include "GraphicsEvents.h"
+#include "GraphicsImpl.h"
+#include "IndexBuffer.h"
+#include "Light.h"
+#include "Log.h"
+#include "Material.h"
+#include "OcclusionBuffer.h"
+#include "Octree.h"
+#include "Profiler.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+#include "Scene.h"
+#include "Shader.h"
+#include "ShaderVariation.h"
+#include "Technique.h"
+#include "Texture2D.h"
+#include "TextureCube.h"
+#include "VertexBuffer.h"
+#include "View.h"
+#include "XMLFile.h"
+#include "Zone.h"
+
+#include "DebugNew.h"
+
+static const float pointLightVertexData[] =
+{
+    -0.423169f, -1.000000f, 0.423169f,
+    -0.423169f, -1.000000f, -0.423169f,
+    0.423169f, -1.000000f, -0.423169f,
+    0.423169f, -1.000000f, 0.423169f,
+    0.423169f, 1.000000f, -0.423169f,
+    -0.423169f, 1.000000f, -0.423169f,
+    -0.423169f, 1.000000f, 0.423169f,
+    0.423169f, 1.000000f, 0.423169f,
+    -1.000000f, 0.423169f, -0.423169f,
+    -1.000000f, -0.423169f, -0.423169f,
+    -1.000000f, -0.423169f, 0.423169f,
+    -1.000000f, 0.423169f, 0.423169f,
+    0.423169f, 0.423169f, -1.000000f,
+    0.423169f, -0.423169f, -1.000000f,
+    -0.423169f, -0.423169f, -1.000000f,
+    -0.423169f, 0.423169f, -1.000000f,
+    1.000000f, 0.423169f, 0.423169f,
+    1.000000f, -0.423169f, 0.423169f,
+    1.000000f, -0.423169f, -0.423169f,
+    1.000000f, 0.423169f, -0.423169f,
+    0.423169f, -0.423169f, 1.000000f,
+    0.423169f, 0.423169f, 1.000000f,
+    -0.423169f, 0.423169f, 1.000000f,
+    -0.423169f, -0.423169f, 1.000000f
+};
+
+static const unsigned short pointLightIndexData[] =
+{
+    0, 1, 2,
+    0, 2, 3,
+    4, 5, 6,
+    4, 6, 7,
+    8, 9, 10,
+    8, 10, 11,
+    12, 13, 14,
+    12, 14, 15,
+    16, 17, 18,
+    16, 18, 19,
+    20, 21, 22,
+    20, 22, 23,
+    0, 10, 9,
+    0, 9, 1,
+    13, 2, 1,
+    13, 1, 14,
+    23, 0, 3,
+    23, 3, 20,
+    17, 3, 2,
+    17, 2, 18,
+    21, 7, 6,
+    21, 6, 22,
+    7, 16, 19,
+    7, 19, 4,
+    5, 8, 11,
+    5, 11, 6,
+    4, 12, 15,
+    4, 15, 5,
+    22, 11, 10,
+    22, 10, 23,
+    8, 15, 14,
+    8, 14, 9,
+    12, 19, 18,
+    12, 18, 13,
+    16, 21, 20,
+    16, 20, 17,
+    0, 23, 10,
+    1, 9, 14,
+    2, 13, 18,
+    3, 17, 20,
+    6, 11, 22,
+    5, 15, 8,
+    4, 19, 12,
+    7, 21, 16
+};
+
+static const float spotLightVertexData[] =
+{
+    0.001f, 0.001f, 0.001f,
+    0.001f, -0.001f, 0.001f,
+    -0.001f, -0.001f, 0.001f,
+    -0.001f, 0.001f, 0.001f,
+    1.0f, 1.0f, 1.0f,
+    1.0f, -1.0f, 1.0f,
+    -1.0f,  -1.0f, 1.0f,
+    -1.0f, 1.0f, 1.0f,
+};
+
+static const unsigned short spotLightIndexData[] =
+{
+    3, 0, 1,
+    3, 1, 2,
+    0, 4, 5,
+    0, 5, 1,
+    3, 7, 4,
+    3, 4, 0,
+    7, 3, 2,
+    7, 2, 6,
+    6, 2, 1,
+    6, 1, 5,
+    7, 5, 4,
+    7, 6, 5
+};
+
+static const String shadowVariations[] =
+{
+    // No specific hardware shadow compare variation on OpenGL, it is always supported
+    #ifdef USE_OPENGL
+    "LQ",
+    "LQ",
+    "",
+    ""
+    #else
+    "",
+    "LQHW",
+    "",
+    "HW"
+    #endif
+};
+
+static const String fallbackVariations[] =
+{
+    "",
+    "FB"
+};
+
+static const String geometryVSVariations[] =
+{
+    "",
+    "Skinned",
+    "Instanced",
+    "Billboard"
+};
+
+static const String lightVSVariations[] =
+{
+    "Dir",
+    "Spot",
+    "Point",
+    "DirSpec",
+    "SpotSpec",
+    "PointSpec",
+    "DirShadow",
+    "SpotShadow",
+    "PointShadow",
+    "DirSpecShadow",
+    "SpotSpecShadow",
+    "PointSpecShadow"
+};
+
+static const String vertexLightVSVariations[] =
+{
+    "",
+    "1VL",
+    "2VL",
+    "3VL",
+    "4VL",
+    "5VL",
+    "6VL"
+};
+
+static const String lightPSVariations[] = 
+{
+    "Dir",
+    "Spot",
+    "Point",
+    "PointMask",
+    "DirSpec",
+    "SpotSpec",
+    "PointSpec",
+    "PointMaskSpec",
+    "DirShadow",
+    "SpotShadow",
+    "PointShadow",
+    "PointMaskShadow",
+    "DirSpecShadow",
+    "SpotSpecShadow",
+    "PointSpecShadow",
+    "PointMaskSpecShadow"
+};
+
+static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3;
+static const Viewport noViewport;
+
+OBJECTTYPESTATIC(Renderer);
+
+Renderer::Renderer(Context* context) :
+    Object(context),
+    defaultZone_(new Zone(context)),
+    numViews_(0),
+    numShadowCameras_(0),
+    numOcclusionBuffers_(0),
+    textureAnisotropy_(4),
+    textureFilterMode_(FILTER_TRILINEAR),
+    textureQuality_(QUALITY_HIGH),
+    materialQuality_(QUALITY_HIGH),
+    shadowMapSize_(1024),
+    shadowQuality_(SHADOWQUALITY_HIGH_16BIT),
+    maxShadowMaps_(1),
+    maxShadowCascades_(MAX_CASCADE_SPLITS),
+    maxInstanceTriangles_(500),
+    maxOccluderTriangles_(5000),
+    occlusionBufferSize_(256),
+    occluderSizeThreshold_(0.1f),
+    shadersChangedFrameNumber_(M_MAX_UNSIGNED),
+    specularLighting_(true),
+    drawShadows_(true),
+    reuseShadowMaps_(true),
+    lightStencilMasking_(true),
+    dynamicInstancing_(true),
+    shadersDirty_(true),
+    initialized_(false)
+{
+    SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
+    SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
+    SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate));
+    
+    // Try to initialize right now, but skip if screen mode is not yet set
+    Initialize();
+}
+
+Renderer::~Renderer()
+{
+}
+
+void Renderer::SetNumViewports(unsigned num)
+{
+    viewports_.Resize(num);
+}
+
+void Renderer::SetViewport(unsigned index, const Viewport& viewport)
+{
+    if (index >= viewports_.Size())
+    {
+        LOGERROR("Viewport index out of bounds");
+        return;
+    }
+    
+    viewports_[index] = viewport;
+}
+
+void Renderer::SetSpecularLighting(bool enable)
+{
+    specularLighting_ = enable;
+}
+
+void Renderer::SetTextureAnisotropy(int level)
+{
+    textureAnisotropy_ = Max(level, 1);
+}
+
+void Renderer::SetTextureFilterMode(TextureFilterMode mode)
+{
+    textureFilterMode_ = mode;
+}
+
+void Renderer::SetTextureQuality(int quality)
+{
+    quality = Clamp(quality, QUALITY_LOW, QUALITY_HIGH);
+    
+    if (quality != textureQuality_)
+    {
+        textureQuality_ = quality;
+        ReloadTextures();
+    }
+}
+
+void Renderer::SetMaterialQuality(int quality)
+{
+    materialQuality_ = Clamp(quality, QUALITY_LOW, QUALITY_MAX);
+    shadersDirty_ = true;
+    ResetViews();
+}
+
+void Renderer::SetDrawShadows(bool enable)
+{
+    if (!graphics_)
+        return;
+    
+    drawShadows_ = enable;
+    if (!drawShadows_)
+        ResetShadowMaps();
+}
+
+void Renderer::SetShadowMapSize(int size)
+{
+    if (!graphics_)
+        return;
+    
+    size = NextPowerOfTwo(Max(size, SHADOW_MIN_PIXELS));
+    if (size != shadowMapSize_)
+    {
+        shadowMapSize_ = size;
+        ResetShadowMaps();
+    }
+}
+
+void Renderer::SetShadowQuality(int quality)
+{
+    if (!graphics_)
+        return;
+    
+    quality &= SHADOWQUALITY_HIGH_24BIT;
+    
+    // If no hardware PCF, do not allow to select one-sample quality
+    if (!graphics_->GetHardwareShadowSupport())
+        quality |= SHADOWQUALITY_HIGH_16BIT;
+    if (!graphics_->GetHiresShadowSupport())
+        quality &= SHADOWQUALITY_HIGH_16BIT;
+    if (graphics_->GetFallback())
+        quality = SHADOWQUALITY_LOW_16BIT;
+    
+    if (quality != shadowQuality_)
+    {
+        shadowQuality_ = quality;
+        shadersDirty_ = true;
+        ResetShadowMaps();
+    }
+}
+
+void Renderer::SetReuseShadowMaps(bool enable)
+{
+    if (enable == reuseShadowMaps_)
+        return;
+    
+    reuseShadowMaps_ = enable;
+}
+
+void Renderer::SetMaxShadowMaps(int shadowMaps)
+{
+    if (shadowMaps < 1)
+        return;
+    
+    maxShadowMaps_ = shadowMaps;
+    for (HashMap<int, Vector<SharedPtr<Texture2D> > >::Iterator i = shadowMaps_.Begin(); i != shadowMaps_.End(); ++i)
+    {
+        if ((int)i->second_.Size() > maxShadowMaps_)
+            i->second_.Resize(maxShadowMaps_);
+    }
+}
+
+void Renderer::SetMaxShadowCascades(int cascades)
+{
+    cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
+    
+    if (cascades != maxShadowCascades_)
+    {
+        maxShadowCascades_ = cascades;
+        ResetShadowMaps();
+    }
+}
+
+void Renderer::SetLightStencilMasking(bool enable)
+{
+    lightStencilMasking_ = enable;
+}
+
+void Renderer::SetDynamicInstancing(bool enable)
+{
+    if (!instancingBuffer_)
+        enable = false;
+    
+    dynamicInstancing_ = enable;
+}
+
+void Renderer::SetMaxInstanceTriangles(int triangles)
+{
+    maxInstanceTriangles_ = Max(triangles, 0);
+}
+
+void Renderer::SetMaxOccluderTriangles(int triangles)
+{
+    maxOccluderTriangles_ = Max(triangles, 0);
+}
+
+void Renderer::SetOcclusionBufferSize(int size)
+{
+    occlusionBufferSize_ = Max(size, 1);
+    occlusionBuffers_.Clear();
+}
+
+void Renderer::SetOccluderSizeThreshold(float screenSize)
+{
+    occluderSizeThreshold_ = Max(screenSize, 0.0f);
+}
+
+const Viewport& Renderer::GetViewport(unsigned index) const
+{
+    return index < viewports_.Size() ? viewports_[index] : noViewport;
+}
+
+ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const
+{
+    return GetShader(name, vsFormat_, checkExists);
+}
+
+ShaderVariation* Renderer::GetPixelShader(const String& name, bool checkExists) const
+{
+    return GetShader(name, psFormat_, checkExists);
+}
+
+unsigned Renderer::GetNumGeometries(bool allViews) const
+{
+    unsigned numGeometries = 0;
+    unsigned lastView = allViews ? numViews_ : 1;
+    
+    for (unsigned i = 0; i < lastView; ++i)
+        numGeometries += views_[i]->GetGeometries().Size();
+    
+    return numGeometries;
+}
+
+unsigned Renderer::GetNumLights(bool allViews) const
+{
+    unsigned numLights = 0;
+    unsigned lastView = allViews ? numViews_ : 1;
+    
+    for (unsigned i = 0; i < lastView; ++i)
+        numLights += views_[i]->GetLights().Size();
+    
+    return numLights;
+}
+
+unsigned Renderer::GetNumShadowMaps(bool allViews) const
+{
+    unsigned numShadowMaps = 0;
+    unsigned lastView = allViews ? numViews_ : 1;
+    
+    for (unsigned i = 0; i < lastView; ++i)
+    {
+        const List<LightBatchQueue>& lightQueues = views_[i]->GetLightQueues();
+        
+        for (List<LightBatchQueue>::ConstIterator i = lightQueues.Begin(); i != lightQueues.End(); ++i)
+        {
+            if (i->shadowMap_)
+                ++numShadowMaps;
+        }
+    }
+    
+    return numShadowMaps;
+}
+
+unsigned Renderer::GetNumOccluders(bool allViews) const
+{
+    unsigned numOccluders = 0;
+    unsigned lastView = allViews ? numViews_ : 1;
+    
+    for (unsigned i = 0; i < lastView; ++i)
+        numOccluders += views_[i]->GetOccluders().Size();
+    
+    return numOccluders;
+}
+
+void Renderer::Update(float timeStep)
+{
+    PROFILE(UpdateViews);
+    
+    numViews_ = 0;
+    
+    // If device lost, do not perform update. This is because any dynamic vertex/index buffer updates happen already here,
+    // and if the device is lost, the updates queue up, causing memory use to rise constantly
+    if (!graphics_ || !graphics_->IsInitialized() || graphics_->IsDeviceLost())
+        return;
+    
+    // Advance frame number & time, set up the frameinfo structure, and reset views & stats
+    frame_.frameNumber_ = GetSubsystem<Time>()->GetFrameNumber();
+    frame_.timeStep_ = timeStep;
+    frame_.camera_ = 0;
+    numShadowCameras_ = 0;
+    numOcclusionBuffers_ = 0;
+    updateOctrees_.Clear();
+    
+    // Reload shaders if needed
+    if (shadersDirty_)
+        LoadShaders();
+    
+    // Process all viewports. Use reverse order, because during rendering the order will be reversed again to handle auxiliary
+    // view dependencies correctly
+    for (unsigned i = viewports_.Size() - 1; i < viewports_.Size(); --i)
+    {
+        unsigned mainView = numViews_;
+        Viewport& viewport = viewports_[i];
+        if (!AddView(0, viewport))
+            continue;
+        
+        // Update octree (perform early update for nodes which need that, and reinsert moved nodes.)
+        // However, if the same scene is viewed from multiple cameras, update the octree only once
+        Octree* octree = viewport.scene_->GetComponent<Octree>();
+        DebugRenderer* debug = viewport.scene_->GetComponent<DebugRenderer>();
+        if (!updateOctrees_.Contains(octree))
+        {
+            frame_.camera_ = viewport.camera_;
+            frame_.viewSize_ = IntVector2(viewport.rect_.right_ - viewport.rect_.left_, viewport.rect_.bottom_ - viewport.rect_.top_);
+            if (frame_.viewSize_ == IntVector2::ZERO)
+                frame_.viewSize_ = IntVector2(graphics_->GetWidth(), graphics_->GetHeight());
+            octree->Update(frame_);
+            updateOctrees_.Insert(octree);
+            
+            // Set also the view for the debug graphics already here, so that it can use culling
+            /// \todo May result in incorrect debug geometry culling if the same scene is drawn from multiple viewports
+            if (debug)
+                debug->SetView(viewport.camera_);
+        }
+        
+        // Update the viewport's main view and any auxiliary views it creates
+        for (unsigned i = mainView; i < numViews_; ++i)
+            views_[i]->Update(frame_);
+    }
+    
+    return;
+}
+
+void Renderer::Render()
+{
+    if (!graphics_)
+        return;
+    
+    PROFILE(RenderViews);
+    
+    graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
+    graphics_->SetTextureAnisotropy(textureAnisotropy_);
+    
+    // If no views, just clear the screen
+    if (!numViews_)
+    {
+        numPrimitives_ = 0;
+        numBatches_ = 0;
+        graphics_->SetAlphaTest(false);
+        graphics_->SetBlendMode(BLEND_REPLACE);
+        graphics_->SetColorWrite(true);
+        graphics_->SetDepthWrite(true);
+        graphics_->SetFillMode(FILL_SOLID);
+        graphics_->SetScissorTest(false);
+        graphics_->SetStencilTest(false);
+        graphics_->Clear(CLEAR_COLOR | CLEAR_DEPTH | CLEAR_STENCIL);
+        return;
+    }
+    
+    // Render views from last to first (each main view is rendered after the auxiliary views it depends on)
+    for (unsigned i = numViews_ - 1; i < numViews_; --i)
+        views_[i]->Render();
+    
+    // Copy the number of batches & primitives from Graphics so that we can account for 3D geometry only
+    numPrimitives_ = graphics_->GetNumPrimitives();
+    numBatches_ = graphics_->GetNumBatches();
+}
+
+void Renderer::DrawDebugGeometry(bool depthTest)
+{
+    PROFILE(RendererDrawDebug);
+    
+    /// \todo Because debug geometry is per-scene, if two cameras show views of the same area, occlusion is not shown correctly
+    HashSet<Drawable*> processedGeometries;
+    HashSet<Light*> processedLights;
+    
+    for (unsigned i = 0; i < numViews_; ++i)
+    {
+        // Make sure it's a main view, and process each node only once
+        View* view = views_[i];
+        if (view->GetRenderTarget())
+            continue;
+        Octree* octree = view->GetOctree();
+        if (!octree)
+            continue;
+        Scene* scene = static_cast<Scene*>(octree->GetNode());
+        if (!scene)
+            continue;
+        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
+        if (!debug)
+            continue;
+        
+        const PODVector<Drawable*>& geometries = view->GetGeometries();
+        const PODVector<Light*>& lights = view->GetLights();
+        
+        for (unsigned i = 0; i < geometries.Size(); ++i)
+        {
+            if (!processedGeometries.Contains(geometries[i]))
+            {
+                geometries[i]->DrawDebugGeometry(debug, depthTest);
+                processedGeometries.Insert(geometries[i]);
+            }
+        }
+        for (unsigned i = 0; i < lights.Size(); ++i)
+        {
+            if (!processedLights.Contains(lights[i]))
+            {
+                lights[i]->DrawDebugGeometry(debug, depthTest);
+                processedLights.Insert(lights[i]);
+            }
+        }
+    }
+}
+
+bool Renderer::AddView(RenderSurface* renderTarget, const Viewport& viewport)
+{
+    // If using a render target texture, make sure it will not be rendered to multiple times
+    if (renderTarget)
+    {
+        for (unsigned i = 0; i < numViews_; ++i)
+        {
+            if (views_[i]->GetRenderTarget() == renderTarget)
+                return false;
+        }
+    }
+    
+    if (views_.Size() <= numViews_)
+        views_.Resize(numViews_ + 1);
+    if (!views_[numViews_])
+        views_[numViews_] = new View(context_);
+    
+    if (views_[numViews_]->Define(renderTarget, viewport))
+    {
+        ++numViews_;
+        return true;
+    }
+    else
+        return false;
+}
+
+Geometry* Renderer::GetLightGeometry(Light* light)
+{
+    LightType type = light->GetLightType();
+    if (type == LIGHT_SPOT)
+        return spotLightGeometry_;
+    else if (type == LIGHT_POINT)
+        return pointLightGeometry_;
+    else
+        return 0;
+}
+
+Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight)
+{
+    LightType type = light->GetLightType();
+    const FocusParameters& parameters = light->GetShadowFocus();
+    float size = (float)shadowMapSize_ * light->GetShadowResolution();
+    // Automatically reduce shadow map size when far away
+    if (parameters.autoSize_ && type != LIGHT_DIRECTIONAL)
+    {
+        Matrix3x4 view(camera->GetInverseWorldTransform());
+        Matrix4 projection(camera->GetProjection());
+        BoundingBox lightBox;
+        float lightPixels;
+        
+        if (type == LIGHT_POINT)
+        {
+            // Calculate point light pixel size from the projection of its diagonal
+            Vector3 center = view * light->GetWorldPosition();
+            float extent = 0.58f * light->GetRange();
+            lightBox.Define(center + Vector3(extent, extent, extent), center - Vector3(extent, extent, extent));
+        }
+        else
+        {
+            // Calculate spot light pixel size from the projection of its frustum far vertices
+            Frustum lightFrustum = light->GetFrustum().Transformed(view);
+            lightBox.Define(&lightFrustum.vertices_[4], 4);
+        }
+        
+        Vector2 projectionSize = lightBox.Projected(projection).Size();
+        lightPixels = Max(0.5f * (float)viewWidth * projectionSize.x_, 0.5f * (float)viewHeight * projectionSize.y_);
+        
+        // Clamp pixel amount to a sufficient minimum to avoid self-shadowing artifacts due to loss of precision
+        if (lightPixels < SHADOW_MIN_PIXELS)
+            lightPixels = SHADOW_MIN_PIXELS;
+        
+        size = Min(size, lightPixels);
+    }
+    
+    /// \todo Allow to specify maximum shadow maps per resolution, as smaller shadow maps take less memory
+    int width = NextPowerOfTwo((unsigned)size);
+    int height = width;
+    
+    // Adjust the size for directional or point light shadow map atlases
+    if (type == LIGHT_DIRECTIONAL)
+    {
+        if (maxShadowCascades_ > 1)
+            width *= 2;
+        if (maxShadowCascades_ > 2)
+            height *= 2;
+    }
+    else if (type == LIGHT_POINT)
+    {
+        width *= 2;
+        height *= 3;
+    }
+    
+    int searchKey = (width << 16) | height;
+    if (shadowMaps_.Contains(searchKey))
+    {
+        // If shadow maps are reused, always return the first
+        if (reuseShadowMaps_)
+            return shadowMaps_[searchKey][0];
+        else
+        {
+            // If not reused, check allocation count and return existing shadow map if possible
+            unsigned allocated = shadowMapAllocations_[searchKey].Size();
+            if (allocated < shadowMaps_[searchKey].Size())
+            {
+                shadowMapAllocations_[searchKey].Push(light);
+                return shadowMaps_[searchKey][allocated];
+            }
+            else if ((int)allocated >= maxShadowMaps_)
+                return 0;
+        }
+    }
+    
+    unsigned shadowMapFormat = (shadowQuality_ & SHADOWQUALITY_LOW_24BIT) ? graphics_->GetHiresShadowMapFormat() :
+        graphics_->GetShadowMapFormat();
+    unsigned dummyColorFormat = graphics_->GetDummyColorFormat();
+    if (!shadowMapFormat)
+        return 0;
+    
+    SharedPtr<Texture2D> newShadowMap(new Texture2D(context_));
+    int retries = 3;
+    
+    #ifdef USE_OPENGL
+    // Create shadow map only. Color rendertarget is not needed
+    while (retries)
+    {
+        if (!newShadowMap->SetSize(width, height, shadowMapFormat, TEXTURE_DEPTHSTENCIL))
+        {
+            width >>= 1;
+            height >>= 1;
+            --retries;
+        }
+        else
+        {
+            newShadowMap->SetFilterMode(FILTER_BILINEAR);
+            newShadowMap->SetShadowCompare(true);
+            break;
+        }
+    }
+    #else
+    // Create shadow map and dummy color rendertarget
+    bool fallback = graphics_->GetFallback();
+    while (retries)
+    {
+        if (!newShadowMap->SetSize(width, height, shadowMapFormat, fallback ? TEXTURE_RENDERTARGET : TEXTURE_DEPTHSTENCIL))
+        {
+            width >>= 1;
+            height >>= 1;
+            --retries;
+        }
+        else
+        {
+            newShadowMap->SetFilterMode(FILTER_BILINEAR);
+            if (!fallback)
+            {
+                // If no dummy color rendertarget for this size exists yet, create one now
+                if (!colorShadowMaps_.Contains(searchKey))
+                {
+                    colorShadowMaps_[searchKey] = new Texture2D(context_);
+                    colorShadowMaps_[searchKey]->SetSize(width, height, dummyColorFormat, TEXTURE_RENDERTARGET);
+                }
+                // Link the color rendertarget to the shadow map
+                newShadowMap->GetRenderSurface()->SetLinkedRenderTarget(colorShadowMaps_[searchKey]->GetRenderSurface());
+            }
+            else
+            {
+                // In fallback mode link the shared shadow map depth stencil to the shadow map instead.
+                // Create it first if not created yet, and resize larger if necessary
+                if (!shadowDepthStencil_)
+                    shadowDepthStencil_ = new Texture2D(context_);
+                if (shadowDepthStencil_->GetWidth() < width || shadowDepthStencil_->GetHeight() < height)
+                    shadowDepthStencil_->SetSize(width, height, D3DFMT_D16, TEXTURE_DEPTHSTENCIL);
+                newShadowMap->GetRenderSurface()->SetLinkedDepthBuffer(shadowDepthStencil_->GetRenderSurface());
+            }
+            break;
+        }
+    }
+    #endif
+    // If failed to set size, store a null pointer so that we will not retry
+    if (!retries)
+        newShadowMap.Reset();
+    
+    shadowMaps_[searchKey].Push(newShadowMap);
+    if (!reuseShadowMaps_)
+        shadowMapAllocations_[searchKey].Push(light);
+    
+    return newShadowMap;
+}
+
+OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
+{
+    if (numOcclusionBuffers_ >= occlusionBuffers_.Size())
+    {
+        SharedPtr<OcclusionBuffer> newBuffer(new OcclusionBuffer(context_));
+        occlusionBuffers_.Push(newBuffer);
+    }
+    
+    int width = occlusionBufferSize_;
+    int height = (int)((float)occlusionBufferSize_ / camera->GetAspectRatio() + 0.5f);
+    
+    OcclusionBuffer* buffer = occlusionBuffers_[numOcclusionBuffers_];
+    buffer->SetSize(width, height);
+    buffer->SetView(camera);
+    
+    ++numOcclusionBuffers_;
+    return buffer;
+}
+
+Camera* Renderer::GetShadowCamera()
+{
+    MutexLock lock(rendererMutex_);
+    
+    if (numShadowCameras_ >= shadowCameraNodes_.Size())
+    {
+        SharedPtr<Node> newNode(new Node(context_));
+        newNode->CreateComponent<Camera>();
+        shadowCameraNodes_.Push(newNode);
+    }
+    
+    Camera* camera = shadowCameraNodes_[numShadowCameras_]->GetComponent<Camera>();
+    camera->SetOrthographic(false);
+    camera->SetZoom(1.0f);
+    
+    ++numShadowCameras_;
+    return camera;
+}
+
+ShaderVariation* Renderer::GetShader(const String& name, const String& extension, bool checkExists) const
+{
+    String shaderName = shaderPath_;
+    String variationName;
+    
+    unsigned split = name.Find('_');
+    if (split != String::NPOS)
+    {
+        shaderName += name.Substring(0, split) + extension;
+        variationName = name.Substring(split + 1);
+    }
+    else
+        shaderName += name + extension;
+    
+    if (checkExists)
+    {
+        if (!cache_->Exists(shaderName))
+            return 0;
+    }
+    
+    Shader* shader = cache_->GetResource<Shader>(shaderName);
+    if (shader)
+        return shader->GetVariation(variationName);
+    else
+        return 0;
+}
+
+void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows)
+{
+    // Check if shaders are unloaded or need reloading
+    Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
+    Vector<SharedPtr<ShaderVariation> >& pixelShaders = pass->GetPixelShaders();
+    if (!vertexShaders.Size() || !pixelShaders.Size() || technique->GetShadersLoadedFrameNumber() !=
+        shadersChangedFrameNumber_)
+    {
+        // First release all previous shaders, then load
+        technique->ReleaseShaders();
+        LoadMaterialShaders(technique);
+    }
+    
+    // Make sure shaders are loaded now
+    if (vertexShaders.Size() && pixelShaders.Size())
+    {
+        GeometryType geomType = batch.geometryType_;
+        // If instancing is not supported, but was requested, or the object is too large to be instanced,
+        // choose static geometry vertex shader instead
+        if (geomType == GEOM_INSTANCED && (!GetDynamicInstancing() || batch.geometry_->GetIndexCount() >
+            (unsigned)maxInstanceTriangles_ * 3))
+            geomType = GEOM_STATIC;
+        
+        //  Check whether is a lit pass. If not, there is only one pixel shader
+        PassType type = pass->GetType();
+        if (type == PASS_LIGHT || type == PASS_LITBASE)
+        {
+            LightBatchQueue* lightQueue = batch.lightQueue_;
+            if (!lightQueue)
+            {
+                // Do not log error, as it would result in a lot of spam
+                batch.vertexShader_ = 0;
+                batch.pixelShader_ = 0;
+                return;
+            }
+            
+            Light* light = lightQueue->light_;
+            unsigned vsi = 0;
+            unsigned psi = 0;
+            vsi = geomType * MAX_LIGHT_VS_VARIATIONS;
+            
+            bool materialHasSpecular = batch.material_ ? batch.material_->GetSpecular() : true;
+            if (specularLighting_ && light->GetSpecularIntensity() > 0.0f && materialHasSpecular)
+            {
+                vsi += LVS_SPEC;
+                psi += LPS_SPEC;
+            }
+            if (allowShadows && lightQueue->shadowMap_)
+            {
+                vsi += LVS_SHADOW;
+                psi += LPS_SHADOW;
+            }
+            
+            switch (light->GetLightType())
+            {
+            case LIGHT_DIRECTIONAL:
+                vsi += LVS_DIR;
+                break;
+                
+            case LIGHT_POINT:
+                if (light->GetShapeTexture())
+                    psi += LPS_POINTMASK;
+                else
+                    psi += LPS_POINT;
+                vsi += LVS_POINT;
+                break;
+                
+            case LIGHT_SPOT:
+                psi += LPS_SPOT;
+                vsi += LVS_SPOT;
+                break;
+            }
+            
+            batch.vertexShader_ = vertexShaders[vsi];
+            batch.pixelShader_ = pixelShaders[psi];
+            
+            // If shadow or specular variations do not exist, try without them
+            if ((!batch.vertexShader_ || !batch.pixelShader_) && (vsi >= LVS_SHADOW))
+            {
+                vsi -= LVS_SHADOW;
+                psi -= LPS_SHADOW;
+                batch.vertexShader_ = vertexShaders[vsi];
+                batch.pixelShader_ = pixelShaders[psi];
+            }
+            if ((!batch.vertexShader_ || !batch.pixelShader_) && (vsi >= LVS_SPEC))
+            {
+                vsi -= LVS_SPEC;
+                psi -= LPS_SPEC;
+                batch.vertexShader_ = vertexShaders[vsi];
+                batch.pixelShader_ = pixelShaders[psi];
+            }
+        }
+        else
+        {
+            if (type == PASS_BASE)
+            {
+                unsigned numVertexLights = 0;
+                if (batch.lightQueue_)
+                    numVertexLights = batch.lightQueue_->vertexLights_.Size();
+                
+                unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS + numVertexLights;
+                batch.vertexShader_ = vertexShaders[vsi];
+                // If vertex lights variations do not exist, try without them
+                if (!batch.vertexShader_)
+                {
+                    unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS;
+                    batch.vertexShader_ = vertexShaders[vsi];
+                }
+            }
+            else
+            {
+                unsigned vsi = geomType;
+                batch.vertexShader_ = vertexShaders[vsi];
+            }
+            
+            batch.pixelShader_ = pixelShaders[0];
+        }
+    }
+    
+    // Log error if shaders could not be assigned, but only once per technique
+    if (!batch.vertexShader_ || !batch.pixelShader_)
+    {
+        if (!shaderErrorDisplayed_.Contains(technique))
+        {
+            shaderErrorDisplayed_.Insert(technique);
+            LOGERROR("Technique " + technique->GetName() + " has missing shaders");
+        }
+    }
+}
+
+bool Renderer::ResizeInstancingBuffer(unsigned numInstances)
+{
+    if (!instancingBuffer_ || !dynamicInstancing_)
+        return false;
+    
+    unsigned oldSize = instancingBuffer_->GetVertexCount();
+    if (numInstances <= oldSize)
+        return true;
+    
+    unsigned newSize = INSTANCING_BUFFER_DEFAULT_SIZE;
+    while (newSize < numInstances)
+        newSize <<= 1;
+    
+    if (!instancingBuffer_->SetSize(newSize, INSTANCING_BUFFER_MASK, true))
+    {
+        LOGERROR("Failed to resize instancing buffer to " + String(newSize));
+        // If failed, try to restore the old size
+        instancingBuffer_->SetSize(oldSize, INSTANCING_BUFFER_MASK, true);
+        return false;
+    }
+    
+    LOGDEBUG("Resized instancing buffer to " + String(newSize));
+    return true;
+}
+
+void Renderer::ResetShadowMapAllocations()
+{
+    for (HashMap<int, PODVector<Light*> >::Iterator i = shadowMapAllocations_.Begin(); i != shadowMapAllocations_.End(); ++i)
+        i->second_.Clear();
+}
+
+void Renderer::Initialize()
+{
+    Graphics* graphics = GetSubsystem<Graphics>();
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    
+    if (!graphics || !graphics->IsInitialized() || !cache)
+        return;
+    
+    PROFILE(InitRenderer);
+    
+    graphics_ = graphics;
+    cache_ = cache;
+    
+    // Check shader model support
+    #ifndef USE_OPENGL
+    if (graphics_->GetSM3Support())
+    {
+        shaderPath_ = "Shaders/SM3/";
+        vsFormat_ = ".vs3";
+        psFormat_ = ".ps3";
+    }
+    else
+    {
+        shaderPath_ = "Shaders/SM2/";
+        vsFormat_ = ".vs2";
+        psFormat_ = ".ps2";
+    }
+    
+    #else
+    {
+        shaderPath_ = "Shaders/GLSL/";
+        vsFormat_ = ".vert";
+        psFormat_ = ".frag";
+    }
+    #endif
+    
+    defaultLightRamp_ = cache->GetResource<Texture2D>("Textures/Ramp.png");
+    defaultLightSpot_ = cache->GetResource<Texture2D>("Textures/Spot.png");
+    defaultMaterial_ = cache->GetResource<Material>("Materials/Default.xml");
+    
+    CreateGeometries();
+    CreateInstancingBuffer();
+    
+    viewports_.Resize(1);
+    ResetViews();
+    ResetShadowMaps();
+    
+    shadersDirty_ = true;
+    initialized_ = true;
+    
+    LOGINFO("Initialized renderer");
+}
+
+void Renderer::ResetViews()
+{
+    views_.Clear();
+    numViews_ = 0;
+}
+
+void Renderer::LoadShaders()
+{
+    LOGINFO("Reloading shaders");
+    
+    // Release old material shaders, mark them for reload
+    ReleaseMaterialShaders();
+    shadersChangedFrameNumber_ = GetSubsystem<Time>()->GetFrameNumber();
+    
+    // Load inbuilt shaders
+    stencilVS_ = GetVertexShader("Stencil");
+    stencilPS_ = GetPixelShader("Stencil");
+    
+    shadersDirty_ = false;
+}
+
+void Renderer::LoadMaterialShaders(Technique* technique)
+{
+    LoadPassShaders(technique, PASS_BASE);
+    LoadPassShaders(technique, PASS_LITBASE);
+    LoadPassShaders(technique, PASS_LIGHT);
+    LoadPassShaders(technique, PASS_PREALPHA);
+    LoadPassShaders(technique, PASS_POSTALPHA);
+    LoadPassShaders(technique, PASS_SHADOW);
+}
+
+void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowShadows)
+{
+    Pass* pass = technique->GetPass(type);
+    if (!pass)
+        return;
+    
+    String vertexShaderName = pass->GetVertexShaderName();
+    String pixelShaderName = pass->GetPixelShaderName();
+    
+    // Check if the shader name is already a variation in itself
+    if (vertexShaderName.Find('_') == String::NPOS)
+        vertexShaderName += "_";
+    if (pixelShaderName.Find('_') == String::NPOS)
+        pixelShaderName += "_";
+    
+    unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
+    unsigned fallback = graphics_->GetFallback() ? 1 : 0;
+    if (fallback)
+        shadows = SHADOWQUALITY_HIGH_16BIT;
+    
+    if (type == PASS_SHADOW)
+    {
+        vertexShaderName += fallbackVariations[fallback];
+        pixelShaderName += fallbackVariations[fallback];
+    }
+    
+    Vector<SharedPtr<ShaderVariation> >& vertexShaders = pass->GetVertexShaders();
+    Vector<SharedPtr<ShaderVariation> >& pixelShaders = pass->GetPixelShaders();
+    
+    // Forget all the old shaders
+    vertexShaders.Clear();
+    pixelShaders.Clear();
+    
+    if (type == PASS_LIGHT || type == PASS_LITBASE)
+    {
+        // If ambient pass is transparent, and shadow maps are reused, do not load shadow variations
+        if (reuseShadowMaps_)
+        {
+            if (!technique->HasPass(PASS_BASE) || technique->GetPass(PASS_BASE)->GetBlendMode() != BLEND_REPLACE)
+                allowShadows = false;
+        }
+        
+        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
+        pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS);
+        
+        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
+        {
+            unsigned g = j / MAX_LIGHT_VS_VARIATIONS;
+            unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
+            if (l < LVS_SHADOW || allowShadows)
+                vertexShaders[j] = GetVertexShader(vertexShaderName + lightVSVariations[l] + geometryVSVariations[g], g != 0);
+            else
+                vertexShaders[j].Reset();
+        }
+        for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS; ++j)
+        {
+            if (j & LPS_SHADOW)
+            {
+                if (allowShadows)
+                {
+                    pixelShaders[j] = GetPixelShader(pixelShaderName + lightPSVariations[j] + shadowVariations[shadows] +
+                        fallbackVariations[fallback]);
+                }
+                else
+                    pixelShaders[j].Reset();
+            }
+            else
+                pixelShaders[j] = GetPixelShader(pixelShaderName + lightPSVariations[j]);
+        }
+    }
+    else
+    {
+        if (type == PASS_BASE)
+        {
+            vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
+            {
+                unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
+                unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
+                vertexShaders[j] = GetVertexShader(vertexShaderName + vertexLightVSVariations[l] + geometryVSVariations[g], g != 0 || l != 0);
+            }
+        }
+        else
+        {
+            vertexShaders.Resize(MAX_GEOMETRYTYPES);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
+                vertexShaders[j] = GetVertexShader(vertexShaderName + geometryVSVariations[j], j != 0);
+        }
+        
+        pixelShaders.Resize(1);
+        pixelShaders[0] = GetPixelShader(pixelShaderName);
+    }
+    
+    technique->MarkShadersLoaded(shadersChangedFrameNumber_);
+}
+
+void Renderer::ReleaseMaterialShaders()
+{
+    PODVector<Material*> materials;
+    cache_->GetResources<Material>(materials);
+    
+    for (unsigned i = 0; i < materials.Size(); ++i)
+        materials[i]->ReleaseShaders();
+}
+
+void Renderer::ReloadTextures()
+{
+    PODVector<Resource*> textures;
+    
+    cache_->GetResources(textures, Texture2D::GetTypeStatic());
+    for (unsigned i = 0; i < textures.Size(); ++i)
+        cache_->ReloadResource(textures[i]);
+    
+    cache_->GetResources(textures, TextureCube::GetTypeStatic());
+    for (unsigned i = 0; i < textures.Size(); ++i)
+        cache_->ReloadResource(textures[i]);
+}
+
+void Renderer::CreateGeometries()
+{
+    SharedPtr<VertexBuffer> plvb(new VertexBuffer(context_));
+    plvb->SetSize(24, MASK_POSITION);
+    plvb->SetData(pointLightVertexData);
+    
+    SharedPtr<IndexBuffer> plib(new IndexBuffer(context_));
+    plib->SetSize(132, false);
+    plib->SetData(pointLightIndexData);
+    
+    pointLightGeometry_ = new Geometry(context_);
+    pointLightGeometry_->SetVertexBuffer(0, plvb);
+    pointLightGeometry_->SetIndexBuffer(plib);
+    pointLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, plib->GetIndexCount());
+    
+    SharedPtr<VertexBuffer> slvb(new VertexBuffer(context_));
+    slvb->SetSize(8, MASK_POSITION);
+    slvb->SetData(spotLightVertexData);
+    
+    SharedPtr<IndexBuffer> slib(new IndexBuffer(context_));
+    slib->SetSize(36, false);
+    slib->SetData(spotLightIndexData);
+    
+    spotLightGeometry_ = new Geometry(context_);
+    spotLightGeometry_->SetVertexBuffer(0, slvb);
+    spotLightGeometry_->SetIndexBuffer(slib);
+    spotLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, slib->GetIndexCount());
+    
+    faceSelectCubeMap_ = new TextureCube(context_);
+    faceSelectCubeMap_->SetNumLevels(1);
+    faceSelectCubeMap_->SetSize(1, graphics_->GetRGBAFormat());
+    faceSelectCubeMap_->SetFilterMode(FILTER_NEAREST);
+    
+    unsigned char data[256 * 256 * 4];
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        unsigned axis = i / 2;
+        data[0] = (axis == 0) ? 255 : 0;
+        data[1] = (axis == 1) ? 255 : 0;
+        data[2] = (axis == 2) ? 255 : 0;
+        data[3] = 0;
+        faceSelectCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 1, 1, data);
+    }
+    
+    indirectionCubeMap_ = new TextureCube(context_);
+    indirectionCubeMap_->SetNumLevels(1);
+    indirectionCubeMap_->SetSize(256, graphics_->GetRGBAFormat());
+    indirectionCubeMap_->SetFilterMode(FILTER_BILINEAR);
+    indirectionCubeMap_->SetAddressMode(COORD_U, ADDRESS_CLAMP);
+    indirectionCubeMap_->SetAddressMode(COORD_V, ADDRESS_CLAMP);
+    indirectionCubeMap_->SetAddressMode(COORD_W, ADDRESS_CLAMP);
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        unsigned char faceX = (i & 1) * 255;
+        unsigned char faceY = (i / 2) * 255 / 3;
+        unsigned char* dest = data;
+        for (unsigned y = 0; y < 256; ++y)
+        {
+            for (unsigned x = 0; x < 256; ++x)
+            {
+                #ifdef USE_OPENGL
+                *dest++ = x;
+                *dest++ = 255 - y;
+                *dest++ = faceX;
+                *dest++ = 255 * 2 / 3 - faceY;
+                #else
+                *dest++ = x;
+                *dest++ = y;
+                *dest++ = faceX;
+                *dest++ = faceY;
+                #endif
+            }
+        }
+        
+        indirectionCubeMap_->SetData((CubeMapFace)i, 0, 0, 0, 256, 256, data);
+    }
+}
+
+void Renderer::CreateInstancingBuffer()
+{
+    // Do not create buffer if instancing not supported
+    if (!graphics_->GetSM3Support())
+    {
+        instancingBuffer_.Reset();
+        dynamicInstancing_ = false;
+        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))
+    {
+        instancingBuffer_.Reset();
+        dynamicInstancing_ = false;
+    }
+}
+
+void Renderer::ResetShadowMaps()
+{
+    shadowMaps_.Clear();
+    colorShadowMaps_.Clear();
+    shadowDepthStencil_.Reset();
+}
+
+void Renderer::HandleScreenMode(StringHash eventType, VariantMap& eventData)
+{
+    if (!initialized_)
+        Initialize();
+    else
+    {
+        // When screen mode changes, purge old views
+        ResetViews();
+    }
+}
+
+void Renderer::HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData)
+{
+    // Reinitialize if already initialized
+    if (initialized_)
+        Initialize();
+}
+
+void Renderer::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (initialized_)
+    {
+        using namespace RenderUpdate;
+        
+        Update(eventData[P_TIMESTEP].GetFloat());
+    }
+}

+ 394 - 392
Engine/Graphics/Renderer.h

@@ -1,392 +1,394 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2011 Lasse Öörni
-//
-// 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.
-//
-
-#pragma once
-
-#include "Batch.h"
-#include "Color.h"
-#include "Drawable.h"
-#include "HashMap.h"
-#include "HashSet.h"
-#include "Mutex.h"
-#include "RenderSurface.h"
-
-class DebugRenderer;
-class Geometry;
-class Drawable;
-class Light;
-class Material;
-class Pass;
-class Technique;
-class Octree;
-class Graphics;
-class ResourceCache;
-class Skeleton;
-class OcclusionBuffer;
-class Texture2D;
-class TextureCube;
-class View;
-class Zone;
-
-static const int SHADOW_MIN_PIXELS = 64;
-static const int NUM_LIGHT_TYPES = 3;
-static const int NUM_SHADOWMAP_RESOLUTIONS = 3;
-static const int INSTANCING_BUFFER_DEFAULT_SIZE = 1024;
-
-/// Light vertex shader variations.
-enum LightVSVariation
-{
-    LVS_DIR = 0,
-    LVS_SPOT,
-    LVS_POINT,
-    LVS_SPEC,
-    LVS_SPOTSPEC,
-    LVS_POINTSPEC,
-    LVS_SHADOW,
-    LVS_SPOTSHADOW,
-    LVS_POINTSHADOW,
-    LVS_DIRSPECSHADOW,
-    LVS_SPOTSPECSHADOW,
-    LVS_POINTSPECSHADOW,
-    MAX_LIGHT_VS_VARIATIONS
-};
-
-/// Per-vertex light vertex shader variations.
-enum VertexLightVSVariation
-{
-    VLVS_NOLIGHTS = 0,
-    VLVS_1LIGHT,
-    VLVS_2LIGHTS,
-    VLVS_3LIGHTS,
-    VLVS_4LIGHTS,
-    MAX_VERTEXLIGHT_VS_VARIATIONS
-};
-
-/// Light pixel shader variations.
-enum LightPSVariation
-{
-    LPS_NONE = 0,
-    LPS_SPOT,
-    LPS_POINT,
-    LPS_POINTMASK,
-    LPS_SPEC,
-    LPS_SPOTSPEC,
-    LPS_POINTSPEC,
-    LPS_POINTMASKSPEC,
-    LPS_SHADOW,
-    LPS_SPOTSHADOW,
-    LPS_POINTSHADOW,
-    LPS_POINTMASKSHADOW,
-    LPS_SHADOWSPEC,
-    LPS_SPOTSHADOWSPEC,
-    LPS_POINTSHADOWSPEC,
-    LPS_POINTMASKSHADOWSPEC,
-    MAX_LIGHT_PS_VARIATIONS
-};
-
-/// High-level rendering subsystem. Manages drawing of 3D views.
-class Renderer : public Object
-{
-    OBJECT(Object);
-    
-public:
-    /// Construct.
-    Renderer(Context* context);
-    /// Destruct.
-    virtual ~Renderer();
-    
-    /// %Set number of viewports to render.
-    void SetNumViewports(unsigned num);
-    /// %Set a viewport.
-    void SetViewport(unsigned index, const Viewport& viewport);
-    /// %Set specular lighting on/off.
-    void SetSpecularLighting(bool enable);
-    /// %Set texture anisotropy.
-    void SetTextureAnisotropy(int level);
-    /// %Set texture filtering.
-    void SetTextureFilterMode(TextureFilterMode mode);
-    /// %Set texture quality level.
-    void SetTextureQuality(int quality);
-    /// %Set material quality level.
-    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.)
-    void SetShadowQuality(int quality);
-    /// %Set reuse of shadow maps. Default is true. If disabled, also transparent geometry can be shadowed.
-    void SetReuseShadowMaps(bool enable);
-    /// %Set maximum number of shadow maps created for one resolution. Only has effect if reuse of shadow maps is disabled.
-    void SetMaxShadowMaps(int shadowMaps);
-    /// %Set maximum number of directional light shadow map cascades. Affects the size of directional light shadow maps.
-    void SetMaxShadowCascades(int cascades);
-    /// %Set light stencil optimization on/off.
-    void SetLightStencilMasking(bool enable);
-    /// %Set dynamic instancing on/off.
-    void SetDynamicInstancing(bool enable);
-    /// %Set maximum number of triangles per object for instancing.
-    void SetMaxInstanceTriangles(int triangles);
-    /// %Set maximum number of occluder trianges.
-    void SetMaxOccluderTriangles(int triangles);
-    /// %Set occluder buffer width.
-    void SetOcclusionBufferSize(int size);
-    /// %Set required screen size (1.0 = full screen) for occluders.
-    void SetOccluderSizeThreshold(float screenSize);
-    /// Return number of viewports.
-    unsigned GetNumViewports() const { return viewports_.Size(); }
-    /// Return viewport.
-    const Viewport& GetViewport(unsigned index) const;
-    /// Return whether specular lighting is enabled.
-    bool GetSpecularLighting() const { return specularLighting_; }
-    /// Return whether drawing shadows is enabled.
-    bool GetDrawShadows() const { return drawShadows_; }
-    /// Return texture anisotropy.
-    int GetTextureAnisotropy() const { return textureAnisotropy_; }
-    /// Return texture filtering.
-    TextureFilterMode GetTextureFilterMode() const { return textureFilterMode_; }
-    /// Return texture quality level.
-    int GetTextureQuality() const { return textureQuality_; }
-    /// Return material quality level.
-    int GetMaterialQuality() const { return materialQuality_; }
-    /// Return shadow map resolution.
-    int GetShadowMapSize() const { return shadowMapSize_; }
-    /// Return shadow quality.
-    int GetShadowQuality() const { return shadowQuality_; }
-    /// Return whether shadow maps are reused.
-    bool GetReuseShadowMaps() const { return reuseShadowMaps_; }
-    /// Return maximum number of shadow maps per resolution.
-    int GetMaxShadowMaps() const { return maxShadowMaps_; }
-    /// Return maximum number of directional light shadow map cascades.
-    int GetMaxShadowCascades() const { return maxShadowCascades_; }
-    /// Return whether light stencil optimization is in use.
-    bool GetLightStencilMasking() const { return lightStencilMasking_; }
-    /// Return whether dynamic instancing is in use.
-    bool GetDynamicInstancing() const { return dynamicInstancing_; }
-    /// Return maximum number of triangles per object for instancing.
-    int GetMaxInstanceTriangles() { return maxInstanceTriangles_; }
-    /// Return maximum number of occluder triangles.
-    int GetMaxOccluderTriangles() const { return maxOccluderTriangles_; }
-    /// Return occlusion buffer width.
-    int GetOcclusionBufferSize() const { return occlusionBufferSize_; }
-    /// Return occluder screen size threshold.
-    float GetOccluderSizeThreshold() const { return occluderSizeThreshold_; }
-    /// Return number of views rendered.
-    unsigned GetNumViews() const { return numViews_; }
-    /// Return number of primitives rendered.
-    unsigned GetNumPrimitives() const { return numPrimitives_; }
-    /// Return number of batches rendered.
-    unsigned GetNumBatches() const { return numBatches_; }
-    /// Return number of geometries rendered.
-    unsigned GetNumGeometries(bool allViews = false) const;
-    /// Return number of lights rendered.
-    unsigned GetNumLights(bool allViews = false) const;
-    /// Return number of shadow maps rendered.
-    unsigned GetNumShadowMaps(bool allViews = false) const;
-    /// Return number of occluders rendered.
-    unsigned GetNumOccluders(bool allViews = false) const;
-    /// Return the default zone.
-    Zone* GetDefaultZone() const { return defaultZone_; }
-    /// Return the default material.
-    Material* GetDefaultMaterial() const { return defaultMaterial_; }
-    /// Return the default range attenuation texture.
-    Texture2D* GetDefaultLightRamp() const { return defaultLightRamp_; }
-    /// Return the default spotlight attenuation texture.
-    Texture2D* GetDefaultLightSpot() const { return defaultLightSpot_; }
-    /// Return the shadowed pointlight face selection cube map.
-    TextureCube* GetFaceSelectCubeMap() const { return faceSelectCubeMap_; }
-    /// Return the shadowed pointlight indirection cube map.
-    TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
-    /// Return the instancing vertex buffer
-    VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
-    /// Return a vertex shader by name.
-    ShaderVariation* GetVertexShader(const String& name, bool checkExists = false) const;
-    /// Return a pixel shader by name.
-    ShaderVariation* GetPixelShader(const String& name, bool checkExists = false) const;
-    /// Return the stencil vertex shader.
-    ShaderVariation* GetStencilVS() const { return stencilVS_; }
-    /// Return the stencil pixel shader.
-    ShaderVariation* GetStencilPS() const { return stencilPS_; }
-    /// Return the frame update parameters.
-    const FrameInfo& GetFrameInfo() { return frame_; }
-    
-    /// Update for rendering. Called by HandleRenderUpdate().
-    void Update(float timeStep);
-    /// Render. Called by Engine.
-    void Render();
-    /// Add debug geometry to the debug renderer.
-    void DrawDebugGeometry(bool depthTest);
-    /// Add a view. Return true if successful.
-    bool AddView(RenderSurface* renderTarget, const Viewport& viewport);
-    /// Return volume geometry for a light.
-    Geometry* GetLightGeometry(Light* light);
-    /// 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 an occlusion buffer.
-    OcclusionBuffer* GetOcclusionBuffer(Camera* camera);
-    /// Allocate a temporary shadow camera and a scene node for it. Is thread-safe.
-    Camera* GetShadowCamera();
-    /// Get a shader program.
-    ShaderVariation* GetShader(const String& name, const String& extension, bool checkExists) const;
-    /// Choose shaders for a batch.
-    void SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows = true);
-    /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.
-    bool ResizeInstancingBuffer(unsigned numInstances);
-    /// Reset shadow map allocation counts.
-    void ResetShadowMapAllocations();
-    
-private:
-    /// Initialize when screen mode initially set.
-    void Initialize();
-    /// Clear views from previous frame.
-    void ResetViews();
-    /// Reload shaders.
-    void LoadShaders();
-    /// Reload shaders for a material technique.
-    void LoadMaterialShaders(Technique* technique);
-    /// Reload shaders for a material pass.
-    void LoadPassShaders(Technique* technique, PassType type, bool allowShadows = true);
-    /// Release shaders used in materials.
-    void ReleaseMaterialShaders();
-    /// Reload textures.
-    void ReloadTextures();
-    /// Create light volume geometries.
-    void CreateGeometries();
-    /// Create instancing vertex buffer.
-    void CreateInstancingBuffer();
-    /// Remove all shadow maps. Called when global shadow map resolution or format is changed.
-    void ResetShadowMaps();
-    /// Handle screen mode event.
-    void HandleScreenMode(StringHash eventType, VariantMap& eventData);
-    /// Handle graphics features (re)check event.
-    void HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData);
-    /// Handle render update event.
-    void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
-    
-    /// Graphics subsystem.
-    WeakPtr<Graphics> graphics_;
-    /// Resource cache subsystem.
-    WeakPtr<ResourceCache> cache_;
-    /// Default zone.
-    SharedPtr<Zone> defaultZone_;
-    /// Point light volume geometry.
-    SharedPtr<Geometry> pointLightGeometry_;
-    /// Spot light volume geometry.
-    SharedPtr<Geometry> spotLightGeometry_;
-    /// Instance stream vertex buffer.
-    SharedPtr<VertexBuffer> instancingBuffer_;
-    /// Default material.
-    SharedPtr<Material> defaultMaterial_;
-    /// Default range attenuation texture.
-    SharedPtr<Texture2D> defaultLightRamp_;
-    /// Default spotlight attenuation texture.
-    SharedPtr<Texture2D> defaultLightSpot_;
-    /// Face selection cube map for shadowed pointlights.
-    SharedPtr<TextureCube> faceSelectCubeMap_;
-    /// Indirection cube map for shadowed pointlights.
-    SharedPtr<TextureCube> indirectionCubeMap_;
-    /// Stencil rendering vertex shader.
-    SharedPtr<ShaderVariation> stencilVS_;
-    /// Stencil rendering pixel shader.
-    SharedPtr<ShaderVariation> stencilPS_;
-    /// Reusable scene nodes with shadow camera components.
-    Vector<SharedPtr<Node> > shadowCameraNodes_;
-    /// Reusable occlusion buffers.
-    Vector<SharedPtr<OcclusionBuffer> > occlusionBuffers_;
-    /// Shadow maps by resolution.
-    HashMap<int, Vector<SharedPtr<Texture2D> > > shadowMaps_;
-    /// Shadow map dummy color buffers by resolution.
-    HashMap<int, SharedPtr<Texture2D> > colorShadowMaps_;
-    /// Shadow map allocations by resolution.
-    HashMap<int, PODVector<Light*> > shadowMapAllocations_;
-    /// Shadow map stencil buffer, fallback mode only.
-    SharedPtr<Texture2D> shadowDepthStencil_;
-    /// Viewports.
-    Vector<Viewport> viewports_;
-    /// Views.
-    Vector<SharedPtr<View> > views_;
-    /// Octrees that have been updated during the frame.
-    HashSet<Octree*> updateOctrees_;
-    /// Techniques for which missing shader error has been displayed.
-    HashSet<Technique*> shaderErrorDisplayed_;
-    /// Mutex for shadow camera allocation.
-    Mutex rendererMutex_;
-    /// Vertex shader format.
-    String vsFormat_;
-    /// Pixel shader format.
-    String psFormat_;
-    /// Base directory for shaders.
-    String shaderPath_;
-    /// Frame info for rendering.
-    FrameInfo frame_;
-    /// Texture anisotropy level.
-    int textureAnisotropy_;
-    /// Texture filtering mode.
-    TextureFilterMode textureFilterMode_;
-    /// Texture quality level.
-    int textureQuality_;
-    /// Material quality level.
-    int materialQuality_;
-    /// Shadow map resolution.
-    int shadowMapSize_;
-    /// Shadow quality.
-    int shadowQuality_;
-    /// Maximum number of shadow maps per resolution.
-    int maxShadowMaps_;
-    /// Maximum number of directional light shadow cascades.
-    int maxShadowCascades_;
-    /// Maximum triangles per object for instancing.
-    int maxInstanceTriangles_;
-    /// Maximum occluder triangles.
-    int maxOccluderTriangles_;
-    /// Occlusion buffer width.
-    int occlusionBufferSize_;
-    /// Occluder screen size threshold.
-    float occluderSizeThreshold_;
-    /// Number of views.
-    unsigned numViews_;
-    /// Number of occlusion buffers in use.
-    unsigned numOcclusionBuffers_;
-    /// Number of temporary shadow cameras in use.
-    unsigned numShadowCameras_;
-    /// Number of primitives (3D geometry only.)
-    unsigned numPrimitives_;
-    /// Number of batches (3D geometry only.)
-    unsigned numBatches_;
-    /// Frame number on which shaders last changed.
-    unsigned shadersChangedFrameNumber_;
-    /// Specular lighting flag.
-    bool specularLighting_;
-    /// Draw shadows flag.
-    bool drawShadows_;
-    /// Shadow map reuse flag.
-    bool reuseShadowMaps_;
-    /// Light stencil optimization flag.
-    bool lightStencilMasking_;
-    /// Dynamic instancing flag.
-    bool dynamicInstancing_;
-    /// Shaders need reloading flag.
-    bool shadersDirty_;
-    /// Initialized flag.
-    bool initialized_;
-};
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse ��rni
+//
+// 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.
+//
+
+#pragma once
+
+#include "Batch.h"
+#include "Color.h"
+#include "Drawable.h"
+#include "HashMap.h"
+#include "HashSet.h"
+#include "Mutex.h"
+#include "RenderSurface.h"
+
+class DebugRenderer;
+class Geometry;
+class Drawable;
+class Light;
+class Material;
+class Pass;
+class Technique;
+class Octree;
+class Graphics;
+class ResourceCache;
+class Skeleton;
+class OcclusionBuffer;
+class Texture2D;
+class TextureCube;
+class View;
+class Zone;
+
+static const int SHADOW_MIN_PIXELS = 64;
+static const int NUM_LIGHT_TYPES = 3;
+static const int NUM_SHADOWMAP_RESOLUTIONS = 3;
+static const int INSTANCING_BUFFER_DEFAULT_SIZE = 1024;
+
+/// Light vertex shader variations.
+enum LightVSVariation
+{
+    LVS_DIR = 0,
+    LVS_SPOT,
+    LVS_POINT,
+    LVS_SPEC,
+    LVS_SPOTSPEC,
+    LVS_POINTSPEC,
+    LVS_SHADOW,
+    LVS_SPOTSHADOW,
+    LVS_POINTSHADOW,
+    LVS_DIRSPECSHADOW,
+    LVS_SPOTSPECSHADOW,
+    LVS_POINTSPECSHADOW,
+    MAX_LIGHT_VS_VARIATIONS
+};
+
+/// Per-vertex light vertex shader variations.
+enum VertexLightVSVariation
+{
+    VLVS_NOLIGHTS = 0,
+    VLVS_1LIGHT,
+    VLVS_2LIGHTS,
+    VLVS_3LIGHTS,
+    VLVS_4LIGHTS,
+    VLVS_5LIGHTS,
+    VLVS_6LIGHTS,
+    MAX_VERTEXLIGHT_VS_VARIATIONS
+};
+
+/// Light pixel shader variations.
+enum LightPSVariation
+{
+    LPS_NONE = 0,
+    LPS_SPOT,
+    LPS_POINT,
+    LPS_POINTMASK,
+    LPS_SPEC,
+    LPS_SPOTSPEC,
+    LPS_POINTSPEC,
+    LPS_POINTMASKSPEC,
+    LPS_SHADOW,
+    LPS_SPOTSHADOW,
+    LPS_POINTSHADOW,
+    LPS_POINTMASKSHADOW,
+    LPS_SHADOWSPEC,
+    LPS_SPOTSHADOWSPEC,
+    LPS_POINTSHADOWSPEC,
+    LPS_POINTMASKSHADOWSPEC,
+    MAX_LIGHT_PS_VARIATIONS
+};
+
+/// High-level rendering subsystem. Manages drawing of 3D views.
+class Renderer : public Object
+{
+    OBJECT(Object);
+    
+public:
+    /// Construct.
+    Renderer(Context* context);
+    /// Destruct.
+    virtual ~Renderer();
+    
+    /// %Set number of viewports to render.
+    void SetNumViewports(unsigned num);
+    /// %Set a viewport.
+    void SetViewport(unsigned index, const Viewport& viewport);
+    /// %Set specular lighting on/off.
+    void SetSpecularLighting(bool enable);
+    /// %Set texture anisotropy.
+    void SetTextureAnisotropy(int level);
+    /// %Set texture filtering.
+    void SetTextureFilterMode(TextureFilterMode mode);
+    /// %Set texture quality level.
+    void SetTextureQuality(int quality);
+    /// %Set material quality level.
+    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.)
+    void SetShadowQuality(int quality);
+    /// %Set reuse of shadow maps. Default is true. If disabled, also transparent geometry can be shadowed.
+    void SetReuseShadowMaps(bool enable);
+    /// %Set maximum number of shadow maps created for one resolution. Only has effect if reuse of shadow maps is disabled.
+    void SetMaxShadowMaps(int shadowMaps);
+    /// %Set maximum number of directional light shadow map cascades. Affects the size of directional light shadow maps.
+    void SetMaxShadowCascades(int cascades);
+    /// %Set light stencil optimization on/off.
+    void SetLightStencilMasking(bool enable);
+    /// %Set dynamic instancing on/off.
+    void SetDynamicInstancing(bool enable);
+    /// %Set maximum number of triangles per object for instancing.
+    void SetMaxInstanceTriangles(int triangles);
+    /// %Set maximum number of occluder trianges.
+    void SetMaxOccluderTriangles(int triangles);
+    /// %Set occluder buffer width.
+    void SetOcclusionBufferSize(int size);
+    /// %Set required screen size (1.0 = full screen) for occluders.
+    void SetOccluderSizeThreshold(float screenSize);
+    /// Return number of viewports.
+    unsigned GetNumViewports() const { return viewports_.Size(); }
+    /// Return viewport.
+    const Viewport& GetViewport(unsigned index) const;
+    /// Return whether specular lighting is enabled.
+    bool GetSpecularLighting() const { return specularLighting_; }
+    /// Return whether drawing shadows is enabled.
+    bool GetDrawShadows() const { return drawShadows_; }
+    /// Return texture anisotropy.
+    int GetTextureAnisotropy() const { return textureAnisotropy_; }
+    /// Return texture filtering.
+    TextureFilterMode GetTextureFilterMode() const { return textureFilterMode_; }
+    /// Return texture quality level.
+    int GetTextureQuality() const { return textureQuality_; }
+    /// Return material quality level.
+    int GetMaterialQuality() const { return materialQuality_; }
+    /// Return shadow map resolution.
+    int GetShadowMapSize() const { return shadowMapSize_; }
+    /// Return shadow quality.
+    int GetShadowQuality() const { return shadowQuality_; }
+    /// Return whether shadow maps are reused.
+    bool GetReuseShadowMaps() const { return reuseShadowMaps_; }
+    /// Return maximum number of shadow maps per resolution.
+    int GetMaxShadowMaps() const { return maxShadowMaps_; }
+    /// Return maximum number of directional light shadow map cascades.
+    int GetMaxShadowCascades() const { return maxShadowCascades_; }
+    /// Return whether light stencil optimization is in use.
+    bool GetLightStencilMasking() const { return lightStencilMasking_; }
+    /// Return whether dynamic instancing is in use.
+    bool GetDynamicInstancing() const { return dynamicInstancing_; }
+    /// Return maximum number of triangles per object for instancing.
+    int GetMaxInstanceTriangles() { return maxInstanceTriangles_; }
+    /// Return maximum number of occluder triangles.
+    int GetMaxOccluderTriangles() const { return maxOccluderTriangles_; }
+    /// Return occlusion buffer width.
+    int GetOcclusionBufferSize() const { return occlusionBufferSize_; }
+    /// Return occluder screen size threshold.
+    float GetOccluderSizeThreshold() const { return occluderSizeThreshold_; }
+    /// Return number of views rendered.
+    unsigned GetNumViews() const { return numViews_; }
+    /// Return number of primitives rendered.
+    unsigned GetNumPrimitives() const { return numPrimitives_; }
+    /// Return number of batches rendered.
+    unsigned GetNumBatches() const { return numBatches_; }
+    /// Return number of geometries rendered.
+    unsigned GetNumGeometries(bool allViews = false) const;
+    /// Return number of lights rendered.
+    unsigned GetNumLights(bool allViews = false) const;
+    /// Return number of shadow maps rendered.
+    unsigned GetNumShadowMaps(bool allViews = false) const;
+    /// Return number of occluders rendered.
+    unsigned GetNumOccluders(bool allViews = false) const;
+    /// Return the default zone.
+    Zone* GetDefaultZone() const { return defaultZone_; }
+    /// Return the default material.
+    Material* GetDefaultMaterial() const { return defaultMaterial_; }
+    /// Return the default range attenuation texture.
+    Texture2D* GetDefaultLightRamp() const { return defaultLightRamp_; }
+    /// Return the default spotlight attenuation texture.
+    Texture2D* GetDefaultLightSpot() const { return defaultLightSpot_; }
+    /// Return the shadowed pointlight face selection cube map.
+    TextureCube* GetFaceSelectCubeMap() const { return faceSelectCubeMap_; }
+    /// Return the shadowed pointlight indirection cube map.
+    TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
+    /// Return the instancing vertex buffer
+    VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
+    /// Return a vertex shader by name.
+    ShaderVariation* GetVertexShader(const String& name, bool checkExists = false) const;
+    /// Return a pixel shader by name.
+    ShaderVariation* GetPixelShader(const String& name, bool checkExists = false) const;
+    /// Return the stencil vertex shader.
+    ShaderVariation* GetStencilVS() const { return stencilVS_; }
+    /// Return the stencil pixel shader.
+    ShaderVariation* GetStencilPS() const { return stencilPS_; }
+    /// Return the frame update parameters.
+    const FrameInfo& GetFrameInfo() { return frame_; }
+    
+    /// Update for rendering. Called by HandleRenderUpdate().
+    void Update(float timeStep);
+    /// Render. Called by Engine.
+    void Render();
+    /// Add debug geometry to the debug renderer.
+    void DrawDebugGeometry(bool depthTest);
+    /// Add a view. Return true if successful.
+    bool AddView(RenderSurface* renderTarget, const Viewport& viewport);
+    /// Return volume geometry for a light.
+    Geometry* GetLightGeometry(Light* light);
+    /// 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 an occlusion buffer.
+    OcclusionBuffer* GetOcclusionBuffer(Camera* camera);
+    /// Allocate a temporary shadow camera and a scene node for it. Is thread-safe.
+    Camera* GetShadowCamera();
+    /// Get a shader program.
+    ShaderVariation* GetShader(const String& name, const String& extension, bool checkExists) const;
+    /// Choose shaders for a batch.
+    void SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows = true);
+    /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.
+    bool ResizeInstancingBuffer(unsigned numInstances);
+    /// Reset shadow map allocation counts.
+    void ResetShadowMapAllocations();
+    
+private:
+    /// Initialize when screen mode initially set.
+    void Initialize();
+    /// Clear views from previous frame.
+    void ResetViews();
+    /// Reload shaders.
+    void LoadShaders();
+    /// Reload shaders for a material technique.
+    void LoadMaterialShaders(Technique* technique);
+    /// Reload shaders for a material pass.
+    void LoadPassShaders(Technique* technique, PassType type, bool allowShadows = true);
+    /// Release shaders used in materials.
+    void ReleaseMaterialShaders();
+    /// Reload textures.
+    void ReloadTextures();
+    /// Create light volume geometries.
+    void CreateGeometries();
+    /// Create instancing vertex buffer.
+    void CreateInstancingBuffer();
+    /// Remove all shadow maps. Called when global shadow map resolution or format is changed.
+    void ResetShadowMaps();
+    /// Handle screen mode event.
+    void HandleScreenMode(StringHash eventType, VariantMap& eventData);
+    /// Handle graphics features (re)check event.
+    void HandleGraphicsFeatures(StringHash eventType, VariantMap& eventData);
+    /// Handle render update event.
+    void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Graphics subsystem.
+    WeakPtr<Graphics> graphics_;
+    /// Resource cache subsystem.
+    WeakPtr<ResourceCache> cache_;
+    /// Default zone.
+    SharedPtr<Zone> defaultZone_;
+    /// Point light volume geometry.
+    SharedPtr<Geometry> pointLightGeometry_;
+    /// Spot light volume geometry.
+    SharedPtr<Geometry> spotLightGeometry_;
+    /// Instance stream vertex buffer.
+    SharedPtr<VertexBuffer> instancingBuffer_;
+    /// Default material.
+    SharedPtr<Material> defaultMaterial_;
+    /// Default range attenuation texture.
+    SharedPtr<Texture2D> defaultLightRamp_;
+    /// Default spotlight attenuation texture.
+    SharedPtr<Texture2D> defaultLightSpot_;
+    /// Face selection cube map for shadowed pointlights.
+    SharedPtr<TextureCube> faceSelectCubeMap_;
+    /// Indirection cube map for shadowed pointlights.
+    SharedPtr<TextureCube> indirectionCubeMap_;
+    /// Stencil rendering vertex shader.
+    SharedPtr<ShaderVariation> stencilVS_;
+    /// Stencil rendering pixel shader.
+    SharedPtr<ShaderVariation> stencilPS_;
+    /// Reusable scene nodes with shadow camera components.
+    Vector<SharedPtr<Node> > shadowCameraNodes_;
+    /// Reusable occlusion buffers.
+    Vector<SharedPtr<OcclusionBuffer> > occlusionBuffers_;
+    /// Shadow maps by resolution.
+    HashMap<int, Vector<SharedPtr<Texture2D> > > shadowMaps_;
+    /// Shadow map dummy color buffers by resolution.
+    HashMap<int, SharedPtr<Texture2D> > colorShadowMaps_;
+    /// Shadow map allocations by resolution.
+    HashMap<int, PODVector<Light*> > shadowMapAllocations_;
+    /// Shadow map stencil buffer, fallback mode only.
+    SharedPtr<Texture2D> shadowDepthStencil_;
+    /// Viewports.
+    Vector<Viewport> viewports_;
+    /// Views.
+    Vector<SharedPtr<View> > views_;
+    /// Octrees that have been updated during the frame.
+    HashSet<Octree*> updateOctrees_;
+    /// Techniques for which missing shader error has been displayed.
+    HashSet<Technique*> shaderErrorDisplayed_;
+    /// Mutex for shadow camera allocation.
+    Mutex rendererMutex_;
+    /// Vertex shader format.
+    String vsFormat_;
+    /// Pixel shader format.
+    String psFormat_;
+    /// Base directory for shaders.
+    String shaderPath_;
+    /// Frame info for rendering.
+    FrameInfo frame_;
+    /// Texture anisotropy level.
+    int textureAnisotropy_;
+    /// Texture filtering mode.
+    TextureFilterMode textureFilterMode_;
+    /// Texture quality level.
+    int textureQuality_;
+    /// Material quality level.
+    int materialQuality_;
+    /// Shadow map resolution.
+    int shadowMapSize_;
+    /// Shadow quality.
+    int shadowQuality_;
+    /// Maximum number of shadow maps per resolution.
+    int maxShadowMaps_;
+    /// Maximum number of directional light shadow cascades.
+    int maxShadowCascades_;
+    /// Maximum triangles per object for instancing.
+    int maxInstanceTriangles_;
+    /// Maximum occluder triangles.
+    int maxOccluderTriangles_;
+    /// Occlusion buffer width.
+    int occlusionBufferSize_;
+    /// Occluder screen size threshold.
+    float occluderSizeThreshold_;
+    /// Number of views.
+    unsigned numViews_;
+    /// Number of occlusion buffers in use.
+    unsigned numOcclusionBuffers_;
+    /// Number of temporary shadow cameras in use.
+    unsigned numShadowCameras_;
+    /// Number of primitives (3D geometry only.)
+    unsigned numPrimitives_;
+    /// Number of batches (3D geometry only.)
+    unsigned numBatches_;
+    /// Frame number on which shaders last changed.
+    unsigned shadersChangedFrameNumber_;
+    /// Specular lighting flag.
+    bool specularLighting_;
+    /// Draw shadows flag.
+    bool drawShadows_;
+    /// Shadow map reuse flag.
+    bool reuseShadowMaps_;
+    /// Light stencil optimization flag.
+    bool lightStencilMasking_;
+    /// Dynamic instancing flag.
+    bool dynamicInstancing_;
+    /// Shaders need reloading flag.
+    bool shadersDirty_;
+    /// Initialized flag.
+    bool initialized_;
+};