Browse Source

Transition to xml-defined rendering path. Pass names changed. Likely caused a large number of regressions to postprocessing, texture rendering & multisampling, which need to be sorted out. The renderpath feature also needs to be documented.

Lasse Öörni 13 years ago
parent
commit
ffe65cf885
52 changed files with 626 additions and 654 deletions
  1. 20 0
      Bin/CoreData/RenderPaths/Deferred.xml
  2. 8 0
      Bin/CoreData/RenderPaths/Forward.xml
  3. 21 0
      Bin/CoreData/RenderPaths/Prepass.xml
  4. 1 1
      Bin/CoreData/Shaders/GLSL/Samplers.frag
  5. 4 4
      Bin/CoreData/Shaders/HLSL/Samplers.hlsl
  6. 2 2
      Bin/CoreData/Techniques/DiffAlpha.xml
  7. 2 2
      Bin/CoreData/Techniques/DiffEnvCubeAlpha.xml
  8. 2 2
      Bin/CoreData/Techniques/DiffLitParticleAlpha.xml
  9. 1 1
      Bin/CoreData/Techniques/DiffMultiply.xml
  10. 2 2
      Bin/CoreData/Techniques/DiffNormalAlpha.xml
  11. 2 2
      Bin/CoreData/Techniques/DiffNormalEnvCubeAlpha.xml
  12. 2 2
      Bin/CoreData/Techniques/DiffNormalSpecAlpha.xml
  13. 2 2
      Bin/CoreData/Techniques/DiffSpecAlpha.xml
  14. 1 1
      Bin/CoreData/Techniques/DiffUnlitAlpha.xml
  15. 1 1
      Bin/CoreData/Techniques/DiffVColAdd.xml
  16. 1 1
      Bin/CoreData/Techniques/DiffVColMultiply.xml
  17. 1 1
      Bin/CoreData/Techniques/DiffVColUnlitAlpha.xml
  18. 1 1
      Bin/CoreData/Techniques/NoTextureAdd.xml
  19. 2 2
      Bin/CoreData/Techniques/NoTextureAlpha.xml
  20. 2 2
      Bin/CoreData/Techniques/NoTextureEnvCubeAlpha.xml
  21. 1 1
      Bin/CoreData/Techniques/NoTextureMultiply.xml
  22. 1 1
      Bin/CoreData/Techniques/NoTextureUnlitAlpha.xml
  23. 1 1
      Bin/CoreData/Techniques/NoTextureVColAdd.xml
  24. 1 1
      Bin/CoreData/Techniques/NoTextureVColMultiply.xml
  25. 0 2
      Bin/Data/Scripts/Editor.as
  26. 0 10
      Bin/Data/Scripts/Editor/EditorSettings.as
  27. 0 3
      Bin/Data/Scripts/LightTest.as
  28. 7 10
      Bin/Data/Scripts/Physics.as
  29. 7 10
      Bin/Data/Scripts/Terrain.as
  30. 7 10
      Bin/Data/Scripts/TestScene.as
  31. 7 10
      Bin/Data/Scripts/TestSceneOld.as
  32. 0 32
      Bin/Data/UI/EditorSettingsDialog.xml
  33. 2 2
      Docs/GettingStarted.dox
  34. 8 4
      Docs/Reference.dox
  35. 1 9
      Engine/Engine/DebugHud.cpp
  36. 5 4
      Engine/Engine/Engine.cpp
  37. 0 7
      Engine/Engine/GraphicsAPI.cpp
  38. 2 0
      Engine/Graphics/Direct3D9/D3D9Graphics.cpp
  39. 1 1
      Engine/Graphics/Direct3D9/D3D9Shader.cpp
  40. 2 0
      Engine/Graphics/GraphicsDefs.cpp
  41. 6 4
      Engine/Graphics/GraphicsDefs.h
  42. 12 3
      Engine/Graphics/Material.cpp
  43. 2 0
      Engine/Graphics/OpenGL/OGLGraphics.cpp
  44. 6 6
      Engine/Graphics/PostProcess.cpp
  45. 1 1
      Engine/Graphics/PostProcess.h
  46. 38 68
      Engine/Graphics/Renderer.cpp
  47. 9 11
      Engine/Graphics/Renderer.h
  48. 271 334
      Engine/Graphics/View.cpp
  49. 46 19
      Engine/Graphics/View.h
  50. 86 54
      Engine/Graphics/Viewport.cpp
  51. 18 5
      Engine/Graphics/Viewport.h
  52. 0 2
      Urho3D/Urho3D.cpp

+ 20 - 0
Bin/CoreData/RenderPaths/Deferred.xml

@@ -0,0 +1,20 @@
+<renderpath>
+    <rendertarget name="albedo" format="rgba" />
+    <rendertarget name="normal" format="rgba" />
+    <rendertarget name="depth" format="lineardepth" />
+    <command type="clear" color="fog" depth="1.0" stencil="0" />
+    <command type="scenepass" pass="deferred" marktostencil="true" vertexlights="true" sort="fronttoback">
+        <output index="0" name="viewport" />
+        <output index="1" name="albedo" />
+        <output index="2" name="normal" />
+        <output index="3" name="depth" />
+    </command>
+    <command type="lightvolumes" vs="DeferredLight" ps="DeferredLight">
+        <texture unit="albedo" name="albedo" />
+        <texture unit="normal" name="normal" />
+        <texture unit="depth" name="depth" />
+    </command>
+    <command type="scenepass" pass="prealpha" sort="fronttoback" />
+    <command type="scenepass" pass="alpha" usescissor="true" vertexlights="true" sort="backtofront" />
+    <command type="scenepass" pass="postalpha" sort="backtofront" />
+</renderpath>

+ 8 - 0
Bin/CoreData/RenderPaths/Forward.xml

@@ -0,0 +1,8 @@
+<renderpath>
+    <command type="clear" color="fog" depth="1.0" stencil="0" />
+    <command type="scenepass" pass="base" vertexlights="true" sort="state" />
+    <command type="forwardlights" />
+    <command type="scenepass" pass="prealpha" sort="state" />
+    <command type="scenepass" pass="alpha" usescissor="true" vertexlights="true" sort="backtofront" />
+    <command type="scenepass" pass="postalpha" sort="backtofront" />
+</renderpath>

+ 21 - 0
Bin/CoreData/RenderPaths/Prepass.xml

@@ -0,0 +1,21 @@
+<renderpath>
+    <rendertarget name="light" format="rgba" />
+    <rendertarget name="normal" format="rgba" />
+    <rendertarget name="depth" format="lineardepth" />
+    <command type="clear" color="fog" depth="1.0" stencil="0" />
+    <command type="scenepass" pass="prepass" marktostencil="true" sort="fronttoback">
+        <output index="0" name="normal" />
+        <output index="1" name="depth" />
+    </command>
+    <command type="clear" color="0 0 0 0" output="light" />
+    <command type="lightvolumes" vs="PrepassLight" ps="PrepassLight" output="light">
+        <texture unit="normal" name="normal" />
+        <texture unit="depth" name="depth" />
+    </command>
+    <command type="scenepass" pass="material" vertexlights="true" sort="fronttoback">
+        <texture unit="light" name="light" />
+    </command>
+    <command type="scenepass" pass="prealpha" sort="fronttoback" />
+    <command type="scenepass" pass="alpha" usescissor="true" vertexlights="true" sort="backtofront" />
+    <command type="scenepass" pass="postalpha" sort="backtofront" />
+</renderpath>

+ 1 - 1
Bin/CoreData/Shaders/GLSL/Samplers.frag

@@ -8,11 +8,11 @@ uniform samplerCube sEnvCubeMap;
 uniform sampler2D sLightRampMap;
 uniform sampler2D sLightSpotMap;
 uniform samplerCube sLightCubeMap;
+#ifndef GL_ES
 uniform sampler2D sAlbedoBuffer;
 uniform sampler2D sNormalBuffer;
 uniform sampler2D sDepthBuffer;
 uniform sampler2D sLightBuffer;
-#ifndef GL_ES
 uniform sampler2DShadow sShadowMap;
 uniform samplerCube sFaceSelectCubeMap;
 uniform samplerCube sIndirectionCubeMap;

+ 4 - 4
Bin/CoreData/Shaders/HLSL/Samplers.hlsl

@@ -1,6 +1,8 @@
 sampler2D sDiffMap : register(S0);
 samplerCUBE sDiffCubeMap : register(S0);
+sampler2D sAlbedoBuffer : register(S0);
 sampler2D sNormalMap : register(S1);
+sampler2D sNormalBuffer : register(S1);
 sampler2D sSpecMap : register(S2);
 sampler2D sEmissiveMap : register(S3);
 sampler2D sEnvMap : register(S4);
@@ -11,10 +13,8 @@ samplerCUBE sLightCubeMap : register(S6);
 sampler2D sShadowMap : register(S7);
 samplerCUBE sFaceSelectCubeMap : register(S8);
 samplerCUBE sIndirectionCubeMap : register(S9);
