Browse Source

Removed the old postprocess system. Instead renderpath fragments can be appended.
Implemented missing quad rendering command in renderpath.

Lasse Öörni 13 years ago
parent
commit
2a4fbcdf54

+ 20 - 0
Bin/CoreData/Shaders/HLSL/CopyFrameBuffer.hlsl

@@ -0,0 +1,20 @@
+#include "Uniforms.hlsl"
+#include "Samplers.hlsl"
+#include "Transform.hlsl"
+#include "ScreenPos.hlsl"
+
+void VS(float4 iPos : POSITION,
+    out float2 oScreenPos : TEXCOORD0,
+    out float4 oPos : POSITION)
+{
+    float4x3 modelMatrix = iModelMatrix;
+    float3 worldPos = GetWorldPos(modelMatrix);
+    oPos = GetClipPos(worldPos);
+    oScreenPos = GetScreenPosPreDiv(oPos);
+}
+
+void PS(float2 iScreenPos : TEXCOORD0,
+    out float4 oColor : COLOR0)
+{
+    oColor = tex2D(sDiffMap, iScreenPos);
+}

+ 3 - 0
Bin/CoreData/Shaders/HLSL/CopyFrameBuffer.xml

@@ -0,0 +1,3 @@
+<shaders>
+    <shader />
+</shaders>

+ 14 - 14
Bin/Data/PostProcess/Bloom.xml

@@ -1,19 +1,19 @@
-<postprocess>
-    <rendertarget name="vblur" sizedivisor="4 4" format="rgb" filter="true" />
-    <rendertarget name="hblur" sizedivisor="4 4" format="rgb" filter="true" />
-    <parameter name="BloomThreshold" value="0.3" />
-    <parameter name="BloomMix" value="0.9 0.4" />
-    <pass vs="Bloom" ps="Bloom_Bright" output="vblur">
+<renderpath>
+    <rendertarget name="vblur" tag="Bloom" sizedivisor="4 4" format="rgb" filter="true" />
+    <rendertarget name="hblur" tag="Bloom" sizedivisor="4 4" format="rgb" filter="true" />
+    <command type="quad" tag="Bloom" vs="Bloom" ps="Bloom_Bright" output="vblur">
+        <parameter name="BloomThreshold" value="0.3" />
         <texture unit="diffuse" name="viewport" />
-    </pass>
-    <pass vs="Bloom" ps="Bloom_HBlur" output="hblur">
+    </command>
+    <command type="quad" tag="Bloom" vs="Bloom" ps="Bloom_HBlur" output="hblur">
         <texture unit="diffuse" name="vblur" />
-    </pass>
-    <pass vs="Bloom" ps="Bloom_VBlur" output="vblur">
+    </command>
+    <command type="quad" tag="Bloom" vs="Bloom" ps="Bloom_VBlur" output="vblur">
         <texture unit="diffuse" name="hblur" />
-    </pass>
-    <pass vs="Bloom" ps="Bloom_Combine" output="viewport">
+    </command>
+    <command type="quad" tag="Bloom" vs="Bloom" ps="Bloom_Combine" output="viewport">
+        <parameter name="BloomMix" value="0.9 0.4" />
         <texture unit="diffuse" name="viewport" />
         <texture unit="normal" name="vblur" />
-    </pass>
-</postprocess>
+    </command>
+</renderpath>

+ 4 - 4
Bin/Data/PostProcess/EdgeFilter.xml

@@ -1,6 +1,6 @@
-<postprocess>
-    <pass vs="EdgeFilter" ps="EdgeFilter" output="viewport">
+<renderpath>
+    <command type="quad" tag="EdgeFilter" vs="EdgeFilter" ps="EdgeFilter" output="viewport">
         <texture unit="diffuse" name="viewport" />
         <parameter name="EdgeFilterParams" value="0.4 0.5 0.75" />
-    </pass>
-</postprocess>
+    </command>
+</renderpath>

+ 4 - 4
Bin/Data/PostProcess/GreyScale.xml

@@ -1,5 +1,5 @@
-<postprocess>
-    <pass vs="GreyScale" ps="GreyScale" output="viewport">
+<renderpath>
+    <command type="quad" tag="GreyScale" vs="GreyScale" ps="GreyScale" output="viewport">
         <texture unit="diffuse" name="viewport" />
-    </pass>
-</postprocess>
+    </command>
+</renderpath>

+ 14 - 18
Bin/Data/Scripts/Physics.as

@@ -3,8 +3,6 @@
 Scene@ testScene;
 Camera@ camera;
 Node@ cameraNode;
-PostProcess@ edgeFilter;
-PostProcess@ bloom;
 
 float yaw = 0.0;
 float pitch = 0.0;
@@ -106,19 +104,17 @@ void InitScene()
 
     if (!engine.headless)
     {
-        edgeFilter = PostProcess();
-        edgeFilter.parameters = cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml");
-        edgeFilter.active = false; // Start out disabled
-
-        bloom = PostProcess();
-        bloom.parameters = cache.GetResource("XMLFile", "PostProcess/Bloom.xml");
-        bloom.active = false;
-
         renderer.viewports[0] = Viewport(testScene, camera);
-        renderer.viewports[0].AddPostProcess(edgeFilter);
-        renderer.viewports[0].AddPostProcess(bloom);
         
-        audio.listener = cameraNode.CreateComponent("SoundListener");        
+        // Add bloom & FXAA effects to the renderpath. Clone the default renderpath so that we don't affect it
+        RenderPath@ newRenderPath = renderer.viewports[0].renderPath.Clone();
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/Bloom.xml"));
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml"));
+        newRenderPath.SetActive("Bloom", false);
+        newRenderPath.SetActive("EdgeFilter", false);
+        renderer.viewports[0].renderPath = newRenderPath;
+
+        audio.listener = cameraNode.CreateComponent("SoundListener");
     }
 
     if (runClient)
@@ -306,14 +302,14 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 drawDebug = 0;
         }
 
-        if (key == 'O')
-            camera.orthographic = !camera.orthographic;
-
         if (key == 'B')
-            bloom.active = !bloom.active;
+            renderer.viewports[0].renderPath.ToggleActive("Bloom");
 
         if (key == 'F')
-            edgeFilter.active = !edgeFilter.active;
+            renderer.viewports[0].renderPath.ToggleActive("EdgeFilter");
+
+        if (key == 'O')
+            camera.orthographic = !camera.orthographic;
 
         if (key == 'T')
             debugHud.Toggle(DEBUGHUD_SHOW_PROFILER);

+ 13 - 17
Bin/Data/Scripts/Terrain.as

@@ -3,8 +3,6 @@
 Scene@ testScene;
 Camera@ camera;
 Node@ cameraNode;
-PostProcess@ edgeFilter;
-PostProcess@ bloom;
 
 float yaw = 0.0;
 float pitch = 0.0;
@@ -108,18 +106,16 @@ void InitScene()
 
     if (!engine.headless)
     {
-        edgeFilter = PostProcess();
-        edgeFilter.parameters = cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml");
-        edgeFilter.active = false; // Start out disabled
-
-        bloom = PostProcess();
-        bloom.parameters = cache.GetResource("XMLFile", "PostProcess/Bloom.xml");
-        bloom.active = false;
-
         renderer.viewports[0] = Viewport(testScene, camera);
-        renderer.viewports[0].AddPostProcess(edgeFilter);
-        renderer.viewports[0].AddPostProcess(bloom);
         
+        // Add bloom & FXAA effects to the renderpath. Clone the default renderpath so that we don't affect it
+        RenderPath@ newRenderPath = renderer.viewports[0].renderPath.Clone();
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/Bloom.xml"));
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml"));
+        newRenderPath.SetActive("Bloom", false);
+        newRenderPath.SetActive("EdgeFilter", false);
+        renderer.viewports[0].renderPath = newRenderPath;
+
         audio.listener = cameraNode.CreateComponent("SoundListener");
     }
 
@@ -295,14 +291,14 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 drawDebug = 0;
         }
 
-        if (key == 'O')
-            camera.orthographic = !camera.orthographic;
-
         if (key == 'B')
-            bloom.active = !bloom.active;
+            renderer.viewports[0].renderPath.ToggleActive("Bloom");
 
         if (key == 'F')
-            edgeFilter.active = !edgeFilter.active;
+            renderer.viewports[0].renderPath.ToggleActive("EdgeFilter");
+
+        if (key == 'O')
+            camera.orthographic = !camera.orthographic;
 
         if (key == 'T')
             debugHud.Toggle(DEBUGHUD_SHOW_PROFILER);

+ 13 - 17
Bin/Data/Scripts/TestScene.as

@@ -3,8 +3,6 @@
 Scene@ testScene;
 Camera@ camera;
 Node@ cameraNode;
-PostProcess@ edgeFilter;
-PostProcess@ bloom;
 
 float yaw = 0.0;
 float pitch = 0.0;
@@ -110,17 +108,15 @@ void InitScene()
 
     if (!engine.headless)
     {
-        edgeFilter = PostProcess();
-        edgeFilter.parameters = cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml");
-        edgeFilter.active = false; // Start out disabled
-
-        bloom = PostProcess();
-        bloom.parameters = cache.GetResource("XMLFile", "PostProcess/Bloom.xml");
-        bloom.active = false;
-
         renderer.viewports[0] = Viewport(testScene, camera);
-        renderer.viewports[0].AddPostProcess(edgeFilter);
-        renderer.viewports[0].AddPostProcess(bloom);
+        
+        // Add bloom & FXAA effects to the renderpath. Clone the default renderpath so that we don't affect it
+        RenderPath@ newRenderPath = renderer.viewports[0].renderPath.Clone();
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/Bloom.xml"));
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml"));
+        newRenderPath.SetActive("Bloom", false);
+        newRenderPath.SetActive("EdgeFilter", false);
+        renderer.viewports[0].renderPath = newRenderPath;
 
         audio.listener = cameraNode.CreateComponent("SoundListener");
     }
@@ -345,14 +341,14 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 drawDebug = 0;
         }
 
-        if (key == 'O')
-            camera.orthographic = !camera.orthographic;
-
         if (key == 'B')
-            bloom.active = !bloom.active;
+            renderer.viewports[0].renderPath.ToggleActive("Bloom");
 
         if (key == 'F')
-            edgeFilter.active = !edgeFilter.active;
+            renderer.viewports[0].renderPath.ToggleActive("EdgeFilter");
+
+        if (key == 'O')
+            camera.orthographic = !camera.orthographic;
 
         if (key == 'T')
             debugHud.Toggle(DEBUGHUD_SHOW_PROFILER);

+ 13 - 17
Bin/Data/Scripts/TestSceneOld.as

@@ -12,8 +12,6 @@ Node@ cameraNode;
 Camera@ camera;
 Node@ cameraLightNode;
 Light@ cameraLight;
-PostProcess@ edgeFilter;
-PostProcess@ bloom;
 
 float yaw = 0.0;
 float pitch = 0.0;
@@ -320,18 +318,16 @@ void CreateCamera()
 
     if (!engine.headless)
     {
-        edgeFilter = PostProcess();
-        edgeFilter.parameters = cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml");
-        edgeFilter.active = false; // Start out disabled
-
-        bloom = PostProcess();
-        bloom.parameters = cache.GetResource("XMLFile", "PostProcess/Bloom.xml");
-        bloom.active = false;
-
         renderer.viewports[0] = Viewport(testScene, camera);
-        renderer.viewports[0].AddPostProcess(edgeFilter);
-        renderer.viewports[0].AddPostProcess(bloom);
         
+        // Add bloom & FXAA effects to the renderpath. Clone the default renderpath so that we don't affect it
+        RenderPath@ newRenderPath = renderer.viewports[0].renderPath.Clone();
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/Bloom.xml"));
+        newRenderPath.Append(cache.GetResource("XMLFile", "PostProcess/EdgeFilter.xml"));
+        newRenderPath.SetActive("Bloom", false);
+        newRenderPath.SetActive("EdgeFilter", false);
+        renderer.viewports[0].renderPath = newRenderPath;
+
         audio.listener = cameraNode.CreateComponent("SoundListener");
     }
 }
@@ -447,14 +443,14 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 cameraLightNode.parent = testScene;
         }
 
-        if (key == 'O')
-            camera.orthographic = !camera.orthographic;
-
         if (key == 'B')
-            bloom.active = !bloom.active;
+            renderer.viewports[0].renderPath.ToggleActive("Bloom");
 
         if (key == 'F')
-            edgeFilter.active = !edgeFilter.active;
+            renderer.viewports[0].renderPath.ToggleActive("EdgeFilter");
+
+        if (key == 'O')
+            camera.orthographic = !camera.orthographic;
 
         if (key == 'T')
             debugHud.Toggle(DEBUGHUD_SHOW_PROFILER);

+ 1 - 29
Docs/Reference.dox

@@ -442,8 +442,6 @@ To render, it needs a Scene with an Octree component, and a Camera that does not
 
 By default there is one viewport, but the amount can be increased with the function \ref Renderer::SetNumViewports "SetNumViewports()". The viewport(s) should cover the entire screen or otherwise hall-of-mirrors artifacts may occur. By specifying a zero screen rectangle the whole window will be used automatically. The viewports will be rendered in ascending order, so if you want for example to have a small overlay window on top of the main viewport, use viewport index 0 for the main view, and 1 for the overlay.
 