-sampler2D sAlbedoBuffer : register(S0);
-sampler2D sNormalBuffer : register(S1);
-sampler2D sDepthBuffer : register(S2);
-sampler2D sLightBuffer : register(S5);
+sampler2D sDepthBuffer : register(S10);
+sampler2D sLightBuffer : register(S11);
 
 float4 Sample(sampler2D map, float2 texCoord)
 {

+ 2 - 2
Bin/CoreData/Techniques/DiffAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffEnvCubeAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid_EnvCube" ps="LitSolid_DiffEnvCube" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid_EnvCube" ps="LitSolid_DiffEnvCube" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffLitParticleAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitParticle" ps="LitParticle_Diff" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitParticle" ps="LitParticle_Diff" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitParticle" ps="LitParticle_Diff" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitParticle" ps="LitParticle_Diff" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/DiffMultiply.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit" ps="Unlit_Diff" depthwrite="false" blend="multiply" />
+    <pass name="alpha" vs="Unlit" ps="Unlit_Diff" depthwrite="false" blend="multiply" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffNormalAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid_Normal" ps="LitSolid_DiffNormal" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid_Normal" ps="LitSolid_DiffNormal" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffNormalEnvCubeAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid_NormalEnvCube" ps="LitSolid_DiffNormalEnvCube" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid_Normal" ps="LitSolid_DiffNormal" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid_NormalEnvCube" ps="LitSolid_DiffNormalEnvCube" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid_Normal" ps="LitSolid_DiffNormal" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffNormalSpecAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid_Normal" ps="LitSolid_DiffNormalSpecMap" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid_Normal" ps="LitSolid_DiffNormalSpecMap" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/DiffSpecAlpha.xml

@@ -1,5 +1,5 @@
 <technique>
-    <pass name="base" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid" ps="LitSolid_DiffSpecMap" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid" ps="LitSolid_Diff" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid" ps="LitSolid_DiffSpecMap" depthwrite="false" blend="addalpha" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/DiffUnlitAlpha.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit" ps="Unlit_Diff" depthwrite="false" blend="alpha" />
+    <pass name="alpha" vs="Unlit" ps="Unlit_Diff" depthwrite="false" blend="alpha" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/DiffVColAdd.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="add" />
+    <pass name="alpha" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="add" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/DiffVColMultiply.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="multiply" />
+    <pass name="alpha" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="multiply" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/DiffVColUnlitAlpha.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="alpha" />
+    <pass name="alpha" vs="Unlit_VCol" ps="Unlit_DiffVCol" depthwrite="false" blend="alpha" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/NoTextureAdd.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit" ps="Unlit" depthwrite="false" blend="add" />
+    <pass name="alpha" vs="Unlit" ps="Unlit" depthwrite="false" blend="add" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/NoTextureAlpha.xml

@@ -1,4 +1,4 @@
 <technique>
-    <pass name="base" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="addalpha" />
 </technique>

+ 2 - 2
Bin/CoreData/Techniques/NoTextureEnvCubeAlpha.xml

@@ -1,4 +1,4 @@
 <technique>
-    <pass name="base" vs="LitSolid_EnvCube" ps="LitSolid_EnvCube" depthwrite="false" blend="alpha" />
-    <pass name="light" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="addalpha" />
+    <pass name="alpha" vs="LitSolid_EnvCube" ps="LitSolid_EnvCube" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" vs="LitSolid" ps="LitSolid" depthwrite="false" blend="addalpha" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/NoTextureMultiply.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit" ps="Unlit" depthwrite="false" blend="multiply" />
+    <pass name="alpha" vs="Unlit" ps="Unlit" depthwrite="false" blend="multiply" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/NoTextureUnlitAlpha.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit" ps="Unlit" depthwrite="false" blend="alpha" />
+    <pass name="alpha" vs="Unlit" ps="Unlit" depthwrite="false" blend="alpha" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/NoTextureVColAdd.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit_VCol" ps="Unlit_VCol" depthwrite="false" blend="add" />
+    <pass name="alpha" vs="Unlit_VCol" ps="Unlit_VCol" depthwrite="false" blend="add" />
 </technique>

+ 1 - 1
Bin/CoreData/Techniques/NoTextureVColMultiply.xml

@@ -1,3 +1,3 @@
 <technique>
-    <pass name="base" vs="Unlit_VCol" ps="Unlit_VCol" depthwrite="false" blend="multiply" />
+    <pass name="alpha" vs="Unlit_VCol" ps="Unlit_VCol" depthwrite="false" blend="multiply" />
 </technique>

+ 0 - 2
Bin/Data/Scripts/Editor.as

@@ -104,7 +104,6 @@ void LoadConfig()
 
     if (!renderingElem.isNull)
     {
-        renderer.renderMode = RenderMode(renderingElem.GetInt("rendermode"));
         renderer.textureQuality = renderingElem.GetInt("texturequality");
         renderer.materialQuality = renderingElem.GetInt("materialquality");
         SetShadowResolution(renderingElem.GetInt("shadowresolution"));
@@ -143,7 +142,6 @@ void SaveConfig()
     objectElem.SetBool("generatetangents", generateTangents);
     objectElem.SetInt("pickmode", pickMode);
 
-    renderingElem.SetInt("rendermode", renderer.renderMode);
     renderingElem.SetInt("texturequality", renderer.textureQuality);
     renderingElem.SetInt("materialquality", renderer.materialQuality);
     renderingElem.SetInt("shadowresolution", GetShadowResolution());

+ 0 - 10
Bin/Data/Scripts/Editor/EditorSettings.as

@@ -58,9 +58,6 @@ void UpdateEditorSettingsDialog()
     DropDownList@ pickModeEdit = settingsDialog.GetChild("PickModeEdit", true);
     pickModeEdit.selection = pickMode;
 
-    DropDownList@ renderModeEdit = settingsDialog.GetChild("RenderModeEdit", true);
-    renderModeEdit.selection = renderer.renderMode;
-
     DropDownList@ textureQualityEdit = settingsDialog.GetChild("TextureQualityEdit", true);
     textureQualityEdit.selection = renderer.textureQuality;
 
@@ -109,7 +106,6 @@ void UpdateEditorSettingsDialog()
         SubscribeToEvent(localIDToggle, "Toggled", "EditUseLocalIDs");
         SubscribeToEvent(generateTangentsToggle, "Toggled", "EditGenerateTangents");
         SubscribeToEvent(pickModeEdit, "ItemSelected", "EditPickMode");
-        SubscribeToEvent(renderModeEdit, "ItemSelected", "EditRenderMode");
         SubscribeToEvent(textureQualityEdit, "ItemSelected", "EditTextureQuality");
         SubscribeToEvent(materialQualityEdit, "ItemSelected", "EditMaterialQuality");
         SubscribeToEvent(shadowResolutionEdit, "ItemSelected", "EditShadowResolution");
@@ -236,12 +232,6 @@ void EditPickMode(StringHash eventType, VariantMap& eventData)
     pickMode = edit.selection;
 }
 
-void EditRenderMode(StringHash eventType, VariantMap& eventData)
-{
-    DropDownList@ edit = eventData["Element"].GetUIElement();
-    renderer.renderMode = RenderMode(edit.selection);
-}
-
 void EditTextureQuality(StringHash eventType, VariantMap& eventData)
 {
     DropDownList@ edit = eventData["Element"].GetUIElement();

+ 0 - 3
Bin/Data/Scripts/LightTest.as

@@ -191,9 +191,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         if (input.keyDown['D'])
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
-        if (input.keyPress['1'])
-            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
-
         if (input.keyPress['2'])
         {
             int quality = renderer.textureQuality;

+ 7 - 10
Bin/Data/Scripts/Physics.as

@@ -254,9 +254,6 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (ui.focusElement is null)
     {
         if (key == '1')
-            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
-        
-        if (key == '2')
         {
             int quality = renderer.textureQuality;
             ++quality;
@@ -265,7 +262,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
         }
 
-        if (key == '3')
+        if (key == '2')
         {
             int quality = renderer.materialQuality;
             ++quality;
@@ -274,13 +271,13 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
         }
 
-        if (key == '4')
+        if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
 
-        if (key == '5')
+        if (key == '4')
             renderer.drawShadows = !renderer.drawShadows;
 
-        if (key == '6')
+        if (key == '5')
         {
             int size = renderer.shadowMapSize;
             size *= 2;
@@ -289,17 +286,17 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
         }
 
-        if (key == '7')
+        if (key == '6')
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
-        if (key == '8')
+        if (key == '7')
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
 
-        if (key == '9')
+        if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
         if (key == ' ')

+ 7 - 10
Bin/Data/Scripts/Terrain.as

@@ -243,9 +243,6 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (ui.focusElement is null)
     {
         if (key == '1')
-            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
-
-        if (key == '2')
         {
             int quality = renderer.textureQuality;
             ++quality;
@@ -254,7 +251,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
         }
 
-        if (key == '3')
+        if (key == '2')
         {
             int quality = renderer.materialQuality;
             ++quality;
@@ -263,13 +260,13 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
         }
 
-        if (key == '4')
+        if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
 
-        if (key == '5')
+        if (key == '4')
             renderer.drawShadows = !renderer.drawShadows;
 
-        if (key == '6')
+        if (key == '5')
         {
             int size = renderer.shadowMapSize;
             size *= 2;
@@ -278,17 +275,17 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
         }
 
-        if (key == '7')
+        if (key == '6')
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
-        if (key == '8')
+        if (key == '7')
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
 
-        if (key == '9')
+        if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
         if (key == ' ')

+ 7 - 10
Bin/Data/Scripts/TestScene.as

@@ -293,9 +293,6 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (ui.focusElement is null)
     {
         if (key == '1')
-            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
-        
-        if (key == '2')
         {
             int quality = renderer.textureQuality;
             ++quality;
@@ -304,7 +301,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
         }
 
-        if (key == '3')
+        if (key == '2')
         {
             int quality = renderer.materialQuality;
             ++quality;
@@ -313,13 +310,13 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
         }
 
-        if (key == '4')
+        if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
 
-        if (key == '5')
+        if (key == '4')
             renderer.drawShadows = !renderer.drawShadows;
 
-        if (key == '6')
+        if (key == '5')
         {
             int size = renderer.shadowMapSize;
             size *= 2;
@@ -328,17 +325,17 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
         }
 
-        if (key == '7')
+        if (key == '6')
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
-        if (key == '8')
+        if (key == '7')
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
 
-        if (key == '9')
+        if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
         if (key == ' ')

+ 7 - 10
Bin/Data/Scripts/TestSceneOld.as

@@ -380,9 +380,6 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     if (ui.focusElement is null)
     {
         if (key == '1')
-            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
-
-        if (key == '2')
         {
             int quality = renderer.textureQuality;
             ++quality;
@@ -391,7 +388,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
         }
 
-        if (key == '3')
+        if (key == '2')
         {
             int quality = renderer.materialQuality;
             ++quality;
@@ -400,13 +397,13 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
         }
 
-        if (key == '4')
+        if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
 
-        if (key == '5')
+        if (key == '4')
             renderer.drawShadows = !renderer.drawShadows;
 
-        if (key == '6')
+        if (key == '5')
         {
             int size = renderer.shadowMapSize;
             size *= 2;
@@ -415,17 +412,17 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
         }
 
-        if (key == '7')
+        if (key == '6')
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
-        if (key == '8')
+        if (key == '7')
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
 
-        if (key == '9')
+        if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
         if (key == ' ')

+ 0 - 32
Bin/Data/UI/EditorSettingsDialog.xml

@@ -234,38 +234,6 @@
         <attribute name="Min Size" value="0 4" />
         <attribute name="Max Size" value="2147483647 4" />
     </element>
-    <element>
-        <attribute name="Min Size" value="0 17" />
-        <attribute name="Max Size" value="2147483647 17" />
-        <attribute name="Layout Mode" value="Horizontal" />
-        <attribute name="Layout Spacing" value="8" />
-        <element type="Text">
-            <attribute name="Text" value="Rendering mode" />
-        </element>
-        <element type="DropDownList">
-            <attribute name="Name" value="RenderModeEdit" />
-            <attribute name="Min Size" value="100 0" />
-            <attribute name="Max Size" value="100 2147483647" />
-            <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true" popup="true">
-                <element type="ListView" internal="true">
-                    <element type="BorderImage" internal="true">
-                        <element internal="true">
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Forward" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Pre-pass" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Deferred" />
-                            </element>
-                        </element>
-                    </element>
-                </element>
-            </element>
-        </element>
-    </element>
     <element>
         <attribute name="Min Size" value="0 17" />
         <attribute name="Max Size" value="2147483647 17" />

+ 2 - 2
Docs/GettingStarted.dox

@@ -124,7 +124,7 @@ Space       Toggle debug geometry
 F1          Toggle AngelScript console
 F5          Save scene
 F7          Load scene
-1 to 9      Toggle rendering options
+1 to 8      Toggle rendering options
 T           Toggle profiling display
 O           Toggle orthographic camera
 F           Toggle FXAA edge filter
@@ -173,7 +173,7 @@ Arrows      Add or remove lights and objects
 Pageup/down Add or remove 10 lights
 Right mouse Hold and move mouse to rotate view
 F1          Toggle AngelScript console
-1 to 9      Toggle rendering options
+1 to 8      Toggle rendering options
 T           Toggle profiling display
 O           Toggle orthographic camera
 V           Toggle vertex lighting

+ 8 - 4
Docs/Reference.dox

@@ -644,7 +644,7 @@ A technique definition looks like this:
 
 \code
 <technique>
-    <pass name="base|litbase|light|prealpha|postalpha|prepass|material|deferred|shadow" vs="VertexShaderName" ps="PixelShaderName"
+    <pass name="base|litbase|light|alpha|litalpha|prealpha|postalpha|prepass|material|deferred|shadow" vs="VertexShaderName" ps="PixelShaderName"
         alphatest="true|false" blend="replace|add|multiply|alpha|addalpha|premulalpha|invdestalpha"
         depthtest="always|equal|less|lessequal|greater|greaterequal" depthwrite="true|false" alphamask="true|false" />
     <pass ... />
@@ -654,9 +654,11 @@ A technique definition looks like this:
 
 The purposes of the different passes are:
 
-- base: Renders ambient light, per-vertex lights and fog.
-- litbase: Renders the first per-pixel light, ambient light and fog. This is an optional pass for optimization.
-- light: Renders one per-pixel light's contribution additively.
+- base: Renders ambient light, per-vertex lights and fog for an opaque object.
+- litbase: Renders the first per-pixel light, ambient light and fog for an opaque object. This is an optional pass for optimization.
+- light: Renders one per-pixel light's contribution additively for an opaque object.
+- alpha: Renders ambient light, per-vertex lights and fog for a transparent object.
+- litalpha: Renders one per-pixel light's contribution additively for a transparent object
 - prealpha: Custom rendering pass after opaque geometry. Can be used for example to render the skybox.
 - postalpha: Custom rendering pass after transparent geometry.
 - prepass: %Light pre-pass only - renders normals, specular power and depth to the G-buffer.
@@ -664,6 +666,8 @@ The purposes of the different passes are:
 - deferred: Deferred rendering only - renders ambient light and per-vertex lights to the output rendertarget, and diffuse albedo, normals, specular intensity + power and depth to the G-buffer.
 - shadow: Renders depth only for shadow map generation.
 
+More custom passes can be defined and referred to in the render path definition.
+
 By default draw calls within passes are sorted by render state, but transparent base and light passes, as well as the postalpha pass, are sorted by distance back to front.
 
 Note that the technique does not need to enumerate shaders used for different geometry types (non-skinned, skinned, instanced, billboard) and different per-vertex and per-pixel light combinations. Instead specific hardcoded shader variations are assumed to exist. See the file LitSolid.xml in either Bin/CoreData/Shaders/HLSL or Bin/CoreData/Shaders/GLSL to see which variations are required.

+ 1 - 9
Engine/Engine/DebugHud.cpp

@@ -38,13 +38,6 @@
 namespace Urho3D
 {
 
-static const char* renderModeTexts[] =
-{
-    "Forward",
-    "Prepass",
-    "Deferred"
-};
-
 static const char* qualityTexts[] =
 {
     "Low",
@@ -135,8 +128,7 @@ void DebugHud::Update()
     if (modeText_->IsVisible())
     {
         String mode;
-        mode.AppendWithFormat("Render:%s Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s Mode:%s",
-            renderModeTexts[renderer->GetRenderMode()],
+        mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s Mode:%s",
             qualityTexts[renderer->GetTextureQuality()],
             qualityTexts[renderer->GetMaterialQuality()],
             renderer->GetSpecularLighting() ? "On" : "Off",

+ 5 - 4
Engine/Engine/Engine.cpp

@@ -106,7 +106,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     if (initialized_)
         return true;
     
-    RenderMode mode = RENDER_FORWARD;
+    String renderPath;
     int width = 0;
     int height = 0;
     int multiSample = 1;
@@ -143,9 +143,9 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
             else if (argument == "mono")
                 stereo = false;
             else if (argument == "prepass")
-                mode = RENDER_PREPASS;
+                renderPath = "CoreData/RenderPaths/Prepass.xml";
             else if (argument == "deferred")
-                mode = RENDER_DEFERRED;
+                renderPath = "CoreData/RenderPaths/Deferred.xml";
             else if (argument == "noshadows")
                 shadows = false;
             else if (argument == "lqshadows")
@@ -264,7 +264,8 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
         if (!graphics->SetMode(width, height, fullscreen, vsync, tripleBuffer, multiSample))
             return false;
         
-        renderer->SetRenderMode(mode);
+        if (!renderPath.Empty())
+            renderer->SetDefaultRenderPathName(renderPath);
         renderer->SetDrawShadows(shadows);
         if (shadows && lqShadows)
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);

+ 0 - 7
Engine/Engine/GraphicsAPI.cpp

@@ -912,11 +912,6 @@ static Renderer* GetRenderer()
 
 static void RegisterRenderer(asIScriptEngine* engine)
 {
-    engine->RegisterEnum("RenderMode");
-    engine->RegisterEnumValue("RenderMode", "RENDER_FORWARD", RENDER_FORWARD);
-    engine->RegisterEnumValue("RenderMode", "RENDER_PREPASS", RENDER_PREPASS);
-    engine->RegisterEnumValue("RenderMode", "RENDER_DEFERRED", RENDER_DEFERRED);
-    
     engine->RegisterGlobalProperty("const int QUALITY_LOW", (void*)&QUALITY_LOW);
     engine->RegisterGlobalProperty("const int QUALITY_MEDIUM", (void*)&QUALITY_MEDIUM);
     engine->RegisterGlobalProperty("const int QUALITY_HIGH", (void*)&QUALITY_HIGH);
@@ -933,8 +928,6 @@ 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 set_renderMode(RenderMode)", asMETHOD(Renderer, SetRenderMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "RenderMode get_renderMode() const", asMETHOD(Renderer, GetRenderMode), 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);

+ 2 - 0
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -2003,6 +2003,8 @@ unsigned Graphics::GetFormat(const String& formatName)
         return GetFloat16Format();
     if (nameLower == "r32f" || nameLower == "float")
         return GetFloat32Format();
+    if (nameLower == "lineardepth" || nameLower == "depth")
+        return GetLinearDepthFormat();
     if (nameLower == "d24s8")
         return GetDepthStencilFormat();
     

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

@@ -284,7 +284,7 @@ bool Shader::PrepareVariation(ShaderVariation* variation)
         unsigned sampler = file->ReadUByte();
         
         TextureUnit tuIndex = graphics->GetTextureUnit(unitName);
-        if (tuIndex != MAX_TEXTURE_UNITS)
+        if (tuIndex < MAX_TEXTURE_UNITS)
             variation->AddTextureUnit(tuIndex);
         else if (sampler < MAX_TEXTURE_UNITS)
             variation->AddTextureUnit((TextureUnit)sampler);

+ 2 - 0
Engine/Graphics/GraphicsDefs.cpp

@@ -72,6 +72,8 @@ StringHash PSP_LIGHTMATRICES("LightMatricesPS");
 StringHash PASS_BASE("base");
 StringHash PASS_LITBASE("litbase");
 StringHash PASS_LIGHT("light");
+StringHash PASS_ALPHA("alpha");
+StringHash PASS_LITALPHA("litalpha");
 StringHash PASS_SHADOW("shadow");
 StringHash PASS_DEFERRED("deferred");
 StringHash PASS_PREPASS("prepass");

+ 6 - 4
Engine/Graphics/GraphicsDefs.h

@@ -236,10 +236,12 @@ extern StringHash PSP_SHADOWMAPINVSIZE;
 extern StringHash PSP_SHADOWSPLITS;
 extern StringHash PSP_LIGHTMATRICES;
 
-// Inbuild pass types
+// Inbuilt pass types
 extern StringHash PASS_BASE;
 extern StringHash PASS_LITBASE;
 extern StringHash PASS_LIGHT;
+extern StringHash PASS_ALPHA;
+extern StringHash PASS_LITALPHA;
 extern StringHash PASS_SHADOW;
 extern StringHash PASS_DEFERRED;
 extern StringHash PASS_PREPASS;
@@ -258,17 +260,17 @@ enum TextureUnit
     TU_NORMAL = 1,
     TU_NORMALBUFFER = 1,
     TU_SPECULAR = 2,
-    TU_DEPTHBUFFER = 2,
     TU_EMISSIVE = 3,
     TU_ENVIRONMENT = 4,
     MAX_MATERIAL_TEXTURE_UNITS = 5,
     TU_LIGHTRAMP = 5,
-    TU_LIGHTBUFFER = 5,
     TU_LIGHTSHAPE = 6,
     TU_SHADOWMAP = 7,
     TU_FACESELECT = 8,
     TU_INDIRECTION = 9,
-    MAX_TEXTURE_UNITS = 10
+    TU_DEPTHBUFFER = 10,
+    TU_LIGHTBUFFER = 11,
+    MAX_TEXTURE_UNITS = 12
 };
 
 /// Shader parameter groups for determining need to update.

+ 12 - 3
Engine/Graphics/Material.cpp

@@ -48,6 +48,13 @@ const String textureUnitNames[] =
     "specular",
     "emissive",
     "environment",
+    "lightramp",
+    "lightshape",
+    "shadowmap",
+    "faceselect",
+    "indirection",
+    "depth",
+    "light",
     ""
 };
 
@@ -61,9 +68,11 @@ static const String cullModeNames[] =
 
 TextureUnit ParseTextureUnitName(const String& name)
 {
-    TextureUnit unit = (TextureUnit)GetStringListIndex(name, textureUnitNames, MAX_MATERIAL_TEXTURE_UNITS);
+    TextureUnit unit = (TextureUnit)GetStringListIndex(name, textureUnitNames, MAX_TEXTURE_UNITS);
     if (name == "diff")
         unit = TU_DIFFUSE;
+    else if (name == "albedo")
+        unit = TU_DIFFUSE;
     else if (name == "norm")
         unit = TU_NORMAL;
     else if (name == "spec")
@@ -168,8 +177,8 @@ bool Material::Load(Deserializer& source)
             if (unitName.Length() > 1)
             {
                 unit = ParseTextureUnitName(unitName);
-                if (unit == MAX_MATERIAL_TEXTURE_UNITS)
-                    LOGERROR("Unknown texture unit " + unitName);
+                if (unit >= MAX_MATERIAL_TEXTURE_UNITS)
+                    LOGERROR("Unknown or illegal texture unit " + unitName);
             }
             else
                 unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_MATERIAL_TEXTURE_UNITS - 1);

+ 2 - 0
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -2148,6 +2148,8 @@ unsigned Graphics::GetFormat(const String& formatName)
         return GetFloat16Format();
     if (nameLower == "r32f" || nameLower == "float")
         return GetFloat32Format();
+    if (nameLower == "lineardepth" || nameLower == "depth")
+        return GetLinearDepthFormat();
     if (nameLower == "d24s8")
         return GetDepthStencilFormat();
     

+ 6 - 6
Engine/Graphics/PostProcess.cpp

@@ -56,7 +56,7 @@ void PostProcessPass::SetPixelShader(const String& name)
 
 void PostProcessPass::SetTexture(TextureUnit unit, const String& name)
 {
-    if (unit < MAX_MATERIAL_TEXTURE_UNITS)
+    if (unit < MAX_TEXTURE_UNITS)
         textureNames_[unit] = name;
 }
 
@@ -81,7 +81,7 @@ SharedPtr<PostProcessPass> PostProcessPass::Clone()
     clone->vertexShaderName_ = vertexShaderName_;
     clone->pixelShaderName_ = pixelShaderName_;
     clone->shaderParameters_ = shaderParameters_;
-    for (unsigned i = 0; i < MAX_MATERIAL_TEXTURE_UNITS; ++i)
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         clone->textureNames_[i] = textureNames_[i];
     clone->outputName_ = outputName_;
     
@@ -90,7 +90,7 @@ SharedPtr<PostProcessPass> PostProcessPass::Clone()
 
 const String& PostProcessPass::GetTexture(TextureUnit unit) const
 {
-    return unit < MAX_MATERIAL_TEXTURE_UNITS ? textureNames_[unit] : String::EMPTY;
+    return unit < MAX_TEXTURE_UNITS ? textureNames_[unit] : String::EMPTY;
 }
 
 const Vector4& PostProcessPass::GetShaderParameter(const String& name) const
@@ -190,13 +190,13 @@ bool PostProcess::LoadParameters(XMLFile* file)
                 if (unitName.Length() > 1)
                 {
                     unit = ParseTextureUnitName(unitName);
-                    if (unit == MAX_MATERIAL_TEXTURE_UNITS)
+                    if (unit >= MAX_TEXTURE_UNITS)
                         LOGERROR("Unknown texture unit " + unitName);
                 }
                 else
-                    unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_MATERIAL_TEXTURE_UNITS - 1);
+                    unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_TEXTURE_UNITS - 1);
             }
-            if (unit != MAX_MATERIAL_TEXTURE_UNITS)
+            if (unit < MAX_TEXTURE_UNITS)
             {
                 String name = textureElem.GetAttribute("name");
                 pass->SetTexture(unit, name);

+ 1 - 1
Engine/Graphics/PostProcess.h

@@ -77,7 +77,7 @@ private:
     /// Pixel shader name.
     String pixelShaderName_;
     /// Textures.
-    String textureNames_[MAX_MATERIAL_TEXTURE_UNITS];
+    String textureNames_[MAX_TEXTURE_UNITS];
     /// %Shader parameters.
     HashMap<StringHash, Vector4> shaderParameters_;
     /// Output rendertarget name.

+ 38 - 68
Engine/Graphics/Renderer.cpp

@@ -259,7 +259,7 @@ Renderer::Renderer(Context* context) :
     Object(context),
     defaultZone_(new Zone(context)),
     quadDirLight_(new Light(context)),
-    renderMode_(RENDER_FORWARD),
+    defaultRenderPathName_("CoreData/RenderPaths/Forward.xml"),
     textureAnisotropy_(4),
     textureFilterMode_(FILTER_TRILINEAR),
     textureQuality_(QUALITY_HIGH),
@@ -329,33 +329,11 @@ bool Renderer::SetViewport(unsigned index, Viewport* viewport)
     return true;
 }
 
-void Renderer::SetRenderMode(RenderMode mode)
+void Renderer::SetDefaultRenderPathName(const String& name)
 {
-    if (!initialized_)
-    {
-        LOGERROR("Can not switch rendering mode before setting initial screen mode");
-        return;
-    }
-    
-    if (mode == RENDER_PREPASS && !graphics_->GetLightPrepassSupport())
-        mode = RENDER_FORWARD;
-    if (mode == RENDER_DEFERRED && !graphics_->GetDeferredSupport())
-        mode = RENDER_FORWARD;
-    
-    if (mode != renderMode_)
-    {
-        // Deferred rendering is incompatible with hardware multisampling, so set new screen mode with 1x sampling if in use
-        if (mode != RENDER_FORWARD && graphics_->GetMultiSample() > 1)
-        {
-            graphics_->SetMode(graphics_->GetWidth(), graphics_->GetHeight(), graphics_->GetFullscreen(), graphics_->GetVSync(),
-                graphics_->GetTripleBuffer(), 1);
-        }
-        
-        ResetBuffers();
-        ResetShadowMaps();
-        renderMode_ = mode;
-        shadersDirty_ = true;
-    }
+    String nameTrimmed = name.Trimmed();
+    if (!nameTrimmed.Empty())
+        defaultRenderPathName_ = name;
 }
 
 void Renderer::SetSpecularLighting(bool enable)
@@ -458,12 +436,7 @@ void Renderer::SetMaxShadowMaps(int shadowMaps)
 
 void Renderer::SetMaxShadowCascades(int cascades)
 {
-    #ifndef USE_OPENGL
-    // Due to instruction count limits, deferred modes in SM2.0 can only support up to 3 cascades
-    cascades = Clamp(cascades, 1, renderMode_ != RENDER_FORWARD && !graphics_->GetSM3Support() ? 3 : MAX_CASCADE_SPLITS);
-    #else
     cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
-    #endif
     
     if (cascades != maxShadowCascades_)
     {
@@ -758,6 +731,32 @@ bool Renderer::AddView(RenderSurface* renderTarget, Viewport* viewport)
         return false;
 }
 
+void Renderer::GetLightVolumeShaders(PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS, const String& vsName, const String& psName)
+{
+    lightVS.Resize(MAX_DEFERRED_LIGHT_VS_VARIATIONS);
+    lightPS.Resize(MAX_DEFERRED_LIGHT_PS_VARIATIONS);
+    
+    unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
+    
+    for (unsigned i = 0; i < MAX_DEFERRED_LIGHT_VS_VARIATIONS; ++i)
+        lightVS[i] = GetVertexShader(vsName + "_" + deferredLightVSVariations[i]);
+    
+    for (unsigned i = 0; i < lightPS.Size(); ++i)
+    {
+        String ortho;
+        if (i >= DLPS_ORTHO)
+            ortho = "Ortho";
+        
+        if (i & DLPS_SHADOW)
+        {
+            lightPS[i] = GetPixelShader(psName + "_" + ortho + lightPSVariations[i % DLPS_ORTHO] +
+                shadowVariations[shadows]);
+        }
+        else
+            lightPS[i] = GetPixelShader(psName + "_" + ortho + lightPSVariations[i % DLPS_ORTHO]);
+    }
+}
+
 Geometry* Renderer::GetLightGeometry(Light* light)
 {
     switch (light->GetLightType())
@@ -1051,7 +1050,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
         
         //  Check whether is a pixel lit forward pass. If not, there is only one pixel shader
         const StringHash& type = batch.pass_->GetType();
-        if (type == PASS_LIGHT || type == PASS_LITBASE)
+        if (type == PASS_LIGHT || type == PASS_LITBASE || type == PASS_LITALPHA)
         {
             LightBatchQueue* lightQueue = batch.lightQueue_;
             if (!lightQueue)
@@ -1121,7 +1120,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
         else
         {
             // Check if pass has vertex lighting support
-            if (type == PASS_BASE || type == PASS_MATERIAL || type == PASS_DEFERRED)
+            if (type == PASS_BASE || type == PASS_ALPHA || type == PASS_MATERIAL || type == PASS_DEFERRED)
             {
                 unsigned numVertexLights = 0;
                 if (batch.lightQueue_)
@@ -1157,7 +1156,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     }
 }
 
-void Renderer::SetLightVolumeBatchShaders(Batch& batch)
+void Renderer::SetLightVolumeBatchShaders(Batch& batch, PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS)
 {
     unsigned vsi = DLVS_NONE;
     unsigned psi = DLPS_NONE;
@@ -1193,8 +1192,8 @@ void Renderer::SetLightVolumeBatchShaders(Batch& batch)
         psi += DLPS_ORTHO;
     }
     
-    batch.vertexShader_ = lightVS_[vsi];
-    batch.pixelShader_ = lightPS_[psi];
+    batch.vertexShader_ = lightVS[vsi];
+    batch.pixelShader_ = lightPS[psi];
 }
 
 void Renderer::SetCullMode(CullMode mode, Camera* camera)
@@ -1464,35 +1463,6 @@ void Renderer::LoadShaders()
     // Load inbuilt shaders
     stencilVS_ = GetVertexShader("Stencil");
     stencilPS_ = GetPixelShader("Stencil");
-    lightVS_.Clear();
-    lightPS_.Clear();
-    
-    if (renderMode_ != RENDER_FORWARD)
-    {
-        lightVS_.Resize(MAX_DEFERRED_LIGHT_VS_VARIATIONS);
-        lightPS_.Resize(MAX_DEFERRED_LIGHT_PS_VARIATIONS);
-        
-        unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
-        String shaderName = renderMode_ == RENDER_PREPASS ? "PrepassLight_" : "DeferredLight_";
-        
-        for (unsigned i = 0; i < MAX_DEFERRED_LIGHT_VS_VARIATIONS; ++i)
-            lightVS_[i] = GetVertexShader(shaderName + deferredLightVSVariations[i]);
-        
-        for (unsigned i = 0; i < lightPS_.Size(); ++i)
-        {
-            String ortho;
-            if (i >= DLPS_ORTHO)
-                ortho = "Ortho";
-            
-            if (i & DLPS_SHADOW)
-            {
-                lightPS_[i] = GetPixelShader(shaderName + ortho + lightPSVariations[i % DLPS_ORTHO] +
-                    shadowVariations[shadows]);
-            }
-            else
-                lightPS_[i] = GetPixelShader(shaderName + ortho + lightPSVariations[i % DLPS_ORTHO]);
-        }
-    }
     
     shadersDirty_ = false;
 }
@@ -1528,7 +1498,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
     vertexShaders.Clear();
     pixelShaders.Clear();
     
-    if (type == PASS_LIGHT || type == PASS_LITBASE)
+    if (type == PASS_LIGHT || type == PASS_LITBASE || type == PASS_LITALPHA)
     {
         // Load forward pixel lit variations
         vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
@@ -1551,7 +1521,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
     else
     {
         // Load vertex light variations for forward ambient pass, deferred G-buffer pass and pre-pass material pass
-        if (type == PASS_BASE || type == PASS_MATERIAL || type == PASS_DEFERRED)
+        if (type == PASS_BASE || type == PASS_ALPHA || type == PASS_MATERIAL || type == PASS_DEFERRED)
         {
             vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)

+ 9 - 11
Engine/Graphics/Renderer.h

@@ -167,8 +167,8 @@ public:
     void SetNumViewports(unsigned num);
     /// Set a viewport. Return true if successful.
     bool SetViewport(unsigned index, Viewport* viewport);
-    /// Set rendering mode (forward / light pre-pass / deferred.)
-    void SetRenderMode(RenderMode mode);
+    /// Set default renderpath resource name.
+    void SetDefaultRenderPathName(const String& name);
     /// Set specular lighting on/off.
     void SetSpecularLighting(bool enable);
     /// Set texture anisotropy.
@@ -210,8 +210,8 @@ public:
     unsigned GetNumViewports() const { return viewports_.Size(); }
     /// Return viewport.
     Viewport* GetViewport(unsigned index) const;
-    /// Return rendering mode.
-    RenderMode GetRenderMode() const { return renderMode_; }
+    /// Return default renderpath resource name.
+    const String& GetDefaultRenderPathName() const { return defaultRenderPathName_; }
     /// Return whether specular lighting is enabled.
     bool GetSpecularLighting() const { return specularLighting_; }
     /// Return whether drawing shadows is enabled.
@@ -295,6 +295,8 @@ public:
     void DrawDebugGeometry(bool depthTest);
     /// Add a view. Return true if successful.
     bool AddView(RenderSurface* renderTarget, Viewport* viewport);
+    /// Populate light volume shaders.
+    void GetLightVolumeShaders(PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS, const String& vsName, const String& psName);
     /// Return volume geometry for a light.
     Geometry* GetLightGeometry(Light* light);
     /// Allocate a shadow map. If shadow map reuse is disabled, a different map is returned each time.
@@ -312,7 +314,7 @@ public:
     /// Choose shaders for a forward rendering batch.
     void SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows = true);
     /// Choose shaders for a light volume batch.
-    void SetLightVolumeBatchShaders(Batch& batch);
+    void SetLightVolumeBatchShaders(Batch& batch, PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS);
     /// Set cull mode while taking possible projection flipping into account.
     void SetCullMode(CullMode mode, Camera* camera);
     /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.
@@ -398,10 +400,6 @@ private:
     SharedPtr<ShaderVariation> stencilVS_;
     /// Stencil rendering pixel shader.
     SharedPtr<ShaderVariation> stencilPS_;
-    /// Light vertex shaders.
-    Vector<SharedPtr<ShaderVariation> > lightVS_;
-    /// Light pixel shaders.
-    Vector<SharedPtr<ShaderVariation> > lightPS_;
     /// Reusable scene nodes with shadow camera components.
     Vector<SharedPtr<Node> > shadowCameraNodes_;
     /// Reusable occlusion buffers.
@@ -430,12 +428,12 @@ 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.
     FrameInfo frame_;
-    /// Rendering mode.
-    RenderMode renderMode_;
     /// Texture anisotropy level.
     int textureAnisotropy_;
     /// Texture filtering mode.

+ 271 - 334
Engine/Graphics/View.cpp

@@ -294,12 +294,13 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     if (!camera->IsProjectionValid())
         return false;
     
-    renderMode_ = renderer_->GetRenderMode();
     scene_ = scene;
     octree_ = octree;
     camera_ = camera;
     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();
@@ -311,6 +312,47 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             postProcesses_.Push(*i);
     }
     
+    // 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.type_ == CMD_SCENEPASS)
+        {
+            ScenePassInfo info;
+            info.pass_ = command.pass_;
+            info.allowInstancing_ = command.sortMode_ != SORT_BACKTOFRONT;
+            info.markToStencil_ = command.markToStencil_;
+            info.useScissor_ = command.useScissor_;
+            info.vertexLights_ = command.vertexLights_;
+            
+            HashMap<StringHash, BatchQueue>::Iterator j = batchQueues_.Find(command.pass_);
+            if (j == batchQueues_.End())
+                j = batchQueues_.Insert(Pair<StringHash, BatchQueue>(command.pass_, BatchQueue()));
+            info.batchQueue_ = &j->second_;
+            
+            scenePasses_.Push(info);
+        }
+    }
+    
+    // Get light volume shaders according to the renderpath, if it needs them
+    deferred_ = false;
+    for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
+    {
+        const RenderPathCommand& command = renderPath_->commands_[i];
+        if (command.type_ == CMD_LIGHTVOLUMES)
+        {
+            renderer_->GetLightVolumeShaders(lightVS_, lightPS_, command.vertexShaderName_, command.pixelShaderName_);
+            deferred_ = true;
+        }
+    }
+    if (!deferred_)
+    {
+        lightVS_.Clear();
+        lightPS_.Clear();
+    }
+    
     // Validate the rect and calculate size. If zero rect, use whole rendertarget size
     int rtWidth = renderTarget ? renderTarget->GetWidth() : graphics_->GetWidth();
     int rtHeight = renderTarget ? renderTarget->GetHeight() : graphics_->GetHeight();
@@ -368,17 +410,15 @@ void View::Update(const FrameInfo& frame)
     
     // Clear screen buffers, geometry, light, occluder & batch lists
     screenBuffers_.Clear();
+    renderTargets_.Clear();
     geometries_.Clear();
     shadowGeometries_.Clear();
     lights_.Clear();
     zones_.Clear();
     occluders_.Clear();
-    baseQueue_.Clear(maxSortedInstances);
-    preAlphaQueue_.Clear(maxSortedInstances);
-    gbufferQueue_.Clear(maxSortedInstances);
-    alphaQueue_.Clear(maxSortedInstances);
-    postAlphaQueue_.Clear(maxSortedInstances);
     vertexLightQueues_.Clear();
+    for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+        i->second_.Clear(maxSortedInstances);
     
     // Set automatic aspect ratio if required
     if (camera_->GetAutoAspectRatio())
@@ -434,10 +474,7 @@ void View::Render()
     #endif
     
     // Render
-    if (renderMode_ == RENDER_FORWARD)
-        RenderBatchesForward();
-    else
-        RenderBatchesDeferred();
+    ExecuteRenderPathCommands();
     
     #ifdef USE_OPENGL
     camera_->SetFlipVertical(false);
@@ -658,6 +695,7 @@ void View::GetBatches()
 {
     WorkQueue* queue = GetSubsystem<WorkQueue>();
     PODVector<Light*> vertexLights;
+    BatchQueue* alphaQueue = batchQueues_.Contains(PASS_ALPHA) ? &batchQueues_[PASS_ALPHA] : (BatchQueue*)0;
     
     // Process lit geometries and shadow casters for each light
     {
@@ -792,13 +830,13 @@ void View::GetBatches()
                     
                     // If drawable limits maximum lights, only record the light, and check maximum count / build batches later
                     if (!drawable->GetMaxLights())
-                        GetLitBatches(drawable, lightQueue);
+                        GetLitBatches(drawable, lightQueue, alphaQueue);
                     else
                         maxLightsDrawables_.Insert(drawable);
                 }
                 
                 // In deferred modes, store the light volume batch now
-                if (renderMode_ != RENDER_FORWARD)
+                if (deferred_)
                 {
                     Batch volumeBatch;
                     volumeBatch.geometry_ = renderer_->GetLightGeometry(light);
@@ -810,7 +848,7 @@ void View::GetBatches()
                     volumeBatch.material_ = 0;
                     volumeBatch.pass_ = 0;
                     volumeBatch.zone_ = 0;
-                    renderer_->SetLightVolumeBatchShaders(volumeBatch);
+                    renderer_->SetLightVolumeBatchShaders(volumeBatch, lightVS_, lightPS_);
                     lightQueue.volumeBatches_.Push(volumeBatch);
                 }
             }
@@ -844,7 +882,7 @@ void View::GetBatches()
                 // Find the correct light queue again
                 LightBatchQueue* queue = light->GetLightQueue();
                 if (queue)
-                    GetLitBatches(drawable, *queue);
+                    GetLitBatches(drawable, *queue, alphaQueue);
             }
         }
     }
@@ -853,8 +891,6 @@ void View::GetBatches()
     {
         PROFILE(GetBaseBatches);
         
-        hasZeroLightMask_ = false;
-        
         for (PODVector<Drawable*>::ConstIterator i = geometries_.Begin(); i != geometries_.End(); ++i)
         {
             Drawable* drawable = *i;
@@ -874,10 +910,6 @@ void View::GetBatches()
                 if (srcBatch.material_ && srcBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_ && !renderTarget_)
                     CheckMaterialForAuxView(srcBatch.material_);
                 
-                // If already has a lit base pass, skip (forward rendering only)
-                if (j < 32 && drawable->HasBasePass(j))
-                    continue;
-                
                 Technique* tech = GetTechnique(drawable, srcBatch.material_);
                 if (!srcBatch.geometry_ || !tech)
                     continue;
@@ -889,37 +921,23 @@ void View::GetBatches()
                 destBatch.pass_ = 0;
                 destBatch.lightMask_ = GetLightMask(drawable);
                 
-                // In deferred modes check for G-buffer and material passes first
-                if (renderMode_ == RENDER_PREPASS)
-                {
-                    destBatch.pass_ = tech->GetPass(PASS_PREPASS);
-                    if (destBatch.pass_)
-                    {
-                        // If the opaque object has a zero lightmask, have to skip light buffer optimization
-                        if (!hasZeroLightMask_ && (!(GetLightMask(drawable) & 0xff)))
-                            hasZeroLightMask_ = true;
-                        
-                        // Allow G-buffer pass instancing only if lightmask matches zone lightmask
-                        AddBatchToQueue(gbufferQueue_, destBatch, tech, destBatch.lightMask_ == (zone->GetLightMask() & 0xff));
-                        destBatch.pass_ = tech->GetPass(PASS_MATERIAL);
-                    }
-                }
-                
-                if (renderMode_ == RENDER_DEFERRED)
-                    destBatch.pass_ = tech->GetPass(PASS_DEFERRED);
-                
-                // Next check for forward unlit base pass
-                if (!destBatch.pass_)
-                    destBatch.pass_ = tech->GetPass(PASS_BASE);
-                
-                if (destBatch.pass_)
+                // Check each of the scene passes
+                for (unsigned k = 0; k < scenePasses_.Size(); ++k)
                 {
-                    // Check for vertex lights (both forward unlit, light pre-pass material pass, and deferred G-buffer)
-                    if (!drawableVertexLights.Empty())
+                    ScenePassInfo& info = scenePasses_[k];
+                    destBatch.pass_ = tech->GetPass(info.pass_);
+                    if (!destBatch.pass_)
+                        continue;
+                    
+                    // Skip forward base pass if the corresponding litbase pass already exists
+                    if (info.pass_ == PASS_BASE && j < 32 && drawable->HasBasePass(j))
+                        continue;
+                    
+                    if (info.vertexLights_ && !drawableVertexLights.Empty())
                     {
                         // For a deferred opaque batch, check if the vertex lights include converted per-pixel lights, and remove
                         // them to prevent double-lighting
-                        if (renderMode_ != RENDER_FORWARD && destBatch.pass_->GetBlendMode() == BLEND_REPLACE)
+                        if (deferred_ && destBatch.pass_->GetBlendMode() == BLEND_REPLACE)
                         {
                             vertexLights.Clear();
                             for (unsigned i = 0; i < drawableVertexLights.Size(); ++i)
@@ -947,40 +965,14 @@ void View::GetBatches()
                             destBatch.lightQueue_ = &(i->second_);
                         }
                     }
-                    
-                    // Check whether batch is opaque or transparent
-                    if (destBatch.pass_->GetBlendMode() == BLEND_REPLACE)
-                    {
-                        if (destBatch.pass_->GetType() != PASS_DEFERRED)
-                            AddBatchToQueue(baseQueue_, destBatch, tech);
-                        else
-                        {
-                            // Allow G-buffer pass instancing only if lightmask matches zone lightmask
-                            AddBatchToQueue(gbufferQueue_, destBatch, tech, destBatch.lightMask_ == (destBatch.zone_->GetLightMask() & 0xff));
-                        }
-                    }
                     else
-                    {
-                        // Transparent batches can not be instanced
-                        AddBatchToQueue(alphaQueue_, destBatch, tech, false);
-                    }
-                    continue;
-                }
-                
-                // If no pass found so far, finally check for pre-alpha / post-alpha custom passes
-                destBatch.pass_ = tech->GetPass(PASS_PREALPHA);
-                if (destBatch.pass_)
-                {
-                    AddBatchToQueue(preAlphaQueue_, destBatch, tech);
-                    continue;
-                }
-                
-                destBatch.pass_ = tech->GetPass(PASS_POSTALPHA);
-                if (destBatch.pass_)
-                {
-                    // Post-alpha pass is treated similarly as alpha, and is not instanced
-                    AddBatchToQueue(postAlphaQueue_, destBatch, tech, false);
-                    continue;
+                        destBatch.lightQueue_ = 0;
+                    
+                    bool allowInstancing = info.allowInstancing_;
+                    if (allowInstancing && info.markToStencil_ && destBatch.lightMask_ != (zone->GetLightMask() & 0xff))
+                        allowInstancing = false;
+                    
+                    AddBatchToQueue(*info.batchQueue_, destBatch, tech, allowInstancing);
                 }
             }
         }
@@ -997,23 +989,19 @@ void View::UpdateGeometries()
     {
         WorkItem item;
         
-        item.workFunction_ = SortBatchQueueFrontToBackWork;
-        item.start_ = &baseQueue_;
-        queue->AddWorkItem(item);
-        item.start_ = &preAlphaQueue_;
-        queue->AddWorkItem(item);
-        if (renderMode_ != RENDER_FORWARD)
+        for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         {
-            item.start_ = &gbufferQueue_;
-            queue->AddWorkItem(item);
+            const RenderPathCommand& command = renderPath_->commands_[i];
+            
+            if (command.type_ == CMD_SCENEPASS)
+            {
+                item.workFunction_ = command.sortMode_ == SORT_FRONTTOBACK ? SortBatchQueueFrontToBackWork :
+                    SortBatchQueueBackToFrontWork;
+                item.start_ = &batchQueues_[command.pass_];
+                queue->AddWorkItem(item);
+            }
         }
         
-        item.workFunction_ = SortBatchQueueBackToFrontWork;
-        item.start_ = &alphaQueue_;
-        queue->AddWorkItem(item);
-        item.start_ = &postAlphaQueue_;
-        queue->AddWorkItem(item);
-        
         for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
         {
             item.workFunction_ = SortLightQueueWork;
@@ -1079,7 +1067,7 @@ void View::UpdateGeometries()
     queue->Complete();
 }
 
-void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
+void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQueue* alphaQueue)
 {
     Light* light = lightQueue.light_;
     Zone* zone = GetZone(drawable);
@@ -1099,11 +1087,11 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
             continue;
         
         // Do not create pixel lit forward passes for materials that render into the G-buffer
-        if ((renderMode_ == RENDER_PREPASS && tech->HasPass(PASS_PREPASS)) || (renderMode_ == RENDER_DEFERRED &&
-            tech->HasPass(PASS_DEFERRED)))
+        if (deferred_ && (tech->HasPass(PASS_PREPASS) || tech->HasPass(PASS_DEFERRED)))
             continue;
         
         Batch destBatch(srcBatch);
+        bool isLitAlpha = false;
         
         // Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
         // Also vertex lighting or ambient gradient require the non-lit base pass, so skip in those cases
@@ -1121,6 +1109,13 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
         else
             destBatch.pass_ = tech->GetPass(PASS_LIGHT);
         
+        // If no lit pass, check for lit alpha
+        if (!destBatch.pass_)
+        {
+            destBatch.pass_ = tech->GetPass(PASS_LITALPHA);
+            isLitAlpha = true;
+        }
+        
         // Skip if material does not receive light at all
         if (!destBatch.pass_)
             continue;
@@ -1129,24 +1124,29 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
         destBatch.lightQueue_ = &lightQueue;
         destBatch.zone_ = zone;
         
-        // Check from the ambient pass whether the object is opaque or transparent
-        Pass* ambientPass = tech->GetPass(PASS_BASE);
-        if (!ambientPass || ambientPass->GetBlendMode() == BLEND_REPLACE)
+        if (!isLitAlpha)
             AddBatchToQueue(lightQueue.litBatches_, destBatch, tech);
-        else
+        else if (alphaQueue)
         {
             // Transparent batches can not be instanced
-            AddBatchToQueue(alphaQueue_, destBatch, tech, false, allowTransparentShadows);
+            AddBatchToQueue(*alphaQueue, destBatch, tech, false, allowTransparentShadows);
         }
     }
 }
 
-void View::RenderBatchesForward()
+void View::ExecuteRenderPathCommands()
 {
     // If using hardware multisampling with post-processing, render to the backbuffer first and then resolve
-    bool resolve = screenBuffers_.Size() && !renderTarget_ && graphics_->GetMultiSample() > 1;
-    RenderSurface* renderTarget = (screenBuffers_.Size() && !resolve) ? screenBuffers_[0]->GetRenderSurface() : renderTarget_;
-    RenderSurface* depthStencil = GetDepthStencil(renderTarget);
+    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())
@@ -1160,252 +1160,173 @@ void View::RenderBatchesForward()
         }
     }
     
-    graphics_->SetRenderTarget(0, renderTarget);
-    graphics_->SetDepthStencil(depthStencil);
-    graphics_->SetViewport(viewRect_);
-    #ifndef GL_ES_VERSION_2_0
-    graphics_->Clear(CLEAR_DEPTH | CLEAR_STENCIL);
-    #else
-    graphics_->Clear(CLEAR_COLOR | CLEAR_DEPTH, farClipZone_->GetFogColor());
-    #endif
-    
     graphics_->SetFillMode(camera_->GetFillMode());
     
-    // Render opaque object unlit base pass
-    if (!baseQueue_.IsEmpty())
     {
-        PROFILE(RenderBase);
-        baseQueue_.Draw(this);
-    }
-    
-    // Render shadow maps + opaque objects' additive lighting
-    if (!lightQueues_.Empty())
-    {
-        PROFILE(RenderLights);
+        PROFILE(RenderCommands);
         
-        for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+        for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         {
-            // If reusing shadowmaps, render each of them before the lit batches
-            if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
+            const RenderPathCommand& command = renderPath_->commands_[i];
+            
+            switch (command.type_)
             {
-                RenderShadowMap(*i);
-                graphics_->SetRenderTarget(0, renderTarget);
-                graphics_->SetDepthStencil(depthStencil);
-                graphics_->SetViewport(viewRect_);
-                graphics_->SetFillMode(camera_->GetFillMode());
+            case CMD_CLEAR:
+                {
+                    PROFILE(ClearRenderTarget);
+                    
+                    Color clearColor = command.clearColor_;
+                    if (clearColor.r_ < 0.0f)
+                        clearColor = farClipZone_->GetFogColor();
+                    
+                    SetRenderTargets(command);
+                    graphics_->Clear(command.clearFlags_, clearColor, command.clearDepth_, command.clearStencil_);
+                }
+                break;
+                
+            case CMD_SCENEPASS:
+                if (!batchQueues_[command.pass_].IsEmpty())
+                {
+                    PROFILE(RenderScenePass);
+                    
+                    SetRenderTargets(command);
+                    SetTextures(command);
+                    batchQueues_[command.pass_].Draw(this, command.useScissor_, command.markToStencil_);
+                }
+                break;
+                
+            case CMD_FORWARDLIGHTS:
+                // Render shadow maps + opaque objects' additive lighting
+                if (!lightQueues_.Empty())
+                {
+                    PROFILE(RenderLights);
+                    
+                    SetRenderTargets(command);
+                    
+                    for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+                    {
+                        // If reusing shadowmaps, render each of them before the lit batches
+                        if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
+                        {
+                            RenderShadowMap(*i);
+                            SetRenderTargets(command);
+                            graphics_->SetFillMode(camera_->GetFillMode());
+                        }
+                        
+                        SetTextures(command);
+                        i->litBatches_.Draw(i->light_, this);
+                    }
+                    
+                    graphics_->SetScissorTest(false);
+                    graphics_->SetStencilTest(false);
+                }
+                break;
+                
+            case CMD_LIGHTVOLUMES:
+                // Render shadow maps + light volumes
+                if (!lightQueues_.Empty())
+                {
+                    PROFILE(RenderLightVolumes);
+                    
+                    SetRenderTargets(command);
+                    
+                    for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+                    {
+                        // If reusing shadowmaps, render each of them before the lit batches
+                        if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
+                        {
+                            RenderShadowMap(*i);
+                            SetRenderTargets(command);
+                        }
+                        
+                        SetTextures(command);
+                        
+                        for (unsigned j = 0; j < i->volumeBatches_.Size(); ++j)
+                        {
+                            SetupLightVolumeBatch(i->volumeBatches_[j]);
+                            i->volumeBatches_[j].Draw(this);
+                        }
+                    }
+                    
+                    graphics_->SetScissorTest(false);
+                    graphics_->SetStencilTest(false);
+                }
+                break;
             }
-            
-            i->litBatches_.Draw(i->light_, this);
         }
     }
     