-Viewports can have a chain of post-processing effects. See \ref Postprocessing "Post-processing" for more details.
-
 Each viewport defines a command sequence for rendering the scene, called the RenderPath. By default there exist forward, light pre-pass and deferred rendering paths in the Bin/CoreData/RenderPaths directory, see \ref Renderer::SetDefaultRenderPathName "SetDefaultRenderPathName()" to set the default for new viewports. Deferred rendering modes will be advantageous once there is a large number of per-pixel lights affecting each object, but their disadvantages are the lack of hardware multisampling and inability to choose the lighting model per material. In place of multisample antialiasing, a FXAA post-processing edge filter can be used, see the TestScene script application for an example of how to use.
 
 The steps for rendering each viewport on each frame are roughly the following:
@@ -503,7 +501,7 @@ See \ref GPUObject::IsDataLost "IsDataLost()" function in VertexBuffer, IndexBuf
 
 \section Rendering_Further Further details
 
-See also \ref Materials "Materials", \ref Lights "Lights and shadows", \ref SkeletalAnimation "Skeletal animation", \ref Particles "Particle systems", \ref Postprocessing "Post-processing", \ref Zones "Zones", and \ref AuxiliaryViews "Auxiliary views".
+See also \ref Materials "Materials", \ref Lights "Lights and shadows", \ref SkeletalAnimation "Skeletal animation", \ref Particles "Particle systems", \ref Zones "Zones", and \ref AuxiliaryViews "Auxiliary views".
 
 See \ref RenderingModes "Rendering modes" for detailed discussion on the forward, light pre-pass and deferred rendering modes.
 
@@ -827,32 +825,6 @@ Zones have two special flags: override mode and ambient gradient. If the camera
 Zones also define a lightmask and a shadowmask (with all bits set by default.) An object's final lightmask for light culling is determined by ANDing the object lightmask and the zone lightmask. The final shadowmask is also calculated in the same way.
 
 
-\page Postprocessing Post-processing
-
-After a viewport's 3D scene content is rendered, post-processing effects can be applied to it. These are viewport-sized quads rendered with configurable vertex and pixel shaders, shader parameters and textures.
-
-The Viewport contains a vector of PostProcess pointers, into which they can be added, see \ref Viewport::AddPostProcess "AddPostProcess()" or \ref Viewport::InsertPostProcess "InsertPostProcess()". They are applied in order from first to last. Each post-processing effect can consist of a number of passes, represented by the class PostProcessPass. The last pass of the last effect will be rendered to the destination rendertarget, while the passes before will be rendered to an internal ping-pong buffer.
-
-In addition to configuring programmatically, the PostProcess can be configured with an XML file; use \ref PostProcess::LoadParameters "LoadParameters()" to specify the XML file to use. The format is the following:
-
-\code
-<postprocess>
-    <rendertarget name="RenderTargetName" size="x y" | sizedivisor="x y" format="rgb|rgba|rgba16|rgba16f|rgba32f|rg16|rg16f|rg32f|r16f|r32f" filter="true|false" />
-    <parameter name="GlobalShaderParameterName" value="x y z w" />
-    <pass vs="VertexShaderName" ps="PixelShaderName" output="viewport|RenderTargetName" />
-        <texture unit="diffuse|normal|specular|emissive|environment" name="viewport|RenderTargetName|TextureName" />
-        <parameter name="ShaderParameterName" value="x y z w" />
-    </pass>
-</postprocess>
-\endcode
-
-The additional rendertargets defined by a post-process effect are temporary and allocated on-demand from the Renderer. They can either specify an absolute width and height, or a divisor for the viewport size (for example sizedivisor="2 2" would divide the viewport dimensions by two.) A pass can output either into a defined rendertarget or into the viewport. The current contents of the viewport (either the scene render or a previous post-process pass) can also be read as a texture; this will always use the other half of the ping-pong buffer to avoid sampling the texture being rendered.
-
-%Shader parameters can be defined either as global for all passes, or as pass-specific. In addition to the user-defined shader parameters, the shader parameters GBufferOffsets and GBufferInvSize will always be set according to the viewport coordinates and the destination rendertarget inverse size, so that the GetScreenPos() shader function can operate correctly, and adjacent pixels can be sampled. See the EdgeFilter shader for an example. For the additional rendertargets the shader parameters <RenderTargetName>Offsets and <RenderTargetName>InvSize will be set (if they exist) to allow to sample also these rendertargets properly. In this case the "offsets" parameter represents the half-pixel UV offset needed to sample whole pixels on Direct3D9 only; on OpenGL it will be zero.
-
-Note that when hardware multisampling is used in conjunction with post-processing, the multisampled backbuffer will first be resolved to the ping-pong buffer before rendering the post-process passes. This has a small performance cost.
-
-
 \page AuxiliaryViews Auxiliary views
 
 Auxiliary views are viewports defined into a RenderSurface. These will be rendered whenever the texture containing the surface is visible, and can be typically used to implement for example reflections. The texture in question must have been created in rendertarget mode, see Texture's \ref Texture2D::SetSize "SetSize()" function.

+ 2 - 1
Engine/Engine/Engine.cpp

@@ -45,6 +45,7 @@
 #include "StringUtils.h"
 #include "UI.h"
 #include "WorkQueue.h"
+#include "XMLFile.h"
 
 #include "DebugNew.h"
 
@@ -264,7 +265,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
             return false;
         
         if (!renderPath.Empty())
-            renderer->SetDefaultRenderPathName(renderPath);
+            renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(renderPath));
         renderer->SetDrawShadows(shadows);
         if (shadows && lqShadows)
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);

+ 32 - 83
Engine/Engine/GraphicsAPI.cpp

@@ -35,7 +35,7 @@
 #include "Material.h"
 #include "Octree.h"
 #include "ParticleEmitter.h"
-#include "PostProcess.h"
+#include "RenderPath.h"
 #include "Scene.h"
 #include "SmoothedTransform.h"
 #include "Technique.h"
@@ -150,50 +150,38 @@ static Viewport* ConstructViewport()
     return new Viewport(GetScriptContext());
 }
 
-static Viewport* ConstructViewportSceneCamera(Scene* scene, Camera* camera, XMLFile* renderPath)
+static Viewport* ConstructViewportSceneCamera(Scene* scene, Camera* camera, RenderPath* renderPath)
 {
     return new Viewport(GetScriptContext(), scene, camera, renderPath);
 }
 
-static Viewport* ConstructViewportSceneCameraRect(Scene* scene, Camera* camera, const IntRect& rect, XMLFile* renderPath)
+static Viewport* ConstructViewportSceneCameraRect(Scene* scene, Camera* camera, const IntRect& rect, RenderPath* renderPath)
 {
     return new Viewport(GetScriptContext(), scene, camera, rect, renderPath);
 }
 
-static Viewport* ConstructViewportSceneCameraPostProcesses(Scene* scene, Camera* camera, CScriptArray* arr, XMLFile* renderPath)
+static bool Texture2DLoad(Image* image, bool useAlpha, Texture2D* ptr)
 {
-    Vector<SharedPtr<PostProcess> > vec;
-    if (arr)
-    {
-        vec.Reserve(arr->GetSize());
-        for (unsigned i = 0; i < arr->GetSize(); ++i)
-            vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
-    }
-    
-    return new Viewport(GetScriptContext(), scene, camera, vec, renderPath);
+    return ptr->Load(SharedPtr<Image>(image), useAlpha);
 }
 
-static Viewport* ConstructViewportSceneCameraRectPostProcesses(Scene* scene, Camera* camera, const IntRect& rect, CScriptArray* arr, XMLFile* renderPath)
+static bool TextureCubeLoad(CubeMapFace face, Image* image, bool useAlpha, TextureCube* ptr)
 {
-    Vector<SharedPtr<PostProcess> > vec;
-    if (arr)
-    {
-        vec.Reserve(arr->GetSize());
-        for (unsigned i = 0; i < arr->GetSize(); ++i)
-            vec.Push(SharedPtr<PostProcess>(*(static_cast<PostProcess**>(arr->At(i)))));
-    }
-    
-    return new Viewport(GetScriptContext(), scene, camera, rect, vec, renderPath);
+    return ptr->Load(face, SharedPtr<Image>(image), useAlpha);
 }
 
-static bool Texture2DLoad(Image* image, bool useAlpha, Texture2D* ptr)
+static RenderPath* ConstructRenderPath()
 {
-    return ptr->Load(SharedPtr<Image>(image), useAlpha);
+    return new RenderPath();
 }
 
-static bool TextureCubeLoad(CubeMapFace face, Image* image, bool useAlpha, TextureCube* ptr)
+static RenderPath* RenderPathClone(RenderPath* ptr)
 {
-    return ptr->Load(face, SharedPtr<Image>(image), useAlpha);
+    SharedPtr<RenderPath> clone = ptr->Clone();
+    // The shared pointer will go out of scope, so have to increment the reference count
+    // (here an auto handle can not be used)
+    clone->AddRef();
+    return clone.Get();
 }
 
 static void RegisterTextures(asIScriptEngine* engine)
@@ -234,27 +222,27 @@ static void RegisterTextures(asIScriptEngine* engine)
     
     RegisterTexture<Texture>(engine, "Texture");
     