-    graphics_->SetScissorTest(false);
-    graphics_->SetStencilTest(false);
-    
-    #ifndef GL_ES_VERSION_2_0
-    // At this point clear the parts of viewport not occupied by opaque geometry with fog color.
-    // On OpenGL ES an ordinary color clear has been performed beforehand instead
-    graphics_->SetBlendMode(BLEND_REPLACE);
-    graphics_->SetColorWrite(true);
-    graphics_->SetDepthTest(CMP_LESSEQUAL);
-    graphics_->SetDepthWrite(false);
-    graphics_->SetFillMode(FILL_SOLID);
-    graphics_->SetScissorTest(false);
-    graphics_->SetStencilTest(false);
-    graphics_->SetShaders(renderer_->GetVertexShader("Basic"), renderer_->GetPixelShader("Basic"));
-    graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, farClipZone_->GetFogColor());
-    graphics_->ClearParameterSource(SP_MATERIAL);
-    DrawFullscreenQuad(false);
-    #endif
-    
-    graphics_->SetFillMode(camera_->GetFillMode());
-    
-    // Render pre-alpha custom pass
-    if (!preAlphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderPreAlpha);
-        preAlphaQueue_.Draw(this);
-    }
-    
-    // Render transparent objects (both base passes & additive lighting)
-    if (!alphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderAlpha);
-        alphaQueue_.Draw(this, true);
-    }
-    
-    // Render post-alpha custom pass
-    if (!postAlphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderPostAlpha);
-        postAlphaQueue_.Draw(this);
-    }
-    
+    graphics_->SetRenderTarget(0, renderTarget_);
+    for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
+        graphics_->SetRenderTarget(i, (RenderSurface*)0);
+    graphics_->SetDepthStencil(depthStencil_);
+    graphics_->SetViewport(viewRect_);
     graphics_->SetFillMode(FILL_SOLID);
     
     // Resolve multisampled backbuffer now if necessary
-    if (resolve)
-        graphics_->ResolveToTexture(screenBuffers_[0], viewRect_);
+    //if (resolve)
+    //    graphics_->ResolveToTexture(screenBuffers_[0], viewRect_);
 }
 
-void View::RenderBatchesDeferred()
+void View::SetRenderTargets(const RenderPathCommand& command)
 {
-    // If not reusing shadowmaps, render all of them first
-    if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
+    unsigned index = 0;
+    
+    while (index < command.outputs_.Size())
     {
-        PROFILE(RenderShadowMaps);
-        
-        for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+        if (!command.outputs_[index].Compare("viewport", false))
+            graphics_->SetRenderTarget(index, usedRenderTarget_);
+        else
         {
-            if (i->shadowMap_)
-                RenderShadowMap(*i);
+            StringHash nameHash(command.outputs_[index]);
+            if (renderTargets_.Contains(nameHash))
+                graphics_->SetRenderTarget(index, renderTargets_[nameHash]);
+            else
+                graphics_->SetRenderTarget(0, (RenderSurface*)0);
         }
+        
+        ++index;
     }
     
-    // In light prepass mode the albedo buffer is used for light accumulation instead
-    Texture2D* albedoBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetRGBAFormat());
-    Texture2D* normalBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetRGBAFormat());
-    Texture2D* depthBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetLinearDepthFormat());
-    
-    RenderSurface* renderTarget = screenBuffers_.Size() ? screenBuffers_[0]->GetRenderSurface() : renderTarget_;
-    RenderSurface* depthStencil = renderer_->GetDepthStencil(rtSize_.x_, rtSize_.y_);
-    
-    if (renderMode_ == RENDER_PREPASS)
+    while (index < MAX_RENDERTARGETS)
     {
-        graphics_->SetRenderTarget(0, normalBuffer);
-        graphics_->SetRenderTarget(1, depthBuffer);
-    }
-    else
-    {
-        graphics_->SetRenderTarget(0, renderTarget);
-        graphics_->SetRenderTarget(1, albedoBuffer);
-        graphics_->SetRenderTarget(2, normalBuffer);
-        graphics_->SetRenderTarget(3, depthBuffer);
+        graphics_->SetRenderTarget(index, (RenderSurface*)0);
+        ++index;
     }
     