-    // Must register PostProcess early as Viewport needs it
-    RegisterObject<PostProcess>(engine, "PostProcess");
-    RegisterRefCounted<Viewport>(engine, "Viewport");
+    RegisterRefCounted<RenderPath>(engine, "RenderPath");
+    engine->RegisterObjectBehaviour("RenderPath", asBEHAVE_FACTORY, "RenderPath@+ f()", asFUNCTION(ConstructRenderPath), asCALL_CDECL);
+    engine->RegisterObjectMethod("RenderPath", "RenderPath@ Clone()", asFUNCTION(RenderPathClone), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("RenderPath", "bool LoadParameters(XMLFile@+)", asMETHOD(RenderPath, LoadParameters), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPath", "bool Append(XMLFile@+)", asMETHOD(RenderPath, Append), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPath", "void SetActive(const String&in, bool)", asMETHOD(RenderPath, SetActive), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPath", "void ToggleActive(const String&in)", asMETHOD(RenderPath, ToggleActive), asCALL_THISCALL);
+    
+    RegisterObject<Viewport>(engine, "Viewport");
     engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f()", asFUNCTION(ConstructViewport), asCALL_CDECL);
-    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, XMLFile@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCamera), asCALL_CDECL);
-    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, const IntRect&in, XMLFile@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCameraRect), asCALL_CDECL);
-    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, Array<PostProcess@>@+, XMLFile@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCameraPostProcesses), asCALL_CDECL);
-    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, const IntRect&in, Array<PostProcess@>@+, XMLFile@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCameraRectPostProcesses), asCALL_CDECL);
-    engine->RegisterObjectMethod("Viewport", "void AddPostProcess(PostProcess@+)", asMETHOD(Viewport, AddPostProcess), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "void InsertPostProcess(uint, PostProcess@+)", asMETHOD(Viewport, InsertPostProcess), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "void RemovePostProcess(PostProcess@+)", asMETHODPR(Viewport, RemovePostProcess, (PostProcess*), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "void RemovePostProcess(uint)", asMETHODPR(Viewport, RemovePostProcess, (unsigned), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "void RemoveAllPostProcesses()", asMETHOD(Viewport, RemoveAllPostProcesses), asCALL_THISCALL);
+    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, RenderPath@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCamera), asCALL_CDECL);
+    engine->RegisterObjectBehaviour("Viewport", asBEHAVE_FACTORY, "Viewport@+ f(Scene@+, Camera@+, const IntRect&in, RenderPath@+ renderPath = null)", asFUNCTION(ConstructViewportSceneCameraRect), asCALL_CDECL);
+    engine->RegisterObjectMethod("Viewport", "void SetRenderPath(XMLFile@+)", asMETHODPR(Viewport, SetRenderPath, (XMLFile*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "void set_scene(Scene@+)", asMETHOD(Viewport, SetScene), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "Scene@+ get_scene() const", asMETHOD(Viewport, GetScene), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "void set_camera(Camera@+)", asMETHOD(Viewport, SetCamera), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "Camera@+ get_camera() const", asMETHOD(Viewport, GetCamera), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "void set_renderPath(XMLFile@+)", asMETHOD(Viewport, SetRenderPath), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Viewport", "void set_renderPath(RenderPath@+)", asMETHODPR(Viewport, SetRenderPath, (RenderPath*), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Viewport", "RenderPath@+ get_renderPath() const", asMETHOD(Viewport, GetRenderPath), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "void set_rect(const IntRect&in)", asMETHOD(Viewport, SetCamera), asCALL_THISCALL);
     engine->RegisterObjectMethod("Viewport", "const IntRect& get_rect() const", asMETHOD(Viewport, GetCamera), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Viewport", "uint get_numPostProcesses() const", asMETHOD(Viewport, GetNumPostProcesses), asCALL_THISCALL);
     
     engine->RegisterObjectType("RenderSurface", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("RenderSurface", asBEHAVE_ADDREF, "void f()", asMETHOD(RenderSurface, AddRef), asCALL_THISCALL);
@@ -411,47 +399,6 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Material", "const BiasParameters& get_depthBias() const", asMETHOD(Material, GetDepthBias), asCALL_THISCALL);
 }
 
-static PostProcess* PostProcessClone(PostProcess* ptr)
-{
-    SharedPtr<PostProcess> clone = ptr->Clone();
-    // The shared pointer will go out of scope, so have to increment the reference count
-    // (here an auto handle can not be used)
-    clone->AddRef();
-    return clone.Get();
-}
-
-static void RegisterPostProcess(asIScriptEngine* engine)
-{
-    RegisterRefCounted<PostProcessPass>(engine, "PostProcessPass");
-    engine->RegisterObjectMethod("PostProcessPass", "void RemoveShaderParameter(const String&in)", asMETHOD(PostProcessPass, RemoveShaderParameter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "void set_vertexShader(const String&in)", asMETHOD(PostProcessPass, SetVertexShader), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "const String& get_vertexShader() const", asMETHOD(PostProcessPass, GetVertexShader), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "void set_pixelShader(const String&in)", asMETHOD(PostProcessPass, SetPixelShader), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "const String& get_pixelShader() const", asMETHOD(PostProcessPass, GetPixelShader), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "void set_output(const String&in)", asMETHOD(PostProcessPass, SetOutput), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "const String& get_output() const", asMETHOD(PostProcessPass, GetOutput), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "void set_textures(uint, const String&in)", asMETHOD(PostProcessPass, SetTexture), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "const String& get_textures(uint) const", asMETHOD(PostProcessPass, GetTexture), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "void set_shaderParameters(const String&in, const Vector4&in)", asMETHOD(PostProcessPass, SetShaderParameter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcessPass", "Vector4 get_shaderParameters(const String&in) const", asMETHOD(PostProcessPass, GetShaderParameter), asCALL_THISCALL);
-    
-    RegisterObjectConstructor<PostProcess>(engine, "PostProcess");
-    engine->RegisterObjectMethod("PostProcess", "bool CreateRenderTarget(const String&in, uint, uint, uint, bool, bool)", asMETHOD(PostProcess, CreateRenderTarget), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "void RemoveRenderTarget(const String&in)", asMETHOD(PostProcess, RemoveRenderTarget), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "void RemoveShaderParameter(const String&in)", asMETHOD(PostProcess, RemoveShaderParameter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "PostProcess@ Clone() const", asFUNCTION(PostProcessClone), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("PostProcess", "bool HasRenderTarget(const String&in) const", asMETHOD(PostProcess, HasRenderTarget), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "bool set_parameters(XMLFile@+)", asMETHOD(PostProcess, LoadParameters), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "XMLFile@+ get_parameters() const", asMETHOD(PostProcess, GetParameters), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "void set_numPasses(uint)", asMETHOD(PostProcess, SetNumPasses), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "uint get_numPasses() const", asMETHOD(PostProcess, GetNumPasses), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "void set_shaderParameters(const String&in, const Vector4&in)", asMETHOD(PostProcess, SetShaderParameter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "Vector4 get_shaderParameters(const String&in) const", asMETHOD(PostProcess, GetShaderParameter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "void set_active(bool)", asMETHOD(PostProcess, SetActive), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "bool get_active() const", asMETHOD(PostProcess, IsActive), asCALL_THISCALL);
-    engine->RegisterObjectMethod("PostProcess", "PostProcessPass@+ get_passes(uint) const", asMETHOD(PostProcess, GetPass), asCALL_THISCALL);
-}
-
 static void RegisterModel(asIScriptEngine* engine)
 {
     RegisterResource<Model>(engine, "Model");
@@ -934,6 +881,9 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "uint get_numViewports() const", asMETHOD(Renderer, GetNumViewports), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool set_viewports(uint, Viewport@+)", asMETHOD(Renderer, SetViewport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "Viewport@+ get_viewports(uint) const", asMETHOD(Renderer, GetViewport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void SetDefaultRenderPath(XMLFile@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (XMLFile*), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_defaultRenderPath(RenderPath@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (RenderPath*), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "RenderPath@+ get_defaultRenderPath() const", asMETHOD(Renderer, GetDefaultRenderPath), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_specularLighting(bool)", asMETHOD(Renderer, SetSpecularLighting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool get_specularLighting() const", asMETHOD(Renderer, GetSpecularLighting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_textureAnisotropy(int)", asMETHOD(Renderer, SetTextureAnisotropy), asCALL_THISCALL);
@@ -1140,7 +1090,6 @@ void RegisterGraphicsAPI(asIScriptEngine* engine)
     RegisterCamera(engine);
     RegisterTextures(engine);
     RegisterMaterial(engine);
-    RegisterPostProcess(engine);
     RegisterModel(engine);
     RegisterAnimation(engine);
     RegisterDrawable(engine);

+ 0 - 302
Engine/Graphics/PostProcess.cpp

@@ -1,302 +0,0 @@
-//
-// Copyright (c) 2008-2013 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#include "Precompiled.h"
-#include "Graphics.h"
-#include "Log.h"
-#include "PostProcess.h"
-#include "StringUtils.h"
-#include "Texture2D.h"
-#include "XMLFile.h"
-
-#include "DebugNew.h"
-
-namespace Urho3D
-{
-
-TextureUnit ParseTextureUnitName(const String& name);
-
-PostProcessPass::PostProcessPass()
-{
-}
-
-PostProcessPass::~PostProcessPass()
-{
-}
-
-void PostProcessPass::SetVertexShader(const String& name)
-{
-    vertexShaderName_ = name;
-}
-
-void PostProcessPass::SetPixelShader(const String& name)
-{
-    pixelShaderName_ = name;
-}
-
-void PostProcessPass::SetTexture(TextureUnit unit, const String& name)
-{
-    if (unit < MAX_TEXTURE_UNITS)
-        textureNames_[unit] = name;
-}
-
-void PostProcessPass::SetShaderParameter(const String& name, const Vector4& value)
-{
-    shaderParameters_[StringHash(name)] = value;
-}
-
-void PostProcessPass::RemoveShaderParameter(const String& name)
-{
-    shaderParameters_.Erase(StringHash(name));
-}
-
-void PostProcessPass::SetOutput(const String& name)
-{
-    outputName_ = name;
-}
-
-SharedPtr<PostProcessPass> PostProcessPass::Clone()
-{
-    SharedPtr<PostProcessPass> clone(new PostProcessPass());
-    clone->vertexShaderName_ = vertexShaderName_;
-    clone->pixelShaderName_ = pixelShaderName_;
-    clone->shaderParameters_ = shaderParameters_;
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        clone->textureNames_[i] = textureNames_[i];
-    clone->outputName_ = outputName_;
-    
-    return clone;
-}
-
-const String& PostProcessPass::GetTexture(TextureUnit unit) const
-{
-    return unit < MAX_TEXTURE_UNITS ? textureNames_[unit] : String::EMPTY;
-}
-
-const Vector4& PostProcessPass::GetShaderParameter(const String& name) const
-{
-    HashMap<StringHash, Vector4>::ConstIterator i = shaderParameters_.Find(StringHash(name));
-    return i != shaderParameters_.End() ? i->second_ : Vector4::ZERO;
-}
-
-OBJECTTYPESTATIC(PostProcess);
-
-PostProcess::PostProcess(Context* context) :
-    Object(context),
-    active_(true)
-{
-}
-
-PostProcess::~PostProcess()
-{
-}
-
-bool PostProcess::LoadParameters(XMLFile* file)
-{
-    if (!file)
-        return false;
-    
-    XMLElement rootElem = file->GetRoot();
-    if (!rootElem)
-        return false;
-    
-    parameterSource_ = file;
-    renderTargets_.Clear();
-    shaderParameters_.Clear();
-    passes_.Clear();
-    
-    XMLElement rtElem = rootElem.GetChild("rendertarget");
-    while (rtElem)
-    {
-        String name = rtElem.GetAttribute("name");
-        
-        String formatName = rtElem.GetAttribute("format");
-        unsigned format = Graphics::GetFormat(formatName);
-        
-        bool sizeDivisor = false;
-        bool filtered = false;
-        unsigned width = 0;
-        unsigned height = 0;
-        
-        if (rtElem.HasAttribute("filter"))
-            filtered = rtElem.GetBool("filter");
-        if (rtElem.HasAttribute("size"))
-        {
-            IntVector2 size = rtElem.GetIntVector2("size");
-            width = size.x_;
-            height = size.y_;
-        }
-        if (rtElem.HasAttribute("width"))
-            width = rtElem.GetInt("width");
-        if (rtElem.HasAttribute("height"))
-            height = rtElem.GetInt("height");
-        if (rtElem.HasAttribute("sizedivisor"))
-        {
-            IntVector2 size = rtElem.GetIntVector2("sizedivisor");
-            width = size.x_;
-            height = size.y_;
-            sizeDivisor = true;
-        }
-        
-        CreateRenderTarget(name, width, height, format, sizeDivisor, filtered);
-        rtElem = rtElem.GetNext("rendertarget");
-    }
-    
-    XMLElement parameterElem = rootElem.GetChild("parameter");
-    while (parameterElem)
-    {
-        String name = parameterElem.GetAttribute("name");
-        Vector4 value = parameterElem.GetVector("value");
-        SetShaderParameter(name, value);
-        
-        parameterElem = parameterElem.GetNext("parameter");
-    }
-    
-    XMLElement passElem = rootElem.GetChild("pass");
-    while (passElem)
-    {
-        SharedPtr<PostProcessPass> pass(new PostProcessPass());
-        pass->SetVertexShader(passElem.GetAttribute("vs"));
-        pass->SetPixelShader(passElem.GetAttribute("ps"));
-        pass->SetOutput(passElem.GetAttribute("output"));
-        
-        XMLElement textureElem = passElem.GetChild("texture");
-        while (textureElem)
-        {
-            TextureUnit unit = TU_DIFFUSE;
-            if (textureElem.HasAttribute("unit"))
-            {
-                String unitName = textureElem.GetAttributeLower("unit");
-                if (unitName.Length() > 1)
-                {
-                    unit = ParseTextureUnitName(unitName);
-                    if (unit >= MAX_TEXTURE_UNITS)
-                        LOGERROR("Unknown texture unit " + unitName);
-                }
-                else
-                    unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_TEXTURE_UNITS - 1);
-            }
-            if (unit < MAX_TEXTURE_UNITS)
-            {
-                String name = textureElem.GetAttribute("name");
-                pass->SetTexture(unit, name);
-            }
-            
-            textureElem = textureElem.GetNext("texture");
-        }
-        
-        XMLElement parameterElem = passElem.GetChild("parameter");
-        while (parameterElem)
-        {
-            String name = parameterElem.GetAttribute("name");
-            Vector4 value = parameterElem.GetVector("value");
-            pass->SetShaderParameter(name, value);
-            
-            parameterElem = parameterElem.GetNext("parameter");
-        }
-        
-        passes_.Push(pass);
-        passElem = passElem.GetNext("pass");
-    }
-    
-    return true;
-}
-
-void PostProcess::SetNumPasses(unsigned passes)
-{
-    passes_.Resize(passes);
-    
-    for (unsigned i = 0; i < passes_.Size(); ++i)
-    {
-        if (!passes_[i])
-            passes_[i] = new PostProcessPass();
-    }
-}
-
-bool PostProcess::CreateRenderTarget(const String& name, unsigned width, unsigned height, unsigned format, bool sizeDivisor, bool filtered)
-{
-    if (name.Trimmed().Empty())
-        return false;
-    
-    RenderTargetInfo target;
-    target.name_ = name;
-    target.format_ = format;
-    target.size_ = IntVector2(width, height),
-    target.sizeDivisor_ = sizeDivisor;
-    target.filtered_ = filtered;
-    
-    renderTargets_[StringHash(name)] = target;
-    return true;
-}
-
-void PostProcess::RemoveRenderTarget(const String& name)
-{
-    renderTargets_.Erase(StringHash(name));
-}
-
-void PostProcess::SetShaderParameter(const String& name, const Vector4& value)
-{
-    shaderParameters_[StringHash(name)] = value;
-}
-
-void PostProcess::RemoveShaderParameter(const String& name)
-{
-    shaderParameters_.Erase(StringHash(name));
-}
-
-void PostProcess::SetActive(bool active)
-{
-    active_ = active;
-}
-
-SharedPtr<PostProcess> PostProcess::Clone()
-{
-    SharedPtr<PostProcess> clone(new PostProcess(context_));
-    clone->passes_.Resize(passes_.Size());
-    for (unsigned i = 0; i < passes_.Size(); ++i)
-        clone->passes_[i] = passes_[i]->Clone();
-    clone->renderTargets_ = renderTargets_;
-    clone->active_ = active_;
-    
-    return clone;
-}
-
-PostProcessPass* PostProcess::GetPass(unsigned index) const
-{
-    if (index < passes_.Size())
-        return passes_[index];
-    else
-        return 0;
-}
-
-bool PostProcess::HasRenderTarget(const String& name) const
-{
-    return renderTargets_.Contains(StringHash(name));
-}
-
-const Vector4& PostProcess::GetShaderParameter(const String& name) const
-{
-    HashMap<StringHash, Vector4>::ConstIterator i = shaderParameters_.Find(StringHash(name));
-    return i != shaderParameters_.End() ? i->second_ : Vector4::ZERO;
-}
-
-}

+ 0 - 145
Engine/Graphics/PostProcess.h

@@ -1,145 +0,0 @@
-//
-// Copyright (c) 2008-2013 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "GraphicsDefs.h"
-#include "Viewport.h"
-
-namespace Urho3D
-{
-
-class Texture2D;
-class XMLFile;
-
-/// Post-processing effect pass.
-class PostProcessPass : public RefCounted
-{
-public:
-    /// Construct.
-    PostProcessPass();
-    /// Destruct.
-    ~PostProcessPass();
-    
-    /// Set vertex shader name.
-    void SetVertexShader(const String& name);
-    /// Set pixel shader name.
-    void SetPixelShader(const String& name);
-    /// Set texture name. This can be a named rendertarget, or a texture resource name.
-    void SetTexture(TextureUnit unit, const String& name);
-    /// Set shader parameter.
-    void SetShaderParameter(const String& name, const Vector4& value);
-    /// Remove shader parameter.
-    void RemoveShaderParameter(const String& name);
-    /// Set output rendertarget name.
-    void SetOutput(const String& name);
-    /// Clone the post-process pass.
-    SharedPtr<PostProcessPass> Clone();
-    
-    /// Return vertex shader name.
-    const String& GetVertexShader() const { return vertexShaderName_; }
-    /// Return pixel shader name.
-    const String& GetPixelShader() const { return pixelShaderName_; }
-    /// Return texture name.
-    const String& GetTexture(TextureUnit unit) const;
-    /// Return all texture names.
-    const String* GetTextures() const { return &textureNames_[0]; }
-    /// Return shader parameter.
-    const Vector4& GetShaderParameter(const String& name) const;
-    /// Return all shader parameters.
-    const HashMap<StringHash, Vector4>& GetShaderParameters() const { return shaderParameters_; }
-    /// Return output rendertarget name.
-    const String& GetOutput() const { return outputName_; }
-    
-private:
-    /// Vertex shader name.
-    String vertexShaderName_;
-    /// Pixel shader name.
-    String pixelShaderName_;
-    /// Textures.
-    String textureNames_[MAX_TEXTURE_UNITS];
-    /// %Shader parameters.
-    HashMap<StringHash, Vector4> shaderParameters_;
-    /// Output rendertarget name.
-    String outputName_;
-};
-
-/// Post-processing effect.
-class PostProcess : public Object
-{
-    OBJECT(PostProcess);
-    
-public:
-    /// Construct.
-    PostProcess(Context* context);
-    /// Destruct.
-    ~PostProcess();
-    
-    /// Load parameters from an XML file. Return true if successful.
-    bool LoadParameters(XMLFile* file);
-    /// Set number of passes.
-    void SetNumPasses(unsigned passes);
-    /// Create a rendertarget. Width and height are either absolute pixels or viewport size divisors. Return true if successful.
-    bool CreateRenderTarget(const String& name, unsigned width, unsigned height, unsigned format, bool sizeDivisor, bool filtered);
-    /// Remove a rendertarget.
-    void RemoveRenderTarget(const String& name);
-    /// Set global shader parameter.
-    void SetShaderParameter(const String& name, const Vector4& value);
-    /// Remove global shader parameter.
-    void RemoveShaderParameter(const String& name);
-    /// Set active flag.
-    void SetActive(bool active);
-    /// Clone the post-process.
-    SharedPtr<PostProcess> Clone();
-    
-    /// Return parameter XML file.
-    XMLFile* GetParameters() const { return parameterSource_; }
-    /// Return number of passes.
-    unsigned GetNumPasses() const { return passes_.Size(); }
-    /// Return pass by index.
-    PostProcessPass* GetPass(unsigned index) const;
-    /// Return if has a specific rendertarget.
-    bool HasRenderTarget(const String& name) const;
-    /// Return all rendertargets.
-    const HashMap<StringHash, RenderTargetInfo>& GetRenderTargets() const { return renderTargets_; }
-    /// Return shader parameter.
-    const Vector4& GetShaderParameter(const String& name) const;
-    /// Return all global shader parameters.
-    const HashMap<StringHash, Vector4>& GetShaderParameters() const { return shaderParameters_; }
-    
-    /// Return active flag.
-    bool IsActive() const { return active_; }
-    
-private:
-    /// Parameter XML file.
-    SharedPtr<XMLFile> parameterSource_;
-    /// Rendertargets.
-    HashMap<StringHash, RenderTargetInfo> renderTargets_;
-    /// Global shader parameters.
-    HashMap<StringHash, Vector4> shaderParameters_;
-    /// Effect passes.
-    Vector<SharedPtr<PostProcessPass> > passes_;
-    /// Active flag.
-    bool active_;
-};
-
-}

+ 275 - 0
Engine/Graphics/RenderPath.cpp

@@ -0,0 +1,275 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Graphics.h"
+#include "RenderPath.h"
+#include "StringUtils.h"
+#include "XMLFile.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+static const String commandTypeNames[] =
+{
+    "none",
+    "clear",
+    "scenepass",
+    "quad",
+    "forwardlights",
+    "lightvolumes",
+    ""
+};
+
+static const String sortModeNames[] =
+{
+    "fronttoback"
+    "backtofront",
+    ""
+};
+
+TextureUnit ParseTextureUnitName(const String& name);
+
+void RenderTargetInfo::LoadParameters(const XMLElement& element)
+{
+    name_ = element.GetAttribute("name");
+    tag_ = element.GetAttribute("tag");
+    if (element.HasAttribute("active"))
+        active_ = element.GetBool("active");
+    
+    String formatName = element.GetAttribute("format");
+    format_ = Graphics::GetFormat(formatName);
+    
+    if (element.HasAttribute("filter"))
+        filtered_ = element.GetBool("filter");
+   
+    if (element.HasAttribute("size"))
+        size_ = element.GetIntVector2("size");
+    if (element.HasAttribute("sizedivisor"))
+    {
+        size_ = element.GetIntVector2("sizedivisor");
+        sizeMode_ = SIZE_VIEWPORTDIVISOR;
+    }
+    if (element.HasAttribute("rtsizedivisor"))
+    {
+        size_ = element.GetIntVector2("rtsizedivisor");
+        sizeMode_ = SIZE_RENDERTARGETDIVISOR;
+    }
+    
+    if (element.HasAttribute("width"))
+        size_.x_ = element.GetInt("width");
+    if (element.HasAttribute("height"))
+        size_.y_ = element.GetInt("height");
+}
+
+void RenderPathCommand::LoadParameters(const XMLElement& element)
+{
+    type_ = (RenderCommandType)GetStringListIndex(element.GetAttributeLower("type"), commandTypeNames, CMD_NONE);
+    tag_ = element.GetAttribute("tag");
+    if (element.HasAttribute("active"))
+        active_ = element.GetBool("active");
+    
+    switch (type_)
+    {
+    case CMD_CLEAR:
+        if (element.HasAttribute("color"))
+        {
+            clearFlags_ |= CLEAR_COLOR;
+            // Mark fog color with negative values
+            if (element.GetAttributeLower("color") == "fog")
+                useFogColor_ = true;
+            else
+                clearColor_ = element.GetColor("color");
+        }
+        if (element.HasAttribute("depth"))
+        {
+            clearFlags_ |= CLEAR_DEPTH;
+            clearDepth_ = element.GetFloat("depth");
+        }
+        if (element.HasAttribute("stencil"))
+        {
+            clearFlags_ |= CLEAR_STENCIL;
+            clearStencil_ = element.GetInt("stencil");
+        }
+        break;
+        
+    case CMD_SCENEPASS:
+        pass_ = StringHash(element.GetAttribute("pass"));
+        sortMode_ = (RenderCommandSortMode)GetStringListIndex(element.GetAttributeLower("sort"), sortModeNames, SORT_FRONTTOBACK);
+        if (element.HasAttribute("marktostencil"))
+            markToStencil_ = element.GetBool("marktostencil");
+        if (element.HasAttribute("vertexlights"))
+            vertexLights_ = element.GetBool("vertexlights");
+        if (element.HasAttribute("usescissor"))
+            useScissor_ = element.GetBool("usescissor");
+        break;
+        
+    case CMD_LIGHTVOLUMES:
+    case CMD_QUAD:
+        vertexShaderName_ = element.GetAttribute("vs");
+        pixelShaderName_ = element.GetAttribute("ps");
+        if (type_ == CMD_QUAD)
+        {
+            XMLElement parameterElem = element.GetChild("parameter");
+            while (parameterElem)
+            {
+                String name = parameterElem.GetAttribute("name");
+                Vector4 value = parameterElem.GetVector("value");
+                shaderParameters_[StringHash(name)] = value;
+                
+                parameterElem = parameterElem.GetNext("parameter");
+            }
+        }
+        break;
+    }
+    
+    // By default use 1 output, which is the viewport
+    outputs_.Push("viewport");
+    if (element.HasAttribute("output"))
+        outputs_[0] = element.GetAttribute("output");
+    // Check for defining multiple outputs
+    XMLElement outputElem = element.GetChild("output");
+    while (outputElem)
+    {
+        unsigned index = outputElem.GetInt("index");
+        if (index < MAX_RENDERTARGETS)
+        {
+            if (index >= outputs_.Size())
+                outputs_.Resize(index + 1);
+            outputs_[index] = outputElem.GetAttribute("name");
+        }
+        outputElem = outputElem.GetNext("output");
+    }
+    
+    XMLElement textureElem = element.GetChild("texture");
+    while (textureElem)
+    {
+        TextureUnit unit = TU_DIFFUSE;
+        if (textureElem.HasAttribute("unit"))
+        {
+            String unitName = textureElem.GetAttributeLower("unit");
+            if (unitName.Length() > 1)
+                unit = ParseTextureUnitName(unitName);
+            else
+                unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_TEXTURE_UNITS - 1);
+        }
+        if (unit < MAX_TEXTURE_UNITS)
+        {
+            String name = textureElem.GetAttribute("name");
+            textureNames_[unit] = name;
+        }
+        
+        textureElem = textureElem.GetNext("texture");
+    }
+}
+
+RenderPath::RenderPath()
+{
+}
+
+RenderPath::~RenderPath()
+{
+}
+
+SharedPtr<RenderPath> RenderPath::Clone()
+{
+    SharedPtr<RenderPath> newRenderPath(new RenderPath());
+    newRenderPath->renderTargets_ = renderTargets_;
+    newRenderPath->commands_ = commands_;
+    return newRenderPath;
+}
+
+bool RenderPath::LoadParameters(XMLFile* file)
+{
+    renderTargets_.Clear();
+    commands_.Clear();
+    
+    return Append(file);
+}
+
+bool RenderPath::Append(XMLFile* file)
+{
+    if (!file)
+        return false;
+    
+    XMLElement rootElem = file->GetRoot();
+    if (!rootElem)
+        return false;
+    
+    XMLElement rtElem = rootElem.GetChild("rendertarget");
+    while (rtElem)
+    {
+        RenderTargetInfo info;
+        info.LoadParameters(rtElem);
+        if (!info.name_.Trimmed().Empty())
+            renderTargets_.Push(info);
+        
+        rtElem = rtElem.GetNext("rendertarget");
+    }
+    
+    XMLElement cmdElem = rootElem.GetChild("command");
+    while (cmdElem)
+    {
+        RenderPathCommand cmd;
+        cmd.LoadParameters(cmdElem);
+        if (cmd.type_ != CMD_NONE)
+            commands_.Push(cmd);
+        
+        cmdElem = cmdElem.GetNext("command");
+    }
+    
+    return true;
+}
+
+void RenderPath::SetActive(const String& tag, bool active)
+{
+    for (unsigned i = 0; i < renderTargets_.Size(); ++i)
+    {
+        if (!renderTargets_[i].tag_.Compare(tag, false))
+            renderTargets_[i].active_ = active;
+    }
+    
+    for (unsigned i = 0; i < commands_.Size(); ++i)
+    {
+        if (!commands_[i].tag_.Compare(tag, false))
+            commands_[i].active_ = active;
+    }
+}
+
+void RenderPath::ToggleActive(const String& tag)
+{
+    for (unsigned i = 0; i < renderTargets_.Size(); ++i)
+    {
+        if (!renderTargets_[i].tag_.Compare(tag, false))
+            renderTargets_[i].active_ = !renderTargets_[i].active_;
+    }
+    
+    for (unsigned i = 0; i < commands_.Size(); ++i)
+    {
+        if (!commands_[i].tag_.Compare(tag, false))
+            commands_[i].active_ = !commands_[i].active_;
+    }
+}
+
+}

+ 173 - 0
Engine/Graphics/RenderPath.h

@@ -0,0 +1,173 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "GraphicsDefs.h"
+#include "RefCounted.h"
+
+namespace Urho3D
+{
+
+class XMLElement;
+class XMLFile;
+
+/// Rendering path command types.
+enum RenderCommandType
+{
+    CMD_NONE = 0,
+    CMD_CLEAR,
+    CMD_SCENEPASS,
+    CMD_QUAD,
+    CMD_FORWARDLIGHTS,
+    CMD_LIGHTVOLUMES,
+    CMD_UNKNOWN
+};
+
+/// Rendering path sorting modes.
+enum RenderCommandSortMode
+{
+    SORT_FRONTTOBACK = 0,
+    SORT_BACKTOFRONT
+};
+
+/// Rendertarget size mode.
+enum RenderTargetSizeMode
+{
+    SIZE_ABSOLUTE = 0,
+    SIZE_RENDERTARGETDIVISOR,
+    SIZE_VIEWPORTDIVISOR
+};
+
+/// Rendertarget definition.
+struct RenderTargetInfo
+{
+    /// Construct.
+    RenderTargetInfo() :
+        size_(IntVector2::ZERO),
+        sizeMode_(SIZE_ABSOLUTE),
+        active_(true),
+        filtered_(false)
+    {
+    }
+    
+    /// Read from an XML element.
+    void LoadParameters(const XMLElement& element);
+    
+    /// Name.
+    String name_;
+    /// Tag name.
+    String tag_;
+    /// Texture format.
+    unsigned format_;
+    /// Size.
+    IntVector2 size_;
+    /// Size mode.
+    RenderTargetSizeMode sizeMode_;
+    /// Active flag.
+    bool active_;
+    /// Filtering flag.
+    bool filtered_;
+};
+
+/// Rendering path command.
+struct RenderPathCommand
+{
+    /// Construct.
+    RenderPathCommand() :
+        clearFlags_(0),
+        active_(true),
+        useFogColor_(false),
+        markToStencil_(false),
+        useScissor_(false),
+        vertexLights_(false)
+    {
+    }
+    
+    /// Read from an XML element.
+    void LoadParameters(const XMLElement& element);
+    
+    /// Tag name.
+    String tag_;
+    /// Command type.
+    RenderCommandType type_;
+    /// Sorting mode.
+    RenderCommandSortMode sortMode_;
+    /// Scene pass hash.
+    StringHash pass_;
+    /// Clear flags.
+    unsigned clearFlags_;
+    /// Clear color.
+    Color clearColor_;
+    /// Clear depth.
+    float clearDepth_;
+    /// Clear stencil value.
+    unsigned clearStencil_;
+    /// Active flag.
+    bool active_;
+    /// Use fog color for clearing.
+    bool useFogColor_;
+    /// Mark to stencil flag.
+    bool markToStencil_;
+    /// Vertex lights flag.
+    bool vertexLights_;
+    /// Scissor optimization flag.
+    bool useScissor_;
+    /// Vertex shader name.
+    String vertexShaderName_;
+    /// Pixel shader name.
+    String pixelShaderName_;
+    /// Textures.
+    String textureNames_[MAX_TEXTURE_UNITS];
+    /// %Shader parameters.
+    HashMap<StringHash, Vector4> shaderParameters_;
+    /// Output rendertarget names.
+    Vector<String> outputs_;
+};
+
+/// Rendering path definition.
+class RenderPath : public RefCounted
+{
+public:
+    /// Construct.
+    RenderPath();
+    /// Destruct.
+    ~RenderPath();
+    
+    /// Clone the rendering path.
+    SharedPtr<RenderPath> Clone();
+    /// Read from an XML file. Return true if successful.
+    bool LoadParameters(XMLFile* file);
+    /// Append data from an XML file. Return true if successful.
+    bool Append(XMLFile* file);
+    /// Activate/inactivate commands and rendertargets by tag.
+    void SetActive(const String& tag, bool active);
+    /// Toggle activation of commands and rendertargets by tag.
+    void ToggleActive(const String& tag);
+    
+    /// Rendertargets.
+    Vector<RenderTargetInfo> renderTargets_;
+    /// Rendering commands.
+    Vector<RenderPathCommand> commands_;
+};
+
+}

+ 19 - 5
Engine/Graphics/Renderer.cpp

@@ -35,6 +35,7 @@
 #include "Octree.h"
 #include "Profiler.h"
 #include "Renderer.h"
+#include "RenderPath.h"
 #include "ResourceCache.h"
 #include "Scene.h"
 #include "Shader.h"
@@ -258,7 +259,6 @@ Renderer::Renderer(Context* context) :
     Object(context),
     defaultZone_(new Zone(context)),
     quadDirLight_(new Light(context)),
-    defaultRenderPathName_("CoreData/RenderPaths/Forward.xml"),
     textureAnisotropy_(4),
     textureFilterMode_(FILTER_TRILINEAR),
     textureQuality_(QUALITY_HIGH),
@@ -328,11 +328,17 @@ bool Renderer::SetViewport(unsigned index, Viewport* viewport)
     return true;
 }
 
-void Renderer::SetDefaultRenderPathName(const String& name)
+void Renderer::SetDefaultRenderPath(RenderPath* renderPath)
 {
-    String nameTrimmed = name.Trimmed();
-    if (!nameTrimmed.Empty())
-        defaultRenderPathName_ = name;
+    if (renderPath)
+        defaultRenderPath_ = renderPath;
+}
+
+void Renderer::SetDefaultRenderPath(XMLFile* xmlFile)
+{
+    SharedPtr<RenderPath> newRenderPath(new RenderPath());
+    if (newRenderPath->LoadParameters(xmlFile))
+        defaultRenderPath_ = newRenderPath;
 }
 
 void Renderer::SetSpecularLighting(bool enable)
@@ -488,6 +494,11 @@ Viewport* Renderer::GetViewport(unsigned index) const
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
 }
 
+RenderPath* Renderer::GetDefaultRenderPath() const
+{
+    return defaultRenderPath_;
+}
+
 ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const
 {
     return GetShader(VS, name, checkExists);
@@ -1428,6 +1439,9 @@ void Renderer::Initialize()
     if (!defaultMaterial_)
         defaultMaterial_ = new Material(context_);
     
+    defaultRenderPath_ = new RenderPath();
+    defaultRenderPath_->LoadParameters(cache->GetResource<XMLFile>("CoreData/RenderPaths/Forward.xml"));
+    
     CreateGeometries();
     CreateInstancingBuffer();
     

+ 9 - 6
Engine/Graphics/Renderer.h

@@ -40,6 +40,7 @@ class Pass;
 class Technique;
 class Octree;
 class Graphics;
+class RenderPath;
 class RenderSurface;
 class ResourceCache;
 class Skeleton;
@@ -166,8 +167,10 @@ public:
     void SetNumViewports(unsigned num);
     /// Set a viewport. Return true if successful.
     bool SetViewport(unsigned index, Viewport* viewport);
-    /// Set default renderpath resource name.
-    void SetDefaultRenderPathName(const String& name);
+    /// Set default renderpath.
+    void SetDefaultRenderPath(RenderPath* renderPath);
+    /// Set default renderpath from an XML file.
+    void SetDefaultRenderPath(XMLFile* file);
     /// Set specular lighting on/off.
     void SetSpecularLighting(bool enable);
     /// Set texture anisotropy.
@@ -209,8 +212,8 @@ public:
     unsigned GetNumViewports() const { return viewports_.Size(); }
     /// Return viewport.
     Viewport* GetViewport(unsigned index) const;
-    /// Return default renderpath resource name.
-    const String& GetDefaultRenderPathName() const { return defaultRenderPathName_; }
+    /// Return default renderpath.
+    RenderPath* GetDefaultRenderPath() const;
     /// Return whether specular lighting is enabled.
     bool GetSpecularLighting() const { return specularLighting_; }
     /// Return whether drawing shadows is enabled.
@@ -373,6 +376,8 @@ private:
     WeakPtr<Graphics> graphics_;
     /// Resource cache subsystem.
     WeakPtr<ResourceCache> cache_;
+    /// Default renderpath.
+    SharedPtr<RenderPath> defaultRenderPath_;
     /// Default zone.
     SharedPtr<Zone> defaultZone_;
     /// Directional light for drawing fullscreen quads.
@@ -427,8 +432,6 @@ private:
     HashSet<Technique*> shaderErrorDisplayed_;
     /// Mutex for shadow camera allocation.
     Mutex rendererMutex_;
-    /// Default renderpath resource name.
-    String defaultRenderPathName_;
     /// Base directory for shaders.
     String shaderPath_;
     /// Frame info for rendering.

+ 247 - 245
Engine/Graphics/View.cpp

@@ -31,8 +31,8 @@
 #include "OcclusionBuffer.h"
 #include "Octree.h"
 #include "Renderer.h"
+#include "RenderPath.h"
 #include "ResourceCache.h"
-#include "PostProcess.h"
 #include "Profiler.h"
 #include "Scene.h"
 #include "ShaderVariation.h"
@@ -299,23 +299,15 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     cameraNode_ = camera->GetNode();
     renderTarget_ = renderTarget;
     depthStencil_ = GetDepthStencil(renderTarget_);
-    renderPath_ = &viewport->GetRenderPath();
-    
-    // Get active post-processing effects on the viewport
-    const Vector<SharedPtr<PostProcess> >& postProcesses = viewport->GetPostProcesses();
-    postProcesses_.Clear();
-    for (Vector<SharedPtr<PostProcess> >::ConstIterator i = postProcesses.Begin(); i != postProcesses.End(); ++i)
-    {
-        PostProcess* effect = i->Get();
-        if (effect && effect->IsActive())
-            postProcesses_.Push(*i);
-    }
+    renderPath_ = viewport->GetRenderPath();
     
     // Make sure that all necessary batch queues exist
     scenePasses_.Clear();
     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
         const RenderPathCommand& command = renderPath_->commands_[i];
+        if (!command.active_)
+            continue;
         
         if (command.type_ == CMD_SCENEPASS)
         {
@@ -340,6 +332,9 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
         const RenderPathCommand& command = renderPath_->commands_[i];
+        if (!command.active_)
+            continue;
+        
         if (command.type_ == CMD_LIGHTVOLUMES)
         {
             renderer_->GetLightVolumeShaders(lightVS_, lightPS_, command.vertexShaderName_, command.pixelShaderName_);
@@ -438,6 +433,10 @@ void View::Render()
     // Allocate screen buffers for post-processing and blitting as necessary
     AllocateScreenBuffers();
     
+    // Initialize screenbuffer indices to use for read and write (pingponging)
+    writeBuffer_ = 0;
+    readBuffer_ = 0;
+    
     // Forget parameter sources from the previous view
     graphics_->ClearParameterSources();
     
@@ -485,14 +484,9 @@ void View::Render()
     graphics_->SetViewTexture(0);
     graphics_->ResetStreamFrequencies();
     
-    // Run post-processes or framebuffer blitting now
-    if (screenBuffers_.Size())
-    {
-        if (postProcesses_.Size())
-            RunPostProcesses();
-        else
-            BlitFramebuffer();
-    }
+    // Run framebuffer blitting if necessary
+    if (screenBuffers_.Size() && currentRenderTarget_ != renderTarget_)
+        BlitFramebuffer(static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture()), renderTarget_, depthStencil_, true);
     
     // If this is a main view, draw the associated debug geometry now
     if (!renderTarget_)
@@ -991,6 +985,8 @@ void View::UpdateGeometries()
         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         {
             const RenderPathCommand& command = renderPath_->commands_[i];
+            if (!command.active_)
+                continue;
             
             if (command.type_ == CMD_SCENEPASS)
             {
@@ -1135,18 +1131,6 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
 
 void View::ExecuteRenderPathCommands()
 {
-    // If using hardware multisampling with post-processing, render to the backbuffer first and then resolve
-    if (screenBuffers_.Size())
-    {
-        usedRenderTarget_ = screenBuffers_[0]->GetRenderSurface();
-        usedDepthStencil_ = GetDepthStencil(usedRenderTarget_);
-    }
-    else
-    {
-        usedRenderTarget_ = renderTarget_;
-        usedDepthStencil_ = depthStencil_;
-    }
-    
     // If not reusing shadowmaps, render all of them first
     if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
     {
@@ -1164,9 +1148,56 @@ void View::ExecuteRenderPathCommands()
     {
         PROFILE(RenderCommands);
         
+        unsigned lastCommandIndex = 0;
+        for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
+        {
+            const RenderPathCommand& command = renderPath_->commands_[i];
+            if (!command.active_)
+                continue;
+            lastCommandIndex = i;
+        }
+        
         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         {
             const RenderPathCommand& command = renderPath_->commands_[i];
+            if (!command.active_)
+                continue;
+            
+            bool pingpong = false;
+            
+            // If command writes and reads the target at same time, pingpong automatically
+            if (CheckViewportRead(command))
+            {
+                readBuffer_ = writeBuffer_;
+                if (!command.outputs_[0].Compare("viewport", false))
+                {
+                    pingpong = true;
+                    ++writeBuffer_;
+                    if (writeBuffer_ >= screenBuffers_.Size())
+                        writeBuffer_ = 0;
+                }
+            }
+            
+            // Check which rendertarget will be used on this pass
+            if (screenBuffers_.Size())
+            {
+                currentRenderTarget_ = screenBuffers_[writeBuffer_]->GetRenderSurface();
+                currentDepthStencil_ = screenBufferDepthStencil_;
+            }
+            else
+            {
+                currentRenderTarget_ = renderTarget_;
+                currentDepthStencil_ = depthStencil_;
+            }
+            
+            // Optimization: if the last command is a quad with output to the viewport, do not use the screenbuffers,
+            // but the viewport directly. This saves the extra copy
+            if (screenBuffers_.Size() && i == lastCommandIndex && command.type_ == CMD_QUAD && command.outputs_.Size() == 1 &&
+                !command.outputs_[0].Compare("viewport", false))
+            {
+                currentRenderTarget_ = renderTarget_;
+                currentDepthStencil_ = depthStencil_;
+            }
             
             switch (command.type_)
             {
@@ -1175,7 +1206,7 @@ void View::ExecuteRenderPathCommands()
                     PROFILE(ClearRenderTarget);
                     
                     Color clearColor = command.clearColor_;
-                    if (clearColor.r_ < 0.0f)
+                    if (command.useFogColor_)
                         clearColor = farClipZone_->GetFogColor();
                     
                     SetRenderTargets(command);
@@ -1184,6 +1215,14 @@ void View::ExecuteRenderPathCommands()
                 break;
                 
             case CMD_SCENEPASS:
+                // If this is a pingpong scene pass which reads the existing viewport contents, must copy it first
+                // in case the whole viewport is not overwritten
+                if (pingpong)
+                {
+                    BlitFramebuffer(screenBuffers_[readBuffer_], screenBuffers_[writeBuffer_]->GetRenderSurface(),
+                        screenBufferDepthStencil_, false);
+                }
+                
                 if (!batchQueues_[command.pass_].IsEmpty())
                 {
                     PROFILE(RenderScenePass);
@@ -1194,6 +1233,16 @@ void View::ExecuteRenderPathCommands()
                 }
                 break;
                 
+            case CMD_QUAD:
+                {
+                    PROFILE(RenderQuad);
+                    
+                    SetRenderTargets(command);
+                    SetTextures(command);
+                    RenderQuad(command);
+                }
+                break;
+                
             case CMD_FORWARDLIGHTS:
                 // Render shadow maps + opaque objects' additive lighting
                 if (!lightQueues_.Empty())
@@ -1274,7 +1323,7 @@ void View::SetRenderTargets(const RenderPathCommand& command)
     while (index < command.outputs_.Size())
     {
         if (!command.outputs_[index].Compare("viewport", false))
-            graphics_->SetRenderTarget(index, usedRenderTarget_);
+            graphics_->SetRenderTarget(index, currentRenderTarget_);
         else
         {
             StringHash nameHash(command.outputs_[index]);
@@ -1293,7 +1342,7 @@ void View::SetRenderTargets(const RenderPathCommand& command)
         ++index;
     }
     
-    graphics_->SetDepthStencil(usedDepthStencil_);
+    graphics_->SetDepthStencil(currentDepthStencil_);
     graphics_->SetViewport(viewRect_);
 }
 
@@ -1303,31 +1352,110 @@ void View::SetTextures(const RenderPathCommand& command)
     
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
     {
-        if (!command.textureNames_[i].Empty())
+        if (command.textureNames_[i].Empty())
+            continue;
+        
+        // Bind the rendered output
+        if (!command.textureNames_[i].Compare("viewport", false))
+        {
+            graphics_->SetTexture(i, screenBuffers_[readBuffer_]);
+            continue;
+        }
+        
+        // Bind a rendertarget
+        HashMap<StringHash, Texture2D*>::ConstIterator j = renderTargets_.Find(StringHash(command.textureNames_[i]));
+        if (j != renderTargets_.End())
+        {
+            graphics_->SetTexture(i, j->second_);
+            continue;
+        }
+        
+        // Bind a texture from the resource system
+        if (cache)
         {
-            /// \todo allow referring to the current pingpong target
-            HashMap<StringHash, Texture2D*>::ConstIterator j = renderTargets_.Find(StringHash(command.textureNames_[i]));
-            if (j != renderTargets_.End())
-                graphics_->SetTexture(i, j->second_);
+            Texture2D* texture = cache->GetResource<Texture2D>(command.textureNames_[i]);
+            if (texture)
+                graphics_->SetTexture(i, texture);
             else
             {
-                if (cache)
-                {
-                    Texture2D* texture = cache->GetResource<Texture2D>(command.textureNames_[i]);
-                    if (texture)
-                        graphics_->SetTexture(i, texture);
-                    else
-                    {
-                        // If requesting a texture fails, clear the texture name to prevent redundant attempts
-                        RenderPathCommand& cmdWrite = const_cast<RenderPathCommand&>(command);
-                        cmdWrite.textureNames_[i] = String();
-                    }
-                }
+                // If requesting a texture fails, clear the texture name to prevent redundant attempts
+                RenderPathCommand& cmdWrite = const_cast<RenderPathCommand&>(command);
+                cmdWrite.textureNames_[i] = String();
             }
         }
     }
 }
 
+void View::RenderQuad(const RenderPathCommand& command)
+{
+    // Set shaders & shader parameters and textures
+    graphics_->SetShaders(renderer_->GetVertexShader(command.vertexShaderName_), renderer_->GetPixelShader(command.pixelShaderName_));
+    
+    const HashMap<StringHash, Vector4>& parameters = command.shaderParameters_;
+    for (HashMap<StringHash, Vector4>::ConstIterator k = parameters.Begin(); k != parameters.End(); ++k)
+        graphics_->SetShaderParameter(k->first_, k->second_);
+    
+    float rtWidth = (float)rtSize_.x_;
+    float rtHeight = (float)rtSize_.y_;
+    float widthRange = 0.5f * viewSize_.x_ / rtWidth;
+    float heightRange = 0.5f * viewSize_.y_ / rtHeight;
+    
+    #ifdef USE_OPENGL
+    Vector4 bufferUVOffset(((float)viewRect_.left_) / rtWidth + widthRange,
+        1.0f - (((float)viewRect_.top_) / rtHeight + heightRange), widthRange, heightRange);
+    #else
+    Vector4 bufferUVOffset((0.5f + (float)viewRect_.left_) / rtWidth + widthRange,
+        (0.5f + (float)viewRect_.top_) / rtHeight + heightRange, widthRange, heightRange);
+    #endif
+    
+    graphics_->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
+    graphics_->SetShaderParameter(PSP_GBUFFERINVSIZE, Vector4(1.0f / rtWidth, 1.0f / rtHeight, 0.0f, 0.0f));
+    
+    // Set per-rendertarget inverse size / offset shader parameters as necessary
+    for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
+    {
+        const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
+        if (!rtInfo.active_)
+            continue;
+        
+        StringHash nameHash(rtInfo.name_);
+        if (!renderTargets_.Contains(nameHash))
+            continue;
+        
+        String invSizeName = rtInfo.name_ + "InvSize";
+        String offsetsName = rtInfo.name_ + "Offsets";
+        float width = (float)renderTargets_[nameHash]->GetWidth();
+        float height = (float)renderTargets_[nameHash]->GetHeight();
+        
+        graphics_->SetShaderParameter(StringHash(invSizeName), Vector4(1.0f / width, 1.0f / height, 0.0f, 0.0f));
+        #ifdef USE_OPENGL
+        graphics_->SetShaderParameter(StringHash(offsetsName), Vector4::ZERO);
+        #else
+        graphics_->SetShaderParameter(StringHash(offsetsName), Vector4(0.5f / width, 0.5f / height, 0.0f, 0.0f));
+        #endif
+    }
+    
+    graphics_->SetBlendMode(BLEND_REPLACE);
+    graphics_->SetDepthTest(CMP_ALWAYS);
+    graphics_->SetDepthWrite(false);
+    graphics_->SetFillMode(FILL_SOLID);
+    graphics_->SetScissorTest(false);
+    graphics_->SetStencilTest(false);
+    
+    DrawFullscreenQuad(false);
+}
+
+bool View::CheckViewportRead(const RenderPathCommand& command)
+{
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        if (!command.textureNames_[i].Empty() && !command.textureNames_[i].Compare("viewport", false))
+            return true;
+    }
+    
+    return false;
+}
+
 void View::AllocateScreenBuffers()
 {
     unsigned neededBuffers = 0;
@@ -1339,37 +1467,69 @@ void View::AllocateScreenBuffers()
         neededBuffers = 1;
     #endif
     
-    unsigned postProcessPasses = 0;
-    for (unsigned i = 0; i < postProcesses_.Size(); ++i)
-        postProcessPasses += postProcesses_[i]->GetNumPasses();
-    
-    // If more than one post-process pass, need 2 buffers for ping-pong rendering
-    if (postProcessPasses)
-        neededBuffers = Min((int)postProcessPasses, 2);
-    
     unsigned format = Graphics::GetRGBFormat();
     #ifdef USE_OPENGL
     if (deferred_)
         format = Graphics::GetRGBAFormat();
     #endif
     
-    // Allocate screen buffers with filtering active in case the post-processing effects need that
+    // Check for commands which read the rendered scene and allocate a buffer for each, up to 2 maximum for pingpong
+    /// \todo If the last copy is optimized away, this allocates an extra buffer unnecessarily
+    bool hasViewportRead = false;
+    bool hasViewportReadWrite = false;
+    
+    for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
+    {
+        const RenderPathCommand& command = renderPath_->commands_[i];
+        if (!command.active_)
+            continue;
+        if (CheckViewportRead(command))
+        {
+            hasViewportRead = true;
+            if (!command.outputs_[0].Compare("viewport", false))
+                hasViewportReadWrite = true;
+        }
+    }
+    if (hasViewportRead && !neededBuffers)
+        neededBuffers = 1;
+    if (hasViewportReadWrite)
+        neededBuffers = 2;
+    
+    // Allocate screen buffers with filtering active in case the quad commands need that
     for (unsigned i = 0; i < neededBuffers; ++i)
         screenBuffers_.Push(renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, format, true));
     
+    screenBufferDepthStencil_ = neededBuffers ? GetDepthStencil(screenBuffers_[0]->GetRenderSurface()) : (RenderSurface*)0;
+    
     // Allocate extra render targets defined by the rendering path
     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
     {
         const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
+        if (!rtInfo.active_)
+            continue;
+        
         unsigned width = rtInfo.size_.x_;
         unsigned height = rtInfo.size_.y_;
         if (!width || !height)
         {
-            width = rtSize_.x_;
-            height = rtSize_.y_;
+            if (rtInfo.sizeMode_ != SIZE_VIEWPORTDIVISOR)
+            {
+                width = rtSize_.x_;
+                height = rtSize_.y_;
+            }
+            else
+            {
+                width = viewSize_.x_;
+                height = viewSize_.y_;
+            }
         }
         
-        if (rtInfo.sizeDivisor_)
+        if (rtInfo.sizeMode_ == SIZE_VIEWPORTDIVISOR)
+        {
+            width = viewSize_.x_ / width;
+            height = viewSize_.y_ / height;
+        }
+        if (rtInfo.sizeMode_ == SIZE_RENDERTARGETDIVISOR)
         {
             width = rtSize_.x_ / width;
             height = rtSize_.y_ / height;
@@ -1379,17 +1539,18 @@ void View::AllocateScreenBuffers()
     }
 }
 
-void View::BlitFramebuffer()
+void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, RenderSurface* depthStencil, bool depthWrite)
 {
-    // Blit the final image to destination rendertarget
-    /// \todo Depth is reset to far plane, so geometry drawn after the view can not be depth tested
     graphics_->SetBlendMode(BLEND_REPLACE);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthWrite(true);
+    graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetScissorTest(false);
     graphics_->SetStencilTest(false);
-    graphics_->SetRenderTarget(0, renderTarget_);
-    graphics_->SetDepthStencil(GetDepthStencil(renderTarget_));
+    graphics_->SetRenderTarget(0, destination);
+    for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
+        graphics_->SetRenderTarget(i, (RenderSurface*)0);
+    graphics_->SetDepthStencil(depthStencil);
     graphics_->SetViewport(viewRect_);
     
     String shaderName = "CopyFramebuffer";
@@ -1409,167 +1570,30 @@ void View::BlitFramebuffer()
     #endif
     
     graphics_->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
-    graphics_->SetTexture(TU_DIFFUSE, screenBuffers_[0]);
+    graphics_->SetTexture(TU_DIFFUSE, source);
     DrawFullscreenQuad(false);
 }
 
-void View::RunPostProcesses()
+void View::DrawFullscreenQuad(bool nearQuad)
 {
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Light* quadDirLight = renderer_->GetQuadDirLight();
+    Geometry* geometry = renderer_->GetLightGeometry(quadDirLight);
     
-    // Ping-pong buffer indices for read and write
-    unsigned readRtIndex = 0;
-    unsigned writeRtIndex = screenBuffers_.Size() - 1;
+    Matrix3x4 model = Matrix3x4::IDENTITY;
+    Matrix4 projection = Matrix4::IDENTITY;
     
-    graphics_->SetBlendMode(BLEND_REPLACE);
-    graphics_->SetDepthTest(CMP_ALWAYS);
-    graphics_->SetScissorTest(false);
-    graphics_->SetStencilTest(false);
+    #ifdef USE_OPENGL
+    model.m23_ = nearQuad ? -1.0f : 1.0f;
+    #else
+    model.m23_ = nearQuad ? 0.0f : 1.0f;
+    #endif
     
-    for (unsigned i = 0; i < postProcesses_.Size(); ++i)
-    {
-        PostProcess* effect = postProcesses_[i];
-        
-        // For each effect, rendertargets can be re-used. Allocate them now
-        renderer_->SaveScreenBufferAllocations();
-        const HashMap<StringHash, RenderTargetInfo>& renderTargetInfos = effect->GetRenderTargets();
-        HashMap<StringHash, Texture2D*> renderTargets;
-        for (HashMap<StringHash, RenderTargetInfo>::ConstIterator j = renderTargetInfos.Begin(); j !=
-            renderTargetInfos.End(); ++j)
-        {
-            unsigned width = j->second_.size_.x_;
-            unsigned height = j->second_.size_.y_;
-            if (j->second_.sizeDivisor_)
-            {
-                width = viewSize_.x_ / width;
-                height = viewSize_.y_ / height;
-            }
-            
-            renderTargets[j->first_] = renderer_->GetScreenBuffer(width, height, j->second_.format_, j->second_.filtered_);
-        }
-        
-        // Run each effect pass
-        for (unsigned j = 0; j < effect->GetNumPasses(); ++j)
-        {
-            PostProcessPass* pass = effect->GetPass(j);
-            bool lastPass = (i == postProcesses_.Size() - 1) && (j == effect->GetNumPasses() - 1);
-            bool swapBuffers = false;
-            
-            // Write depth on the last pass only
-            graphics_->SetDepthWrite(lastPass);
-            
-            // Set output rendertarget
-            RenderSurface* rt = 0;
-            String output = pass->GetOutput().ToLower();
-            if (output == "viewport")
-            {
-                if (!lastPass)
-                {
-                    rt = screenBuffers_[writeRtIndex]->GetRenderSurface();
-                    swapBuffers = true;
-                }
-                else
-                    rt = renderTarget_;
-                
-                graphics_->SetRenderTarget(0, rt);
-                graphics_->SetDepthStencil(GetDepthStencil(rt));
-                graphics_->SetViewport(viewRect_);
-            }
-            else
-            {
-                HashMap<StringHash, Texture2D*>::ConstIterator k = renderTargets.Find(StringHash(output));
-                if (k != renderTargets.End())
-                    rt = k->second_->GetRenderSurface();
-                else
-                    continue; // Skip pass if rendertarget can not be found
-                
-                graphics_->SetRenderTarget(0, rt);
-                graphics_->SetDepthStencil(GetDepthStencil(rt));
-                graphics_->SetViewport(IntRect(0, 0, rt->GetWidth(), rt->GetHeight()));
-            }
-            
-            // Set shaders, shader parameters and textures
-            graphics_->SetShaders(renderer_->GetVertexShader(pass->GetVertexShader()),
-                renderer_->GetPixelShader(pass->GetPixelShader()));
-            
-            const HashMap<StringHash, Vector4>& globalParameters = effect->GetShaderParameters();
-            for (HashMap<StringHash, Vector4>::ConstIterator k = globalParameters.Begin(); k != globalParameters.End(); ++k)
-                graphics_->SetShaderParameter(k->first_, k->second_);
-            
-            const HashMap<StringHash, Vector4>& parameters = pass->GetShaderParameters();
-            for (HashMap<StringHash, Vector4>::ConstIterator k = parameters.Begin(); k != parameters.End(); ++k)
-                graphics_->SetShaderParameter(k->first_, k->second_);
-            
-            float rtWidth = (float)rtSize_.x_;
-            float rtHeight = (float)rtSize_.y_;
-            float widthRange = 0.5f * viewSize_.x_ / rtWidth;
-            float heightRange = 0.5f * viewSize_.y_ / rtHeight;
-            
-            #ifdef USE_OPENGL
-            Vector4 bufferUVOffset(((float)viewRect_.left_) / rtWidth + widthRange,
-                1.0f - (((float)viewRect_.top_) / rtHeight + heightRange), widthRange, heightRange);
-            #else
-            Vector4 bufferUVOffset((0.5f + (float)viewRect_.left_) / rtWidth + widthRange,
-                (0.5f + (float)viewRect_.top_) / rtHeight + heightRange, widthRange, heightRange);
-            #endif
-            
-            graphics_->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
-            graphics_->SetShaderParameter(PSP_GBUFFERINVSIZE, Vector4(1.0f / rtWidth, 1.0f / rtHeight, 0.0f, 0.0f));
-            
-            // Set per-rendertarget inverse size / offset shader parameters as necessary
-            for (HashMap<StringHash, RenderTargetInfo>::ConstIterator k = renderTargetInfos.Begin(); k !=
-                renderTargetInfos.End(); ++k)
-            {
-                String invSizeName = k->second_.name_ + "InvSize";
-                String offsetsName = k->second_.name_ + "Offsets";
-                float width = (float)renderTargets[k->first_]->GetWidth();
-                float height = (float)renderTargets[k->first_]->GetHeight();
-                
-                graphics_->SetShaderParameter(StringHash(invSizeName), Vector4(1.0f / width, 1.0f / height, 0.0f, 0.0f));
-                #ifdef USE_OPENGL
-                graphics_->SetShaderParameter(StringHash(offsetsName), Vector4::ZERO);
-                #else
-                graphics_->SetShaderParameter(StringHash(offsetsName), Vector4(0.5f / width, 0.5f / height, 0.0f, 0.0f));
-                #endif
-            }
-            
-            const String* textureNames = pass->GetTextures();
-            for (unsigned k = 0; k < MAX_TEXTURE_UNITS; ++k)
-            {
-                if (!textureNames[k].Empty())
-                {
-                    // Texture may either refer to a rendertarget or to a texture resource
-                    if (!textureNames[k].Compare("viewport", false))
-                        graphics_->SetTexture(k, screenBuffers_[readRtIndex]);
-                    else
-                    {
-                        HashMap<StringHash, Texture2D*>::ConstIterator l = renderTargets.Find(StringHash(textureNames[k]));
-                        if (l != renderTargets.End())
-                            graphics_->SetTexture(k, l->second_);
-                        else
-                        {
-                            // If requesting a texture fails, clear the texture name to prevent redundant attempts
-                            Texture2D* texture = cache->GetResource<Texture2D>(textureNames[k]);
-                            if (texture)
-                                graphics_->SetTexture(k, texture);
-                            else
-                                pass->SetTexture((TextureUnit)k, String());
-                        }
-                    }
-                }
-            }
-            
-            /// \todo Draw a near plane quad optionally
-            DrawFullscreenQuad(false);
-            
-            // Swap the ping-pong buffer sides now if necessary
-            if (swapBuffers)
-                Swap(readRtIndex, writeRtIndex);
-        }
-        
-        // Forget the rendertargets allocated during this effect
-        renderer_->RestoreScreenBufferAllocations();
-    }
+    graphics_->SetCullMode(CULL_NONE);
+    graphics_->SetShaderParameter(VSP_MODEL, model);
+    graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
+    graphics_->ClearTransformSources();
+    
+    geometry->Draw(graphics_);
 }
 
 void View::UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera)
@@ -2397,28 +2421,6 @@ void View::SetupLightVolumeBatch(Batch& batch)
     graphics_->SetStencilTest(true, CMP_NOTEQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0, light->GetLightMask());
 }
 
-void View::DrawFullscreenQuad(bool nearQuad)
-{
-    Light* quadDirLight = renderer_->GetQuadDirLight();
-    Geometry* geometry = renderer_->GetLightGeometry(quadDirLight);
-    
-    Matrix3x4 model = Matrix3x4::IDENTITY;
-    Matrix4 projection = Matrix4::IDENTITY;
-    
-    #ifdef USE_OPENGL
-    model.m23_ = nearQuad ? -1.0f : 1.0f;
-    #else
-    model.m23_ = nearQuad ? 0.0f : 1.0f;
-    #endif
-    
-    graphics_->SetCullMode(CULL_NONE);
-    graphics_->SetShaderParameter(VSP_MODEL, model);
-    graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
-    graphics_->ClearTransformSources();
-    
-    geometry->Draw(graphics_);
-}
-
 void View::RenderShadowMap(const LightBatchQueue& queue)
 {
     PROFILE(RenderShadowMap);

+ 22 - 16
Engine/Graphics/View.h

@@ -37,12 +37,12 @@ class Light;
 class Drawable;
 class OcclusionBuffer;
 class Octree;
+class RenderPath;
 class RenderSurface;
 class Technique;
 class Texture2D;
 class Viewport;
 class Zone;
-struct RenderPath;
 struct RenderPathCommand;
 struct WorkItem;
 
@@ -145,12 +145,16 @@ private:
     void SetRenderTargets(const RenderPathCommand& command);
     /// Set textures for current render command.
     void SetTextures(const RenderPathCommand& command);
-    /// Allocate needed screen buffers for post-processing and/or framebuffer blitting.
+    /// Perform a quad rendering command.
+    void RenderQuad(const RenderPathCommand& command);
+    /// Check if a command reads the rendered scene.
+    bool CheckViewportRead(const RenderPathCommand& command);
+    /// Allocate needed screen buffers.
     void AllocateScreenBuffers();
-    /// Blit the framebuffer to destination. Used in OpenGL deferred rendering modes.
-    void BlitFramebuffer();
-    /// Run post-processing effects.
-    void RunPostProcesses();
+    /// Blit the viewport from one surface to another.
+    void BlitFramebuffer(Texture2D* source, RenderSurface* destination, RenderSurface* depthStencil, bool depthWrite);
+    /// Draw a fullscreen quad. Shaders and renderstates must have been set beforehand.
+    void DrawFullscreenQuad(bool nearQuad);
     /// Query for occluders as seen from a camera.
     void UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera);
     /// Draw occluders to occlusion buffer.
@@ -191,11 +195,9 @@ private:
     void PrepareInstancingBuffer();
     /// Set up a light volume rendering batch.
     void SetupLightVolumeBatch(Batch& batch);
-    /// Draw a full screen quad (either near or far.) Shaders must have been set beforehand.
-    void DrawFullscreenQuad(bool nearQuad);
     /// Render a shadow map.
     void RenderShadowMap(const LightBatchQueue& queue);
-    /// Return the proper depth-stencil surface to use for a rendertarget.
+    /// Return the proper depth-stencil surface to use for a rendertarget. Should only be called once per rendertarget.
     RenderSurface* GetDepthStencil(RenderSurface* renderTarget);
     
     /// Graphics subsystem.
@@ -216,14 +218,16 @@ private:
     Zone* farClipZone_;
     /// Occlusion buffer for the main camera.
     OcclusionBuffer* occlusionBuffer_;
-    /// Color rendertarget to use.
+    /// Destination color rendertarget.
     RenderSurface* renderTarget_;
-    /// Depth stencil to use.
+    /// Destination depth stencil.
     RenderSurface* depthStencil_;
+    /// Screenbuffers' depth stencil.
+    RenderSurface* screenBufferDepthStencil_;
     /// Effective color rendertarget to use, may be different if screenbuffers are used.
-    RenderSurface* usedRenderTarget_;
+    RenderSurface* currentRenderTarget_;
     /// Effective depth stencil to use, may be different if screenbuffers are used.
-    RenderSurface* usedDepthStencil_;
+    RenderSurface* currentDepthStencil_;
     /// Viewport rectangle.
     IntRect viewRect_;
     /// Viewport size.
@@ -234,6 +238,10 @@ private:
     FrameInfo frame_;
     /// Combined bounding box of visible geometries.
     BoundingBox sceneBox_;
+    /// Write screenbuffer index.
+    unsigned writeBuffer_;
+    /// Read screenbuffer index.
+    unsigned readBuffer_;
     /// Minimum Z value of the visible scene.
     float minZ_;
     /// Maximum Z value of the visible scene.
@@ -252,9 +260,7 @@ private:
     bool deferred_;
     /// Renderpath.
     const RenderPath* renderPath_;
-    /// Post-processing effects.
-    Vector<SharedPtr<PostProcess> > postProcesses_;
-    /// Intermediate screen buffers used in postprocessing and OpenGL deferred framebuffer blit.
+    /// Intermediate screen buffers used in pingpong copies and OpenGL deferred framebuffer blit.
     PODVector<Texture2D*> screenBuffers_;
     /// Per-thread octree query results.
     Vector<PODVector<Drawable*> > tempDrawables_;

+ 17 - 262
Engine/Graphics/Viewport.cpp

@@ -24,11 +24,12 @@
 #include "Camera.h"
 #include "Graphics.h"
 #include "Log.h"
-#include "PostProcess.h"
 #include "Renderer.h"
+#include "RenderPath.h"
 #include "ResourceCache.h"
 #include "Scene.h"
 #include "StringUtils.h"
+#include "Viewport.h"
 #include "XMLFile.h"
 
 #include "DebugNew.h"
@@ -36,35 +37,16 @@
 namespace Urho3D
 {
 
-TextureUnit ParseTextureUnitName(const String& name);
-
-static const String commandTypeNames[] =
-{
-    "clear",
-    "scenepass",
-    "quad",
-    "forwardlights",
-    "lightvolumes",
-    ""
-};
-
-static const String sortModeNames[] =
-{
-    "fronttoback"
-    "backtofront",
-    ""
-};
-
 OBJECTTYPESTATIC(Viewport);
 
 Viewport::Viewport(Context* context) :
     Object(context),
     rect_(IntRect::ZERO)
 {
-    SetRenderPath(0);
+    SetRenderPath((RenderPath*)0);
 }
 
-Viewport::Viewport(Context* context, Scene* scene, Camera* camera, XMLFile* renderPath) :
+Viewport::Viewport(Context* context, Scene* scene, Camera* camera, RenderPath* renderPath) :
     Object(context),
     scene_(scene),
     camera_(camera),
@@ -73,7 +55,7 @@ Viewport::Viewport(Context* context, Scene* scene, Camera* camera, XMLFile* rend
     SetRenderPath(renderPath);
 }
 
-Viewport::Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, XMLFile* renderPath) :
+Viewport::Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, RenderPath* renderPath) :
     Object(context),
     scene_(scene),
     camera_(camera),
@@ -82,26 +64,6 @@ Viewport::Viewport(Context* context, Scene* scene, Camera* camera, const IntRect
     SetRenderPath(renderPath);
 }
 
-Viewport::Viewport(Context* context, Scene* scene, Camera* camera, const Vector<SharedPtr<PostProcess> >& postProcesses, XMLFile* renderPath) :
-    Object(context),
-    scene_(scene),
-    camera_(camera),
-    rect_(IntRect::ZERO),
-    postProcesses_(postProcesses)
-{
-    SetRenderPath(renderPath);
-}
-
-Viewport::Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, const Vector<SharedPtr<PostProcess> >& postProcesses, XMLFile* renderPath) :
-    Object(context),
-    scene_(scene),
-    camera_(camera),
-    rect_(rect),
-    postProcesses_(postProcesses)
-{
-    SetRenderPath(renderPath);
-}
-
 Viewport::~Viewport()
 {
 }
@@ -121,230 +83,23 @@ void Viewport::SetRect(const IntRect& rect)
     rect_ = rect;
 }
 
-bool Viewport::SetRenderPath(XMLFile* file)
+void Viewport::SetRenderPath(RenderPath* renderPath)
 {
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    Renderer* renderer = GetSubsystem<Renderer>();
-    if (!file && cache && renderer)
-        file = cache->GetResource<XMLFile>(renderer->GetDefaultRenderPathName());
-    if (!file)
-        return false;
-    
-    XMLElement rootElem = file->GetRoot();
-    if (!rootElem)
-        return false;
-    
-    renderPath_.renderTargets_.Clear();
-    renderPath_.commands_.Clear();
-    
-    XMLElement rtElem = rootElem.GetChild("rendertarget");
-    while (rtElem)
-    {
-        String name = rtElem.GetAttribute("name");
-        
-        String formatName = rtElem.GetAttribute("format");
-        unsigned format = Graphics::GetFormat(formatName);
-        
-        bool sizeDivisor = false;
-        bool filtered = false;
-        unsigned width = 0;
-        unsigned height = 0;
-        
-        if (rtElem.HasAttribute("filter"))
-            filtered = rtElem.GetBool("filter");
-        if (rtElem.HasAttribute("size"))
-        {
-            IntVector2 size = rtElem.GetIntVector2("size");
-            width = size.x_;
-            height = size.y_;
-        }
-        if (rtElem.HasAttribute("width"))
-            width = rtElem.GetInt("width");
-        if (rtElem.HasAttribute("height"))
-            height = rtElem.GetInt("height");
-        if (rtElem.HasAttribute("sizedivisor"))
-        {
-            IntVector2 size = rtElem.GetIntVector2("sizedivisor");
-            width = size.x_;
-            height = size.y_;
-            sizeDivisor = true;
-        }
-        
-        if (!name.Trimmed().Empty())
-        {
-            RenderTargetInfo rtInfo;
-            rtInfo.name_ = name;
-            rtInfo.format_ = format;
-            rtInfo.size_ = IntVector2(width, height),
-            rtInfo.sizeDivisor_ = sizeDivisor;
-            rtInfo.filtered_ = filtered;
-            renderPath_.renderTargets_.Push(rtInfo);
-        }
-        
-        rtElem = rtElem.GetNext("rendertarget");
-    }
-    
-    XMLElement commandElem = rootElem.GetChild("command");
-    while (commandElem)
-    {
-        RenderCommandType type = (RenderCommandType)GetStringListIndex(commandElem.GetAttributeLower("type"), commandTypeNames,
-            CMD_UNKNOWN);
-        if (type != CMD_UNKNOWN)
-        {
-            RenderPathCommand command;
-            command.type_ = type;
-            
-            switch (type)
-            {
-            case CMD_CLEAR:
-                if (commandElem.HasAttribute("color"))
-                {
-                    command.clearFlags_ |= CLEAR_COLOR;
-                    // Mark fog color with negative values
-                    if (commandElem.GetAttributeLower("color") == "fog")
-                        command.clearColor_ = Color(-1.0f, -1.0f, -1.0f, -1.0f);
-                    else
-                        command.clearColor_ = commandElem.GetColor("color");
-                }
-                if (commandElem.HasAttribute("depth"))
-                {
-                    command.clearFlags_ |= CLEAR_DEPTH;
-                    command.clearDepth_ = commandElem.GetFloat("depth");
-                }
-                if (commandElem.HasAttribute("stencil"))
-                {
-                    command.clearFlags_ |= CLEAR_STENCIL;
-                    command.clearStencil_ = commandElem.GetInt("stencil");
-                }
-                break;
-            
-            case CMD_SCENEPASS:
-                command.pass_ = StringHash(commandElem.GetAttribute("pass"));
-                command.sortMode_ = (RenderCommandSortMode)GetStringListIndex(commandElem.GetAttributeLower("sort"), sortModeNames, SORT_FRONTTOBACK);
-                if (commandElem.HasAttribute("marktostencil"))
-                    command.markToStencil_ = commandElem.GetBool("marktostencil");
-                if (commandElem.HasAttribute("vertexlights"))
-                    command.vertexLights_ = commandElem.GetBool("vertexlights");
-                if (commandElem.HasAttribute("usescissor"))
-                    command.useScissor_ = commandElem.GetBool("usescissor");
-                break;
-                
-            case CMD_LIGHTVOLUMES:
-            case CMD_QUAD:
-                command.vertexShaderName_ = commandElem.GetAttribute("vs");
-                command.pixelShaderName_ = commandElem.GetAttribute("ps");
-                if (type == CMD_QUAD)
-                {
-                    XMLElement parameterElem = commandElem.GetChild("parameter");
-                    while (parameterElem)
-                    {
-                        String name = parameterElem.GetAttribute("name");
-                        Vector4 value = parameterElem.GetVector("value");
-                        command.shaderParameters_[StringHash(name)] = value;
-                        
-                        parameterElem = parameterElem.GetNext("parameter");
-                    }
-                }
-                break;
-            }
-            
-            // By default use 1 output, which is the viewport
-            command.outputs_.Push("viewport");
-            if (commandElem.HasAttribute("output"))
-                command.outputs_[0] = commandElem.GetAttribute("output");
-            // Check for defining multiple outputs
-            XMLElement outputElem = commandElem.GetChild("output");
-            while (outputElem)
-            {
-                unsigned index = outputElem.GetInt("index");
-                if (index < MAX_RENDERTARGETS)
-                {
-                    if (index >= command.outputs_.Size())
-                        command.outputs_.Resize(index + 1);
-                    command.outputs_[index] = outputElem.GetAttribute("name");
-                }
-                outputElem = outputElem.GetNext("output");
-            }
-            
-            XMLElement textureElem = commandElem.GetChild("texture");
-            while (textureElem)
-            {
-                TextureUnit unit = TU_DIFFUSE;
-                if (textureElem.HasAttribute("unit"))
-                {
-                    String unitName = textureElem.GetAttributeLower("unit");
-                    if (unitName.Length() > 1)
-                    {
-                        unit = ParseTextureUnitName(unitName);
-                        if (unit >= MAX_TEXTURE_UNITS)
-                            LOGERROR("Unknown texture unit " + unitName);
-                    }
-                    else
-                        unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_TEXTURE_UNITS - 1);
-                }
-                if (unit < MAX_TEXTURE_UNITS)
-                {
-                    String name = textureElem.GetAttribute("name");
-                    command.textureNames_[unit] = name;
-                }
-                
-                textureElem = textureElem.GetNext("texture");
-            }
-            
-            renderPath_.commands_.Push(command);
-        }
-        
-        commandElem = commandElem.GetNext("command");
-    }
-    
-    return true;
-}
-
-void Viewport::AddPostProcess(PostProcess* effect)
-{
-    if (!effect)
-        return;
-    
-    SharedPtr<PostProcess> effectPtr(effect);
-    if (!postProcesses_.Contains(effectPtr))
-        postProcesses_.Push(effectPtr);
-}
-
-void Viewport::InsertPostProcess(unsigned index, PostProcess* effect)
-{
-    if (!effect)
-        return;
-    
-    SharedPtr<PostProcess> effectPtr(effect);
-    if (postProcesses_.Contains(effectPtr))
-        return;
-    
-    if (index >= postProcesses_.Size())
-        postProcesses_.Push(effectPtr);
+    if (renderPath)
+        renderPath_ = renderPath;
     else
-        postProcesses_.Insert(index, effectPtr);
-}
-
-void Viewport::RemovePostProcess(PostProcess* effect)
-{
-    for (Vector<SharedPtr<PostProcess> >::Iterator i = postProcesses_.Begin(); i != postProcesses_.End(); ++i)
     {
-        if (i->Get() == effect)
-        {
-            postProcesses_.Erase(i);
-            return;
-        }
+        Renderer* renderer = GetSubsystem<Renderer>();
+        if (renderer)
+            renderPath_ = renderer->GetDefaultRenderPath();
     }
 }
 
-void Viewport::RemovePostProcess(unsigned index)
-{
-    postProcesses_.Erase(index);
-}
-
-void Viewport::RemoveAllPostProcesses()
+void Viewport::SetRenderPath(XMLFile* file)
 {
-    postProcesses_.Clear();
+    SharedPtr<RenderPath> newRenderPath(new RenderPath());
+    if (newRenderPath->LoadParameters(file))
+        renderPath_ = newRenderPath;
 }
 
 Scene* Viewport::GetScene() const
@@ -357,9 +112,9 @@ Camera* Viewport::GetCamera() const
     return camera_;
 }
 
-PostProcess* Viewport::GetPostProcess(unsigned index) const
+RenderPath* Viewport::GetRenderPath() const
 {
-    return index < postProcesses_.Size() ? postProcesses_[index] : (PostProcess*)0;
+    return renderPath_;
 }
 
 }

+ 10 - 115
Engine/Graphics/Viewport.h

@@ -29,95 +29,10 @@ namespace Urho3D
 {
 
 class Camera;
-class PostProcess;
+class RenderPath;
 class Scene;
 class XMLFile;
 
-/// Rendering path command types.
-enum RenderCommandType
-{
-    CMD_CLEAR = 0,
-    CMD_SCENEPASS,
-    CMD_QUAD,
-    CMD_FORWARDLIGHTS,
-    CMD_LIGHTVOLUMES,
-    CMD_UNKNOWN
-};
-
-/// Rendering path sorting modes.
-enum RenderCommandSortMode
-{
-    SORT_FRONTTOBACK = 0,
-    SORT_BACKTOFRONT
-};
-
-/// Rendertarget definition.
-struct RenderTargetInfo
-{
-    /// Name.
-    String name_;
-    /// Texture format.
-    unsigned format_;
-    /// Size.
-    IntVector2 size_;
-    /// Divisor mode flag.
-    bool sizeDivisor_;
-    /// Filtering flag.
-    bool filtered_;
-};
-
-/// Rendering path command.
-struct RenderPathCommand
-{
-    RenderPathCommand() :
-        clearFlags_(0),
-        markToStencil_(false),
-        useScissor_(false),
-        vertexLights_(false)
-    {
-    }
-    
-    /// Command type.
-    RenderCommandType type_;
-    /// Sorting mode.
-    RenderCommandSortMode sortMode_;
-    /// Scene pass hash.
-    StringHash pass_;
-    /// Clear flags.
-    unsigned clearFlags_;
-    /// Clear color.
-    Color clearColor_;
-    /// Clear depth.
-    float clearDepth_;
-    /// Clear stencil value.
-    unsigned clearStencil_;
-    /// Mark to stencil flag.
-    bool markToStencil_;
-    /// Vertex lights flag.
-    bool vertexLights_;
-    /// Scissor optimization flag.
-    bool useScissor_;
-    /// Vertex shader name.
-    String vertexShaderName_;
-    /// Pixel shader name.
-    String pixelShaderName_;
-    /// Textures.
-    String textureNames_[MAX_TEXTURE_UNITS];
-    /// %Shader parameters.
-    HashMap<StringHash, Vector4> shaderParameters_;
-    /// Output rendertarget names.
-    Vector<String> outputs_;
-};
-
-/// Rendering path definition.
-struct RenderPath
-{
-    /// Rendertargets.
-    Vector<RenderTargetInfo> renderTargets_;
-    /// Rendering commands.
-    Vector<RenderPathCommand> commands_;
-};
-
 /// %Viewport definition either for a render surface or the backbuffer.
 class Viewport : public Object
 {
@@ -127,13 +42,9 @@ public:
     /// Construct with defaults.
     Viewport(Context* context);
     /// Construct with a full rectangle.
-    Viewport(Context* context, Scene* scene, Camera* camera, XMLFile* renderPath = 0);
+    Viewport(Context* context, Scene* scene, Camera* camera, RenderPath* renderPath = 0);
     /// Construct with a specified rectangle.
-    Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, XMLFile* renderPath = 0);
-    /// Construct with a full rectangle and post-processing effects.
-    Viewport(Context* context, Scene* scene, Camera* camera, const Vector<SharedPtr<PostProcess> >& postProcesses, XMLFile* renderPath = 0);
-    /// Construct with a specified rectangle and post-processing effects.
-    Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, const Vector<SharedPtr<PostProcess> >& postProcesses, XMLFile* renderPath = 0);
+    Viewport(Context* context, Scene* scene, Camera* camera, const IntRect& rect, RenderPath* renderPath = 0);
     /// Destruct.
     ~Viewport();
     
@@ -143,33 +54,19 @@ public:
     void SetCamera(Camera* camera);
     /// Set rectangle.
     void SetRect(const IntRect& rect);
-    /// Set rendering path. Return true if successful.
-    bool SetRenderPath(XMLFile* file);
-    /// Add a post-processing effect at the end of the chain.
-    void AddPostProcess(PostProcess* effect);
-    /// Insert a post-processing effect at position in the chain.
-    void InsertPostProcess(unsigned index, PostProcess* effect);
-    /// Remove a post-processing effect.
-    void RemovePostProcess(PostProcess* effect);
-    /// Remove a post-processing effect by index.
-    void RemovePostProcess(unsigned index);
-    /// Remove all post-processing effects.
-    void RemoveAllPostProcesses();
+    /// Set rendering path.
+    void SetRenderPath(RenderPath* path);
+    /// Set rendering path from an XML file.
+    void SetRenderPath(XMLFile* file);
     
     /// Return scene.
     Scene* GetScene() const;
     /// Return camera.
     Camera* GetCamera() const;
-    /// Return rendering path.
-    const RenderPath& GetRenderPath() const { return renderPath_; }
     /// Return rectangle.
     const IntRect& GetRect() const { return rect_; }
-    /// Return number of post-processing effects.
-    unsigned GetNumPostProcesses() const { return postProcesses_.Size(); }
-    /// Return post-processing effect at index.
-    PostProcess* GetPostProcess(unsigned index) const;
-    /// Return all post-processing effects.
-    const Vector<SharedPtr<PostProcess> >& GetPostProcesses() const { return postProcesses_; }
+    /// Return rendering path.
+    RenderPath* GetRenderPath() const;
     
 private:
     /// Scene pointer.
@@ -179,9 +76,7 @@ private:
     /// Viewport rectangle.
     IntRect rect_;
     /// Rendering path.
-    RenderPath renderPath_;
-    /// Post-processing effects.
-    Vector<SharedPtr<PostProcess> > postProcesses_;
+    SharedPtr<RenderPath> renderPath_;
 };
 
 }

+ 2 - 1
Engine/Resource/ResourceCache.cpp

@@ -44,7 +44,8 @@ static const String checkDirs[] = {
     "Music",
     "Objects",
     "Particle",
-    "PostProcess",
+	"PostProcess",
+    "RenderPaths",
     "Scenes",
     "Scripts",
     "Sounds",