-    graphics_->SetDepthStencil(depthStencil);
+    graphics_->SetDepthStencil(usedDepthStencil_);
     graphics_->SetViewport(viewRect_);
-    graphics_->Clear(CLEAR_DEPTH | CLEAR_STENCIL);
-    
-    graphics_->SetFillMode(camera_->GetFillMode());
-    
-    // Render G-buffer batches
-    if (!gbufferQueue_.IsEmpty())
-    {
-        PROFILE(RenderGBuffer);
-        gbufferQueue_.Draw(this, false, true);
-    }
-    
-    graphics_->SetFillMode(FILL_SOLID);
-    
-    // Clear the light accumulation buffer (light pre-pass only.) However, skip the clear if the first light is a directional
-    // light with full mask
-    RenderSurface* lightRenderTarget = renderMode_ == RENDER_PREPASS ? albedoBuffer->GetRenderSurface() : renderTarget;
-    if (renderMode_ == RENDER_PREPASS)
-    {
-        bool optimizeLightBuffer = !hasZeroLightMask_ && !lightQueues_.Empty() && lightQueues_.Front().light_->GetLightType() ==
-            LIGHT_DIRECTIONAL && (lightQueues_.Front().light_->GetLightMask() & 0xff) == 0xff;
-        
-        graphics_->SetRenderTarget(0, lightRenderTarget);
-        graphics_->ResetRenderTarget(1);
-        graphics_->SetDepthStencil(depthStencil);
-        graphics_->SetViewport(viewRect_);
-        if (!optimizeLightBuffer)
-            graphics_->Clear(CLEAR_COLOR);
-    }
-    else
-    {
-        graphics_->ResetRenderTarget(1);
-        graphics_->ResetRenderTarget(2);
-        graphics_->ResetRenderTarget(3);
-    }
+}
+
+void View::SetTextures(const RenderPathCommand& command)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
     
-    // Render shadow maps + light volumes
-    if (!lightQueues_.Empty())
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
     {
-        PROFILE(RenderLights);
-        
-        for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+        if (!command.textureNames_[i].Empty())
         {
-            // If reusing shadowmaps, render each of them before the lit batches
-            if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
-            {
-                RenderShadowMap(*i);
-                graphics_->SetRenderTarget(0, lightRenderTarget);
-                graphics_->SetDepthStencil(depthStencil);
-                graphics_->SetViewport(viewRect_);
-            }
-            
-            if (renderMode_ == RENDER_DEFERRED)
-                graphics_->SetTexture(TU_ALBEDOBUFFER, albedoBuffer);
-            graphics_->SetTexture(TU_NORMALBUFFER, normalBuffer);
-            graphics_->SetTexture(TU_DEPTHBUFFER, depthBuffer);
-            
-            for (unsigned j = 0; j < i->volumeBatches_.Size(); ++j)
+            /// \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_);
+            else
             {
-                SetupLightVolumeBatch(i->volumeBatches_[j]);
-                i->volumeBatches_[j].Draw(this);
+                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();
+                    }
+                }
             }
         }
     }
-    
-    graphics_->SetTexture(TU_ALBEDOBUFFER, 0);
-    graphics_->SetTexture(TU_NORMALBUFFER, 0);
-    graphics_->SetTexture(TU_DEPTHBUFFER, 0);
-    
-    if (renderMode_ == RENDER_PREPASS)
-    {
-        graphics_->SetRenderTarget(0, renderTarget);
-        graphics_->SetDepthStencil(depthStencil);
-        graphics_->SetViewport(viewRect_);
-    }
-    
-    // At this point clear the parts of viewport not occupied by opaque geometry with fog color
-    graphics_->SetBlendMode(BLEND_REPLACE);
-    graphics_->SetColorWrite(true);
-    graphics_->SetDepthTest(CMP_ALWAYS);
-    graphics_->SetDepthWrite(false);
-    graphics_->SetScissorTest(false);
-    graphics_->SetStencilTest(true, CMP_EQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0);
-    graphics_->SetShaders(renderer_->GetVertexShader("Basic"), renderer_->GetPixelShader("Basic"));
-    graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, farClipZone_->GetFogColor());
-    graphics_->ClearParameterSource(SP_MATERIAL);
-    DrawFullscreenQuad(false);
-    
-    graphics_->SetFillMode(camera_->GetFillMode());
-    
-    // Render opaque objects with deferred lighting result (light pre-pass only)
-    if (!baseQueue_.IsEmpty())
-    {
-        PROFILE(RenderBase);
-        
-        graphics_->SetTexture(TU_LIGHTBUFFER, renderMode_ == RENDER_PREPASS ? albedoBuffer : 0);
-        baseQueue_.Draw(this);
-        graphics_->SetTexture(TU_LIGHTBUFFER, 0);
-    }
-    
-    // Render pre-alpha custom pass
-    if (!preAlphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderPreAlpha);
-        preAlphaQueue_.Draw(this);
-    }
-    
-    // Render transparent objects (both base passes & additive lighting)
-    if (!alphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderAlpha);
-        alphaQueue_.Draw(this, true);
-    }
-    
-    // Render post-alpha custom pass
-    if (!postAlphaQueue_.IsEmpty())
-    {
-        PROFILE(RenderPostAlpha);
-        postAlphaQueue_.Draw(this);
-    }
-    
-    graphics_->SetFillMode(FILL_SOLID);
 }
 
 void View::AllocateScreenBuffers()
@@ -1414,8 +1335,8 @@ void View::AllocateScreenBuffers()
     #ifdef USE_OPENGL
     // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
     // Also, if rendering to a texture with deferred rendering, it must be RGBA to comply with the rest of the buffers.
-    if (renderMode_ != RENDER_FORWARD && (!renderTarget_ || (renderMode_ == RENDER_DEFERRED &&
-        renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat())))
+    if (deferred_ && (!renderTarget_ || (deferred_ && renderTarget_->GetParentTexture()->GetFormat() != 
+        Graphics::GetRGBAFormat())))
         neededBuffers = 1;
     #endif
     
@@ -1429,13 +1350,34 @@ void View::AllocateScreenBuffers()
     
     unsigned format = Graphics::GetRGBFormat();
     #ifdef USE_OPENGL
-    if (renderMode_ == RENDER_DEFERRED)
+    if (deferred_)
         format = Graphics::GetRGBAFormat();
     #endif
     
     // Allocate screen buffers with filtering active in case the post-processing effects need that
     for (unsigned i = 0; i < neededBuffers; ++i)
         screenBuffers_.Push(renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, format, true));
+    
+    // Allocate extra render targets defined by the rendering path
+    for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
+    {
+        const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
+        unsigned width = rtInfo.size_.x_;
+        unsigned height = rtInfo.size_.y_;
+        if (!width || !height)
+        {
+            width = rtSize_.x_;
+            height = rtSize_.y_;
+        }
+        
+        if (rtInfo.sizeDivisor_)
+        {
+            width = rtSize_.x_ / width;
+            height = rtSize_.y_ / height;
+        }
+        
+        renderTargets_[StringHash(rtInfo.name_)] = renderer_->GetScreenBuffer(width, height, rtInfo.format_, rtInfo.filtered_);
+    }
 }
 
 void View::BlitFramebuffer()
@@ -1593,7 +1535,7 @@ void View::RunPostProcesses()
             }
             
             const String* textureNames = pass->GetTextures();
-            for (unsigned k = 0; k < MAX_MATERIAL_TEXTURE_UNITS; ++k)
+            for (unsigned k = 0; k < MAX_TEXTURE_UNITS; ++k)
             {
                 if (!textureNames[k].Empty())
                 {
@@ -2380,10 +2322,8 @@ void View::PrepareInstancingBuffer()
     
     unsigned totalInstances = 0;
     
-    totalInstances += baseQueue_.GetNumInstances();
-    totalInstances += preAlphaQueue_.GetNumInstances();
-    if (renderMode_ != RENDER_FORWARD)
-        totalInstances += gbufferQueue_.GetNumInstances();
+    for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+        totalInstances += i->second_.GetNumInstances();
     
     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
     {
@@ -2401,10 +2341,8 @@ void View::PrepareInstancingBuffer()
         if (!dest)
             return;
         
-        baseQueue_.SetTransforms(this, dest, freeIndex);
-        preAlphaQueue_.SetTransforms(this, dest, freeIndex);
-        if (renderMode_ != RENDER_FORWARD)
-            gbufferQueue_.SetTransforms(this, dest, freeIndex);
+        for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+            i->second_.SetTransforms(this, dest, freeIndex);
         
         for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
         {
@@ -2424,8 +2362,7 @@ void View::SetupLightVolumeBatch(Batch& batch)
     Vector3 cameraPos = cameraNode_->GetWorldPosition();
     float lightDist;
     
-    // Use replace blend mode for the first pre-pass light volume, and additive for the rest
-    graphics_->SetBlendMode(renderMode_ == RENDER_PREPASS && light == lightQueues_.Front().light_ ? BLEND_REPLACE : BLEND_ADD);
+    graphics_->SetBlendMode(BLEND_ADD);
     graphics_->SetDepthBias(0.0f, 0.0f);
     graphics_->SetDepthWrite(false);
     

+ 46 - 19
Engine/Graphics/View.h

@@ -43,6 +43,8 @@ class Technique;
 class Texture2D;
 class Viewport;
 class Zone;
+struct RenderPath;
+struct RenderPathCommand;
 struct WorkItem;
 
 /// Intermediate light processing result.
@@ -70,6 +72,23 @@ struct LightQueryResult
     unsigned numSplits_;
 };
 
+/// Scene render pass info.
+struct ScenePassInfo
+{
+    /// Pass name hash.
+    StringHash pass_;
+    /// Allow instancing flag.
+    bool allowInstancing_;
+    /// Mark to stencil flag.
+    bool markToStencil_;
+    /// Light scissor optimization flag.
+    bool useScissor_;
+    /// Vertex light flag.
+    bool vertexLights_;
+    /// Batch queue.
+    BatchQueue* batchQueue_;
+};
+
 /// 3D rendering view. Includes the main view(s) and any auxiliary views, but not shadow cameras.
 class View : public Object
 {
@@ -120,11 +139,13 @@ private:
     /// Update geometries and sort batches.
     void UpdateGeometries();
     /// Get pixel lit batches for a certain light and drawable.
-    void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue);
-    /// Render batches using forward rendering.
-    void RenderBatchesForward();
-    /// Render batches using light pre-pass or deferred rendering.
-    void RenderBatchesDeferred();
+    void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQueue* alphaQueue);
+    /// Execute render commands.
+    void ExecuteRenderPathCommands();
+    /// Set rendertargets for current render command.
+    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.
     void AllocateScreenBuffers();
     /// Blit the framebuffer to destination. Used in OpenGL deferred rendering modes.
@@ -198,6 +219,12 @@ private:
     OcclusionBuffer* occlusionBuffer_;
     /// Color rendertarget to use.
     RenderSurface* renderTarget_;
+    /// Depth stencil to use.
+    RenderSurface* depthStencil_;
+    /// Effective color rendertarget to use, may be different if screenbuffers are used.
+    RenderSurface* usedRenderTarget_;
+    /// Effective depth stencil to use, may be different if screenbuffers are used.
+    RenderSurface* usedDepthStencil_;
     /// Viewport rectangle.
     IntRect viewRect_;
     /// Viewport size.
@@ -212,8 +239,6 @@ private:
     float minZ_;
     /// Maximum Z value of the visible scene.
     float maxZ_;
-    /// Rendering mode.
-    RenderMode renderMode_;
     /// Material quality level.
     int materialQuality_;
     /// Maximum number of occluder triangles.
@@ -224,8 +249,10 @@ private:
     bool cameraZoneOverride_;
     /// Draw shadows flag.
     bool drawShadows_;
-    /// Whether objects with zero lightmask exist. In light pre-pass mode this means skipping some optimizations.
-    bool hasZeroLightMask_;
+    /// Deferred flag. Inferred from the existence of a light volume command in the renderpath.
+    bool deferred_;
+    /// Renderpath.
+    const RenderPath* renderPath_;
     /// Post-processing effects.
     Vector<SharedPtr<PostProcess> > postProcesses_;
     /// Intermediate screen buffers used in postprocessing and OpenGL deferred framebuffer blit.
@@ -246,24 +273,24 @@ private:
     PODVector<Drawable*> occluders_;
     /// Lights.
     PODVector<Light*> lights_;
+    /// Light volume vertex shaders.
+    PODVector<ShaderVariation*> lightVS_;
+    /// Light volume pixel shaders.
+    PODVector<ShaderVariation*> lightPS_;
     /// Drawables that limit their maximum light count.
     HashSet<Drawable*> maxLightsDrawables_;
+    /// Rendertargets defined by the renderpath.
+    HashMap<StringHash, Texture2D*> renderTargets_;
     /// Intermediate light processing results.
     Vector<LightQueryResult> lightQueryResults_;
+    /// Info for scene render passes defined by the renderpath.
+    Vector<ScenePassInfo> scenePasses_;
     /// Per-pixel light queues.
     Vector<LightBatchQueue> lightQueues_;
     /// Per-vertex light queues.
     HashMap<unsigned long long, LightBatchQueue> vertexLightQueues_;
-    /// Base pass batches.
-    BatchQueue baseQueue_;
-    /// Deferred rendering G-buffer batches.
-    BatchQueue gbufferQueue_;
-    /// Pre-transparent pass batches.
-    BatchQueue preAlphaQueue_;
-    /// Transparent geometry batches.
-    BatchQueue alphaQueue_;
-    /// Post-transparent pass batches.
-    BatchQueue postAlphaQueue_;
+    /// Batch queues.
+    HashMap<StringHash, BatchQueue> batchQueues_;
 };
 
 }

+ 86 - 54
Engine/Graphics/Viewport.cpp

@@ -26,6 +26,7 @@
 #include "Graphics.h"
 #include "Log.h"
 #include "PostProcess.h"
+#include "Renderer.h"
 #include "ResourceCache.h"
 #include "Scene.h"
 #include "StringUtils.h"
@@ -38,7 +39,7 @@ namespace Urho3D
 
 TextureUnit ParseTextureUnitName(const String& name);
 
-static const String cmdNames[] =
+static const String commandTypeNames[] =
 {
     "clear",
     "scenepass",
@@ -48,6 +49,13 @@ static const String cmdNames[] =
     ""
 };
 
+static const String sortModeNames[] =
+{
+    "fronttoback"
+    "backtofront",
+    ""
+};
+
 OBJECTTYPESTATIC(Viewport);
 
 Viewport::Viewport(Context* context) :
@@ -117,8 +125,9 @@ void Viewport::SetRect(const IntRect& rect)
 bool Viewport::SetRenderPath(XMLFile* file)
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();
-    if (!file && cache)
-        file = cache->GetResource<XMLFile>("Data/RenderPaths/Forward.xml");
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (!file && cache && renderer)
+        file = cache->GetResource<XMLFile>(renderer->GetDefaultRenderPathName());
     if (!file)
         return false;
     
@@ -126,6 +135,9 @@ bool Viewport::SetRenderPath(XMLFile* file)
     if (!rootElem)
         return false;
     
+    renderPath_.renderTargets_.Clear();
+    renderPath_.commands_.Clear();
+    
     XMLElement rtElem = rootElem.GetChild("rendertarget");
     while (rtElem)
     {
@@ -161,83 +173,101 @@ bool Viewport::SetRenderPath(XMLFile* file)
         
         if (!name.Trimmed().Empty())
         {
-            RenderTargetInfo target;
-            target.name_ = name;
-            target.format_ = format;
-            target.size_ = IntVector2(width, height),
-            target.sizeDivisor_ = sizeDivisor;
-            target.filtered_ = filtered;
-            renderPath_.renderTargets_[StringHash(name)] = target;
+            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 cmdElem = rootElem.GetChild("command");
-    while (cmdElem)
+    XMLElement commandElem = rootElem.GetChild("command");
+    while (commandElem)
     {
-        RenderCommandType type = (RenderCommandType)GetStringListIndex(cmdElem.GetAttributeLower("type"), cmdNames, CMD_UNKNOWN);
+        RenderCommandType type = (RenderCommandType)GetStringListIndex(commandElem.GetAttributeLower("type"), commandTypeNames,
+            CMD_UNKNOWN);
         if (type != CMD_UNKNOWN)
         {
-            RenderPathCommand cmd;
-            cmd.type_ = type;
-            cmd.passName_ = cmdElem.GetAttribute("pass");
-            cmd.vertexShaderName_ = cmdElem.GetAttribute("vs");
-            cmd.pixelShaderName_ = cmdElem.GetAttribute("ps");
+            RenderPathCommand command;
+            command.type_ = type;
             
-            if (type == CMD_CLEAR)
+            switch (type)
             {
-                cmd.clearFlags_ = 0;
-                if (cmdElem.HasAttribute("color"))
+            case CMD_CLEAR:
+                if (commandElem.HasAttribute("color"))
                 {
-                    cmd.clearFlags_ |= CLEAR_COLOR;
+                    command.clearFlags_ |= CLEAR_COLOR;
                     // Mark fog color with negative values
-                    if (cmdElem.GetAttributeLower("color") == "fog")
-                        cmd.clearColor_ = Color(-1.0f, -1.0f, -1.0f, -1.0f);
+                    if (commandElem.GetAttributeLower("color") == "fog")
+                        command.clearColor_ = Color(-1.0f, -1.0f, -1.0f, -1.0f);
                     else
-                        cmd.clearColor_ = cmdElem.GetColor("color");
+                        command.clearColor_ = commandElem.GetColor("color");
+                }
+                if (commandElem.HasAttribute("depth"))
+                {
+                    command.clearFlags_ |= CLEAR_DEPTH;
+                    command.clearDepth_ = commandElem.GetFloat("depth");
                 }
-                if (cmdElem.HasAttribute("depth"))
+                if (commandElem.HasAttribute("stencil"))
                 {
-                    cmd.clearFlags_ |= CLEAR_DEPTH;
-                    cmd.clearDepth_ = cmdElem.GetFloat("depth");
+                    command.clearFlags_ |= CLEAR_STENCIL;
+                    command.clearStencil_ = commandElem.GetInt("stencil");
                 }
-                if (cmdElem.HasAttribute("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)
                 {
-                    cmd.clearFlags_ |= CLEAR_STENCIL;
-                    cmd.clearStencil_ = cmdElem.GetInt("stencil");
+                    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
-            cmd.outputs_.Push("viewport");
-            if (cmdElem.HasAttribute("output"))
-                cmd.outputs_[0] = cmdElem.GetAttribute("output");
+            command.outputs_.Push("viewport");
+            if (commandElem.HasAttribute("output"))
+                command.outputs_[0] = commandElem.GetAttribute("output");
             // Check for defining multiple outputs
-            XMLElement outputElem = cmdElem.GetChild("output");
+            XMLElement outputElem = commandElem.GetChild("output");
             while (outputElem)
             {
                 unsigned index = outputElem.GetInt("index");
                 if (index < MAX_RENDERTARGETS)
                 {
-                    if (index >= cmd.outputs_.Size())
-                        cmd.outputs_.Resize(index + 1);
-                    cmd.outputs_[index] = outputElem.GetAttribute("name");
+                    if (index >= command.outputs_.Size())
+                        command.outputs_.Resize(index + 1);
+                    command.outputs_[index] = outputElem.GetAttribute("name");
                 }
                 outputElem = outputElem.GetNext("output");
             }
             
-            XMLElement parameterElem = cmdElem.GetChild("parameter");
-            while (parameterElem)
-            {
-                String name = parameterElem.GetAttribute("name");
-                Vector4 value = parameterElem.GetVector("value");
-                cmd.shaderParameters_[StringHash(name)] = value;
-                
-                parameterElem = parameterElem.GetNext("parameter");
-            }
-            
-            XMLElement textureElem = cmdElem.GetChild("texture");
+            XMLElement textureElem = commandElem.GetChild("texture");
             while (textureElem)
             {
                 TextureUnit unit = TU_DIFFUSE;
@@ -247,23 +277,25 @@ bool Viewport::SetRenderPath(XMLFile* file)
                     if (unitName.Length() > 1)
                     {
                         unit = ParseTextureUnitName(unitName);
-                        if (unit == MAX_MATERIAL_TEXTURE_UNITS)
+                        if (unit >= MAX_TEXTURE_UNITS)
                             LOGERROR("Unknown texture unit " + unitName);
                     }
                     else
-                        unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_MATERIAL_TEXTURE_UNITS - 1);
+                        unit = (TextureUnit)Clamp(ToInt(unitName), 0, MAX_TEXTURE_UNITS - 1);
                 }
-                if (unit != MAX_TEXTURE_UNITS)
+                if (unit < MAX_TEXTURE_UNITS)
                 {
                     String name = textureElem.GetAttribute("name");
-                    cmd.textureNames_[unit] = name;
+                    command.textureNames_[unit] = name;
                 }
                 
                 textureElem = textureElem.GetNext("texture");
             }
+            
+            renderPath_.commands_.Push(command);
         }
         
-        cmdElem = cmdElem.GetNext("command");
+        commandElem = commandElem.GetNext("command");
     }
     
     return true;

+ 18 - 5
Engine/Graphics/Viewport.h

@@ -48,8 +48,7 @@ enum RenderCommandType
 /// Rendering path sorting modes.
 enum RenderCommandSortMode
 {
-    SORT_STATE = 0,
-    SORT_FRONTTOBACK,
+    SORT_FRONTTOBACK = 0,
     SORT_BACKTOFRONT
 };
 
@@ -71,12 +70,20 @@ struct RenderTargetInfo
 /// Rendering path command.
 struct RenderPathCommand
 {
+    RenderPathCommand() :
+        clearFlags_(0),
+        markToStencil_(false),
+        useScissor_(false),
+        vertexLights_(false)
+    {
+    }
+    
     /// Command type.
     RenderCommandType type_;
     /// Sorting mode.
     RenderCommandSortMode sortMode_;
-    /// Scene pass name.
-    String passName_;
+    /// Scene pass hash.
+    StringHash pass_;
     /// Clear flags.
     unsigned clearFlags_;
     /// Clear color.
@@ -85,12 +92,18 @@ struct RenderPathCommand
     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_MATERIAL_TEXTURE_UNITS];
+    String textureNames_[MAX_TEXTURE_UNITS];
     /// %Shader parameters.
     HashMap<StringHash, Vector4> shaderParameters_;
     /// Output rendertarget names.

+ 0 - 2
Urho3D/Urho3D.cpp

@@ -75,8 +75,6 @@ void Run()
                 "-r<freq>    Sound mixing frequency in Hz\n"
                 "-headless   Headless mode. No application window will be created\n"
                 "-logdebug   Display debug level log messages also in release mode\n"
-                "-prepass    Use light pre-pass rendering\n"
-                "-deferred   Use deferred rendering\n"
                 "-lqshadows  Use low-quality (1-sample) shadow filtering\n"
                 "-noshadows  Disable shadow rendering\n"
                 "-nolimit    Disable frame limiter\n"