Browse Source

Merge branch 'renderpath-cubemap'

Conflicts:
	Source/Urho3D/Graphics/View.cpp
Lasse Öörni 10 years ago
parent
commit
318a3db786

+ 11 - 6
Docs/Reference.dox

@@ -1159,14 +1159,17 @@ Note that the used shader variations will vary with graphics settings, for examp
 
 
 %Scene rendering and any post-processing on a Viewport is defined by its RenderPath object, which can either be read from an XML file or be created programmatically.
 %Scene rendering and any post-processing on a Viewport is defined by its RenderPath object, which can either be read from an XML file or be created programmatically.
 
 
-The render path consists of rendertarget definitions and commands. The commands are executed in order to yield the rendering result. Each command outputs either to the destination rendertarget & viewport (default if output definition is omitted), or one of the named rendertargets. MRT output is also possible.
+The render path consists of rendertarget definitions and commands. The commands are executed in order to yield the rendering result. Each command outputs either to the destination rendertarget & viewport (default if output definition is omitted), or one of the named rendertargets. MRT output is also possible. If the rendertarget is a cube map,
+the face to render to (0-5) can also be specified.
 
 
 A rendertarget's size can be either absolute or multiply or divide the destination viewport size. The multiplier or divisor does not need to be an integer number. Furthermore, a rendertarget can be declared "persistent" so that it will not be mixed with other rendertargets of the same size and format, and its contents can be assumed to be available also on subsequent frames.
 A rendertarget's size can be either absolute or multiply or divide the destination viewport size. The multiplier or divisor does not need to be an integer number. Furthermore, a rendertarget can be declared "persistent" so that it will not be mixed with other rendertargets of the same size and format, and its contents can be assumed to be available also on subsequent frames.
 
 
+Note that if you already have created a named rendertarget texture in code and have stored it into the resource cache by using \ref ResourceCache::AddManualResource "AddManualResource()" you can use it directly as an output (by referring to its name) without requiring a rendertarget definition for it.
+
 The available commands are:
 The available commands are:
 
 
 - clear: Clear any of color, depth and stencil. Color clear can optionally use the fog color from the Zone visible at the far clip distance.
 - clear: Clear any of color, depth and stencil. Color clear can optionally use the fog color from the Zone visible at the far clip distance.
-- scenepass: Render scene objects whose \ref Materials "material technique" contains the specified pass. Will either be front-to-back ordered with state sorting, or back-to-front ordered with no state sorting. For deferred rendering, object lightmasks can be optionally marked to the stencil buffer. Vertex lights can optionally be handled during a pass, if it has the necessary shader combinations. Textures global to the pass can be bound to free texture units; these can either be the viewport, a named rendertarget, or a 2D texture resource identified with its pathname.
+- scenepass: Render scene objects whose \ref Materials "material technique" contains the specified pass. Will either be front-to-back ordered with state sorting, or back-to-front ordered with no state sorting. For deferred rendering, object lightmasks can be optionally marked to the stencil buffer. Vertex lights can optionally be handled during a pass, if it has the necessary shader combinations. Textures global to the pass can be bound to free texture units; these can either be the viewport, a named rendertarget, or a texture resource identified with its pathname.
 - quad: Render a viewport-sized quad using the specified shaders and compilation defines. Textures can be bound and additionally shader parameters can be specified.
 - quad: Render a viewport-sized quad using the specified shaders and compilation defines. Textures can be bound and additionally shader parameters can be specified.
 - forwardlights: Render per-pixel forward lighting for opaque objects with the specified pass name. Shadow maps are also rendered as necessary.
 - forwardlights: Render per-pixel forward lighting for opaque objects with the specified pass name. Shadow maps are also rendered as necessary.
 - lightvolumes: Render deferred light volumes using the specified shaders. G-buffer textures can be bound as necessary.
 - lightvolumes: Render deferred light volumes using the specified shaders. G-buffer textures can be bound as necessary.
@@ -1180,11 +1183,11 @@ The render path XML definition looks like this:
 
 
 \code
 \code
 <renderpath>
 <renderpath>
-    <rendertarget name="RTName" tag="TagName" enabled="true|false" size="x y"|sizedivisor="x y"|sizemultiplier="x y"
+    <rendertarget name="RTName" tag="TagName" enabled="true|false" cubemap="true|false" size="x y"|sizedivisor="x y"|sizemultiplier="x y"
         format="rgb|rgba|r32f|rgba16|rgba16f|rgba32f|rg16|rg16f|rg32f|lineardepth|readabledepth" filter="true|false" srgb="true|false" persistent="true|false" />
         format="rgb|rgba|r32f|rgba16|rgba16f|rgba32f|rg16|rg16f|rg32f|lineardepth|readabledepth" filter="true|false" srgb="true|false" persistent="true|false" />
-    <command type="clear" tag="TagName" enabled="true|false" clearcolor="r g b a|fog" cleardepth="x" clearstencil="y" output="viewport|RTName" depthstencil="DSName" />
+    <command type="clear" tag="TagName" enabled="true|false" clearcolor="r g b a|fog" cleardepth="x" clearstencil="y" output="viewport|RTName" face="0|1|2|3|4|5" depthstencil="DSName" />
     <command type="scenepass" pass="PassName" sort="fronttoback|backtofront" marktostencil="true|false" vertexlights="true|false" metadata="base|alpha|gbuffer" depthstencil="DSName">
     <command type="scenepass" pass="PassName" sort="fronttoback|backtofront" marktostencil="true|false" vertexlights="true|false" metadata="base|alpha|gbuffer" depthstencil="DSName">
-        <output index="0" name="RTName1" />
+        <output index="0" name="RTName1" face="0|1|2|3|4|5" />
         <output index="1" name="RTName2" />
         <output index="1" name="RTName2" />
         <output index="2" name="RTName3" />
         <output index="2" name="RTName3" />
         <texture unit="unit" name="viewport|RTName|TextureName" />
         <texture unit="unit" name="viewport|RTName|TextureName" />
@@ -1207,7 +1210,9 @@ For examples of renderpath definitions, see the default forward, deferred and li
 
 
 Normally needed depth-stencil surfaces are automatically allocated when the render path is executed.
 Normally needed depth-stencil surfaces are automatically allocated when the render path is executed.
 
 
-The special "lineardepth" (synonym "depth") format is intended for storing scene depth in deferred rendering. It is not an actual hardware depth-stencil texture, but will be D3DFMT_R32F on Direct3D9 and RGBA on OpenGL, due to the limitation of all color buffers having to be the same format. The shader include file Samplers.glsl in bin/CoreData/Shaders/GLSL provides functions to encode and decode linear depth to RGB. Writing depth manually to a rendertarget, while using a non-readable depth-stencil surface ensures best compatibility and prevents any conflicts from using both depth test and manual depth sampling at the same time.
+The special "lineardepth" (synonym "depth") format is intended for storing scene depth in deferred rendering. It is not an actual hardware depth-stencil texture, but a 32-bit single channel (R) float rendertarget. (On OpenGL2 it's RGBA instead, due to the limitation of all color buffers having to be the same format. The shader include file Samplers.glsl in bin/CoreData/Shaders/GLSL provides functions to encode and decode linear depth to RGB.) 
+
+Writing depth manually to a rendertarget, while using a non-readable depth-stencil surface ensures best compatibility and prevents any conflicts from using both depth test and manual depth sampling at the same time.
 
 
 There is also a possibility to define a readable hardware depth texture, and instruct the render path to use it instead. Availability for this must first be checked with the function \ref Graphics::GetReadableDepthSupport "GetReadableDepthSupport()". On Direct3D9 this will use the INTZ "hack" format. To define a readable depth-stencil texture, use the format "readabledepth" (synonym "hwdepth") and set it as the depth-stencil by using the "depthstencil" attribute in render path commands. Note that you must set it in every command where you want to use it, otherwise an automatically allocated depth-stencil will be used. Note also that the existence of a stencil channel is not guaranteed, so stencil masking optimizations for lights normally used by the Renderer & View classes will be disabled.
 There is also a possibility to define a readable hardware depth texture, and instruct the render path to use it instead. Availability for this must first be checked with the function \ref Graphics::GetReadableDepthSupport "GetReadableDepthSupport()". On Direct3D9 this will use the INTZ "hack" format. To define a readable depth-stencil texture, use the format "readabledepth" (synonym "hwdepth") and set it as the depth-stencil by using the "depthstencil" attribute in render path commands. Note that you must set it in every command where you want to use it, otherwise an automatically allocated depth-stencil will be used. Note also that the existence of a stencil channel is not guaranteed, so stencil masking optimizations for lights normally used by the Renderer & View classes will be disabled.
 
 

+ 39 - 11
Source/Urho3D/Graphics/RenderPath.cpp

@@ -59,6 +59,8 @@ void RenderTargetInfo::Load(const XMLElement& element)
     tag_ = element.GetAttribute("tag");
     tag_ = element.GetAttribute("tag");
     if (element.HasAttribute("enabled"))
     if (element.HasAttribute("enabled"))
         enabled_ = element.GetBool("enabled");
         enabled_ = element.GetBool("enabled");
+    if (element.HasAttribute("cubemap"))
+        cubemap_ = element.GetBool("cubemap");
     
     
     String formatName = element.GetAttribute("format");
     String formatName = element.GetAttribute("format");
     format_ = Graphics::GetFormat(formatName);
     format_ = Graphics::GetFormat(formatName);
@@ -169,9 +171,12 @@ void RenderPathCommand::Load(const XMLElement& element)
     }
     }
     
     
     // By default use 1 output, which is the viewport
     // By default use 1 output, which is the viewport
-    outputNames_.Push("viewport");
+    outputs_.Resize(1);
+    outputs_[0] = MakePair(String("viewport"), FACE_POSITIVE_X);
     if (element.HasAttribute("output"))
     if (element.HasAttribute("output"))
-        outputNames_[0] = element.GetAttribute("output");
+        outputs_[0].first_ = element.GetAttribute("output");
+    if (element.HasAttribute("face"))
+        outputs_[0].second_ = (CubeMapFace)element.GetInt("face");
     if (element.HasAttribute("depthstencil"))
     if (element.HasAttribute("depthstencil"))
         depthStencilName_ = element.GetAttribute("depthstencil");
         depthStencilName_ = element.GetAttribute("depthstencil");
     // Check for defining multiple outputs
     // Check for defining multiple outputs
@@ -181,9 +186,10 @@ void RenderPathCommand::Load(const XMLElement& element)
         unsigned index = outputElem.GetInt("index");
         unsigned index = outputElem.GetInt("index");
         if (index < MAX_RENDERTARGETS)
         if (index < MAX_RENDERTARGETS)
         {
         {
-            if (index >= outputNames_.Size())
-                outputNames_.Resize(index + 1);
-            outputNames_[index] = outputElem.GetAttribute("name");
+            if (index >= outputs_.Size())
+                outputs_.Resize(index + 1);
+            outputs_[index].first_ = outputElem.GetAttribute("name");
+            outputs_[index].second_ = outputElem.HasAttribute("face") ? (CubeMapFace)outputElem.GetInt("face") : FACE_POSITIVE_X;
         }
         }
         outputElem = outputElem.GetNext("output");
         outputElem = outputElem.GetNext("output");
     }
     }
@@ -223,17 +229,34 @@ void RenderPathCommand::RemoveShaderParameter(const String& name)
 void RenderPathCommand::SetNumOutputs(unsigned num)
 void RenderPathCommand::SetNumOutputs(unsigned num)
 {
 {
     num = Clamp((int)num, 1, MAX_RENDERTARGETS);
     num = Clamp((int)num, 1, MAX_RENDERTARGETS);
-    outputNames_.Resize(num);
+    outputs_.Resize(num);
+}
+
+void RenderPathCommand::SetOutput(unsigned index, const String& name, CubeMapFace face)
+{
+    if (index < outputs_.Size())
+        outputs_[index] = MakePair(name, face);
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(name, face));
 }
 }
 
 
 void RenderPathCommand::SetOutputName(unsigned index, const String& name)
 void RenderPathCommand::SetOutputName(unsigned index, const String& name)
 {
 {
-    if (index < outputNames_.Size())
-        outputNames_[index] = name;
-    else if (index == outputNames_.Size() && index < MAX_RENDERTARGETS)
-        outputNames_.Push(name);
+    if (index < outputs_.Size())
+        outputs_[index].first_ = name;
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(name, FACE_POSITIVE_X));
+}
+
+void RenderPathCommand::SetOutputFace(unsigned index, CubeMapFace face)
+{
+    if (index < outputs_.Size())
+        outputs_[index].second_ = face;
+    else if (index == outputs_.Size() && index < MAX_RENDERTARGETS)
+        outputs_.Push(MakePair(String::EMPTY, face));
 }
 }
 
 
+
 void RenderPathCommand::SetDepthStencilName(const String& name)
 void RenderPathCommand::SetDepthStencilName(const String& name)
 {
 {
     depthStencilName_ = name;
     depthStencilName_ = name;
@@ -252,7 +275,12 @@ const Variant& RenderPathCommand::GetShaderParameter(const String& name) const
 
 
 const String& RenderPathCommand::GetOutputName(unsigned index) const
 const String& RenderPathCommand::GetOutputName(unsigned index) const
 {
 {
-    return index < outputNames_.Size() ? outputNames_[index] : String::EMPTY;
+    return index < outputs_.Size() ? outputs_[index].first_ : String::EMPTY;
+}
+
+CubeMapFace RenderPathCommand::GetOutputFace(unsigned index) const
+{
+    return index < outputs_.Size() ? outputs_[index].second_ : FACE_POSITIVE_X;
 }
 }
 
 
 RenderPath::RenderPath()
 RenderPath::RenderPath()

+ 12 - 3
Source/Urho3D/Graphics/RenderPath.h

@@ -69,6 +69,7 @@ struct RenderTargetInfo
         size_(Vector2::ZERO),
         size_(Vector2::ZERO),
         sizeMode_(SIZE_ABSOLUTE),
         sizeMode_(SIZE_ABSOLUTE),
         enabled_(true),
         enabled_(true),
+        cubemap_(false),
         filtered_(false),
         filtered_(false),
         sRGB_(false),
         sRGB_(false),
         persistent_(false)
         persistent_(false)
@@ -90,6 +91,8 @@ struct RenderTargetInfo
     RenderTargetSizeMode sizeMode_;
     RenderTargetSizeMode sizeMode_;
     /// Enabled flag.
     /// Enabled flag.
     bool enabled_;
     bool enabled_;
+    /// Cube map flag.
+    bool cubemap_;
     /// Filtering flag.
     /// Filtering flag.
     bool filtered_;
     bool filtered_;
     /// sRGB sampling/writing mode flag.
     /// sRGB sampling/writing mode flag.
@@ -122,8 +125,12 @@ struct RenderPathCommand
     void RemoveShaderParameter(const String& name);
     void RemoveShaderParameter(const String& name);
     /// Set number of output rendertargets.
     /// Set number of output rendertargets.
     void SetNumOutputs(unsigned num);
     void SetNumOutputs(unsigned num);
+    /// Set output rendertarget name and face index for cube maps.
+    void SetOutput(unsigned index, const String& name, CubeMapFace face = FACE_POSITIVE_X);
     /// Set output rendertarget name.
     /// Set output rendertarget name.
     void SetOutputName(unsigned index, const String& name);
     void SetOutputName(unsigned index, const String& name);
+    /// Set output rendertarget face index for cube maps.
+    void SetOutputFace(unsigned index, CubeMapFace face);
     /// Set depth-stencil output name. When empty, will assign a depth-stencil buffer automatically.
     /// Set depth-stencil output name. When empty, will assign a depth-stencil buffer automatically.
     void SetDepthStencilName(const String& name);
     void SetDepthStencilName(const String& name);
     
     
@@ -132,9 +139,11 @@ struct RenderPathCommand
     /// Return shader parameter.
     /// Return shader parameter.
     const Variant& GetShaderParameter(const String& name) const;
     const Variant& GetShaderParameter(const String& name) const;
     /// Return number of output rendertargets.
     /// Return number of output rendertargets.
-    unsigned GetNumOutputs() const { return outputNames_.Size(); }
+    unsigned GetNumOutputs() const { return outputs_.Size(); }
     /// Return output rendertarget name.
     /// Return output rendertarget name.
     const String& GetOutputName(unsigned index) const;
     const String& GetOutputName(unsigned index) const;
+    /// Return output rendertarget face index.
+    CubeMapFace GetOutputFace(unsigned index) const;
     /// Return depth-stencil output name.
     /// Return depth-stencil output name.
     const String& GetDepthStencilName() const { return depthStencilName_; }
     const String& GetDepthStencilName() const { return depthStencilName_; }
     
     
@@ -162,8 +171,8 @@ struct RenderPathCommand
     String textureNames_[MAX_TEXTURE_UNITS];
     String textureNames_[MAX_TEXTURE_UNITS];
     /// %Shader parameters.
     /// %Shader parameters.
     HashMap<StringHash, Variant> shaderParameters_;
     HashMap<StringHash, Variant> shaderParameters_;
-    /// Output rendertarget names.
-    Vector<String> outputNames_;
+    /// Output rendertarget names and faces.
+    Vector<Pair<String, CubeMapFace> > outputs_;
     /// Depth-stencil output name.
     /// Depth-stencil output name.
     String depthStencilName_;
     String depthStencilName_;
     /// Clear flags.
     /// Clear flags.

+ 47 - 23
Source/Urho3D/Graphics/Renderer.cpp

@@ -921,7 +921,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     return newShadowMap;
     return newShadowMap;
 }
 }
 
 
-Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool filtered, bool srgb, unsigned persistentKey)
+Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb, unsigned persistentKey)
 {
 {
     bool depthStencil = (format == Graphics::GetDepthStencilFormat()) || (format == Graphics::GetReadableDepthFormat());
     bool depthStencil = (format == Graphics::GetDepthStencilFormat()) || (format == Graphics::GetReadableDepthFormat());
     if (depthStencil)
     if (depthStencil)
@@ -930,11 +930,16 @@ Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, boo
         srgb = false;
         srgb = false;
     }
     }
     
     
+    if (cubemap)
+        height = width;
+
     long long searchKey = ((long long)format << 32) | (width << 16) | height;
     long long searchKey = ((long long)format << 32) | (width << 16) | height;
     if (filtered)
     if (filtered)
         searchKey |= 0x8000000000000000LL;
         searchKey |= 0x8000000000000000LL;
     if (srgb)
     if (srgb)
         searchKey |= 0x4000000000000000LL;
         searchKey |= 0x4000000000000000LL;
+    if (cubemap)
+        searchKey |= 0x2000000000000000LL;
     
     
     // Add persistent key if defined
     // Add persistent key if defined
     if (persistentKey)
     if (persistentKey)
@@ -951,32 +956,48 @@ Texture2D* Renderer::GetScreenBuffer(int width, int height, unsigned format, boo
     
     
     if (allocations >= screenBuffers_[searchKey].Size())
     if (allocations >= screenBuffers_[searchKey].Size())
     {
     {
-        SharedPtr<Texture2D> newBuffer(new Texture2D(context_));
+        SharedPtr<Texture> newBuffer;
+
+        if (!cubemap)
+        {
+            SharedPtr<Texture2D> newTex2D(new Texture2D(context_));
+            newTex2D->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET);
+
+            #ifdef URHO3D_OPENGL
+            // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
+            // Otherwise eg. the AutoExposure post process will not work correctly
+            if (persistentKey && Texture::GetDataType(format) == GL_FLOAT)
+            {
+                // Note: this loses current rendertarget assignment
+                graphics_->ResetRenderTargets();
+                graphics_->SetRenderTarget(0, newTex2D);
+                graphics_->SetDepthStencil((RenderSurface*)0);
+                graphics_->SetViewport(IntRect(0, 0, width, height));
+                graphics_->Clear(CLEAR_COLOR);
+            }
+            #endif
+
+            newBuffer = StaticCast<Texture>(newTex2D);
+        }
+        else
+        {
+            SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
+            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET);
+
+            newBuffer = StaticCast<Texture>(newTexCube);
+        }
+        
         newBuffer->SetSRGB(srgb);
         newBuffer->SetSRGB(srgb);
-        newBuffer->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET);
         newBuffer->SetFilterMode(filtered ? FILTER_BILINEAR : FILTER_NEAREST);
         newBuffer->SetFilterMode(filtered ? FILTER_BILINEAR : FILTER_NEAREST);
         newBuffer->ResetUseTimer();
         newBuffer->ResetUseTimer();
         screenBuffers_[searchKey].Push(newBuffer);
         screenBuffers_[searchKey].Push(newBuffer);
-        #ifdef URHO3D_OPENGL
-        // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
-        // Otherwise eg. the AutoExposure post process will not work correctly
-        if (persistentKey && Texture::GetDataType(format) == GL_FLOAT)
-        {
-            // Note: this loses current rendertarget assignment
-            graphics_->ResetRenderTargets();
-            graphics_->SetRenderTarget(0, newBuffer);
-            graphics_->SetDepthStencil((RenderSurface*)0);
-            graphics_->SetViewport(IntRect(0, 0, width, height));
-            graphics_->Clear(CLEAR_COLOR);
-        }
-        #endif
-        
+
         LOGDEBUG("Allocated new screen buffer size " + String(width) + "x" + String(height) + " format " + String(format));
         LOGDEBUG("Allocated new screen buffer size " + String(width) + "x" + String(height) + " format " + String(format));
         return newBuffer;
         return newBuffer;
     }
     }
     else
     else
     {
     {
-        Texture2D* buffer = screenBuffers_[searchKey][allocations];
+        Texture* buffer = screenBuffers_[searchKey][allocations];
         buffer->ResetUseTimer();
         buffer->ResetUseTimer();
         return buffer;
         return buffer;
     }
     }
@@ -989,7 +1010,10 @@ RenderSurface* Renderer::GetDepthStencil(int width, int height)
     if (width == graphics_->GetWidth() && height == graphics_->GetHeight() && graphics_->GetMultiSample() <= 1)
     if (width == graphics_->GetWidth() && height == graphics_->GetHeight() && graphics_->GetMultiSample() <= 1)
         return 0;
         return 0;
     else
     else
-        return GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), false, false)->GetRenderSurface();
+    {
+        return static_cast<Texture2D*>(GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), false, false, false))->
+            GetRenderSurface();
+    }
 }
 }
 
 
 OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
 OcclusionBuffer* Renderer::GetOcclusionBuffer(Camera* camera)
@@ -1360,13 +1384,13 @@ void Renderer::RemoveUnusedBuffers()
         }
         }
     }
     }
     
     
-    for (HashMap<long long, Vector<SharedPtr<Texture2D> > >::Iterator i = screenBuffers_.Begin(); i != screenBuffers_.End();)
+    for (HashMap<long long, Vector<SharedPtr<Texture> > >::Iterator i = screenBuffers_.Begin(); i != screenBuffers_.End();)
     {
     {
-        HashMap<long long, Vector<SharedPtr<Texture2D> > >::Iterator current = i++;
-        Vector<SharedPtr<Texture2D> >& buffers = current->second_;
+        HashMap<long long, Vector<SharedPtr<Texture> > >::Iterator current = i++;
+        Vector<SharedPtr<Texture> >& buffers = current->second_;
         for (unsigned j = buffers.Size() - 1; j < buffers.Size(); --j)
         for (unsigned j = buffers.Size() - 1; j < buffers.Size(); --j)
         {
         {
-            Texture2D* buffer = buffers[j];
+            Texture* buffer = buffers[j];
             if (buffer->GetUseTimer() > MAX_BUFFER_AGE)
             if (buffer->GetUseTimer() > MAX_BUFFER_AGE)
             {
             {
                 LOGDEBUG("Removed unused screen buffer size " + String(buffer->GetWidth()) + "x" + String(buffer->GetHeight()) + " format " + String(buffer->GetFormat()));
                 LOGDEBUG("Removed unused screen buffer size " + String(buffer->GetWidth()) + "x" + String(buffer->GetHeight()) + " format " + String(buffer->GetFormat()));

+ 2 - 2
Source/Urho3D/Graphics/Renderer.h

@@ -299,7 +299,7 @@ public:
     /// Allocate a shadow map. If shadow map reuse is disabled, a different map is returned each time.
     /// Allocate a shadow map. If shadow map reuse is disabled, a different map is returned each time.
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
     /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
     /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
-    Texture2D* GetScreenBuffer(int width, int height, unsigned format, bool filtered, bool srgb, unsigned persistentKey = 0);
+    Texture* GetScreenBuffer(int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb, unsigned persistentKey = 0);
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
     RenderSurface* GetDepthStencil(int width, int height);
     RenderSurface* GetDepthStencil(int width, int height);
     /// Allocate an occlusion buffer.
     /// Allocate an occlusion buffer.
@@ -394,7 +394,7 @@ private:
     /// Shadow map allocations by resolution.
     /// Shadow map allocations by resolution.
     HashMap<int, PODVector<Light*> > shadowMapAllocations_;
     HashMap<int, PODVector<Light*> > shadowMapAllocations_;
     /// Screen buffers by resolution and format.
     /// Screen buffers by resolution and format.
-    HashMap<long long, Vector<SharedPtr<Texture2D> > > screenBuffers_;
+    HashMap<long long, Vector<SharedPtr<Texture> > > screenBuffers_;
     /// Current screen buffer allocations by resolution and format.
     /// Current screen buffer allocations by resolution and format.
     HashMap<long long, unsigned> screenBufferAllocations_;
     HashMap<long long, unsigned> screenBufferAllocations_;
     /// Saved status of screen buffer allocations for restoring.
     /// Saved status of screen buffer allocations for restoring.

+ 99 - 74
Source/Urho3D/Graphics/View.cpp

@@ -436,7 +436,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             continue;
             continue;
         
         
         // Check if ambient pass and G-buffer rendering happens at the same time
         // Check if ambient pass and G-buffer rendering happens at the same time
-        if (command.type_ == CMD_SCENEPASS && command.outputNames_.Size() > 1)
+        if (command.type_ == CMD_SCENEPASS && command.outputs_.Size() > 1)
         {
         {
             if (CheckViewportWrite(command))
             if (CheckViewportWrite(command))
                 deferredAmbient_ = true;
                 deferredAmbient_ = true;
@@ -627,7 +627,7 @@ void View::Render()
     
     
     // Run framebuffer blitting if necessary
     // Run framebuffer blitting if necessary
     if (currentRenderTarget_ != renderTarget_)
     if (currentRenderTarget_ != renderTarget_)
-        BlitFramebuffer(static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture()), renderTarget_, true);
+        BlitFramebuffer(currentRenderTarget_->GetParentTexture(), renderTarget_, true);
     
     
     // "Forget" the scene, camera, octree and zone after rendering
     // "Forget" the scene, camera, octree and zone after rendering
     scene_ = 0;
     scene_ = 0;
@@ -1433,7 +1433,7 @@ void View::ExecuteRenderPathCommands()
                 {
                 {
                     if (!currentRenderTarget_)
                     if (!currentRenderTarget_)
                     {
                     {
-                        graphics_->ResolveToTexture(viewportTextures_[0], viewRect_);
+                        graphics_->ResolveToTexture(dynamic_cast<Texture2D*>(viewportTextures_[0]), viewRect_);
                         currentViewportTexture_ = viewportTextures_[0];
                         currentViewportTexture_ = viewportTextures_[0];
                         viewportModified = false;
                         viewportModified = false;
                     }
                     }
@@ -1441,8 +1441,8 @@ void View::ExecuteRenderPathCommands()
                     {
                     {
                         if (viewportWrite)
                         if (viewportWrite)
                         {
                         {
-                            BlitFramebuffer(static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture()),
-                                viewportTextures_[0]->GetRenderSurface(), false);
+                            BlitFramebuffer(currentRenderTarget_->GetParentTexture(),
+                                GetRenderSurfaceFromTexture(viewportTextures_[0]), false);
                             currentViewportTexture_ = viewportTextures_[0];
                             currentViewportTexture_ = viewportTextures_[0];
                             viewportModified = false;
                             viewportModified = false;
                         }
                         }
@@ -1451,7 +1451,7 @@ void View::ExecuteRenderPathCommands()
                             // If the current render target is already a texture, and we are not writing to it, can read that
                             // If the current render target is already a texture, and we are not writing to it, can read that
                             // texture directly instead of blitting. However keep the viewport dirty flag in case a later command
                             // texture directly instead of blitting. However keep the viewport dirty flag in case a later command
                             // will do both read and write, and then we need to blit / resolve
                             // will do both read and write, and then we need to blit / resolve
-                            currentViewportTexture_ = static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture());
+                            currentViewportTexture_ = currentRenderTarget_->GetParentTexture();
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1459,7 +1459,7 @@ void View::ExecuteRenderPathCommands()
                 {
                 {
                     // Swap the pingpong double buffer sides. Texture 0 will be read next
                     // Swap the pingpong double buffer sides. Texture 0 will be read next
                     viewportTextures_[1] = viewportTextures_[0];
                     viewportTextures_[1] = viewportTextures_[0];
-                    viewportTextures_[0] = static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture());
+                    viewportTextures_[0] = currentRenderTarget_->GetParentTexture();
                     currentViewportTexture_ = viewportTextures_[0];
                     currentViewportTexture_ = viewportTextures_[0];
                     viewportModified = false;
                     viewportModified = false;
                 }
                 }
@@ -1473,7 +1473,7 @@ void View::ExecuteRenderPathCommands()
             {
             {
                 if (isPingponging)
                 if (isPingponging)
                 {
                 {
-                    currentRenderTarget_ = viewportTextures_[1]->GetRenderSurface();
+                    currentRenderTarget_ = GetRenderSurfaceFromTexture(viewportTextures_[1]);
                     // If the render path ends into a quad, it can be redirected to the final render target
                     // If the render path ends into a quad, it can be redirected to the final render target
                     // However, on OpenGL we can not reliably do this in case the final target is the backbuffer, and we want to
                     // However, on OpenGL we can not reliably do this in case the final target is the backbuffer, and we want to
                     // render depth buffer sensitive debug geometry afterward (backbuffer and textures can not share depth)
                     // render depth buffer sensitive debug geometry afterward (backbuffer and textures can not share depth)
@@ -1620,38 +1620,33 @@ void View::SetRenderTargets(RenderPathCommand& command)
     bool useColorWrite = true;
     bool useColorWrite = true;
     bool useCustomDepth = false;
     bool useCustomDepth = false;
 
 
-    while (index < command.outputNames_.Size())
+    while (index < command.outputs_.Size())
     {
     {
-        if (!command.outputNames_[index].Compare("viewport", false))
+        if (!command.outputs_[index].first_.Compare("viewport", false))
             graphics_->SetRenderTarget(index, currentRenderTarget_);
             graphics_->SetRenderTarget(index, currentRenderTarget_);
         else
         else
         {
         {
-            StringHash nameHash(command.outputNames_[index]);
-            if (renderTargets_.Contains(nameHash))
+            Texture* texture = FindNamedTexture(command.outputs_[index].first_, true, false);
+
+            // Check for depth only rendering (by specifying a depth texture as the sole output)
+            if (!index && command.outputs_.Size() == 1 && texture && (texture->GetFormat() ==
+                Graphics::GetReadableDepthFormat() || texture->GetFormat() == Graphics::GetDepthStencilFormat()))
             {
             {
-                Texture2D* texture = renderTargets_[nameHash];
-                // Check for depth only rendering (by specifying a depth texture as the sole output)
-                if (!index && command.outputNames_.Size() == 1 && texture && (texture->GetFormat() ==
-                    Graphics::GetReadableDepthFormat() || texture->GetFormat() == Graphics::GetDepthStencilFormat()))
+                useColorWrite = false;
+                useCustomDepth = true;
+                #if !defined(URHO3D_OPENGL) && !defined(URHO3D_D3D11)
+                // On D3D actual depth-only rendering is illegal, we need a color rendertarget
+                if (!depthOnlyDummyTexture_)
                 {
                 {
-                    useColorWrite = false;
-                    useCustomDepth = true;
-                    #if !defined(URHO3D_OPENGL) && !defined(URHO3D_D3D11)
-                    // On D3D9 actual depth-only rendering is illegal, we need a color rendertarget
-                    if (!depthOnlyDummyTexture_)
-                    {
-                        depthOnlyDummyTexture_ = renderer_->GetScreenBuffer(texture->GetWidth(), texture->GetHeight(),
-                            graphics_->GetDummyColorFormat(), false, false);
-                    }
-                    #endif
-                    graphics_->SetRenderTarget(0, depthOnlyDummyTexture_); 
-                    graphics_->SetDepthStencil(texture);
+                    depthOnlyDummyTexture_ = renderer_->GetScreenBuffer(texture->GetWidth(), texture->GetHeight(),
+                        graphics_->GetDummyColorFormat(), false, false, false);
                 }
                 }
-                else
-                    graphics_->SetRenderTarget(index, texture);
+                #endif
+                graphics_->SetRenderTarget(0, GetRenderSurfaceFromTexture(depthOnlyDummyTexture_));
+                graphics_->SetDepthStencil(GetRenderSurfaceFromTexture(texture));
             }
             }
             else
             else
-                graphics_->SetRenderTarget(0, (RenderSurface*)0);
+                graphics_->SetRenderTarget(index, GetRenderSurfaceFromTexture(texture, command.outputs_[index].second_));
         }
         }
         
         
         ++index;
         ++index;
@@ -1665,11 +1660,11 @@ void View::SetRenderTargets(RenderPathCommand& command)
     
     
     if (command.depthStencilName_.Length())
     if (command.depthStencilName_.Length())
     {
     {
-        Texture2D* depthTexture = renderTargets_[StringHash(command.depthStencilName_)];
+        Texture* depthTexture = FindNamedTexture(command.depthStencilName_, true, false);
         if (depthTexture)
         if (depthTexture)
         {
         {
             useCustomDepth = true;
             useCustomDepth = true;
-            graphics_->SetDepthStencil(depthTexture);
+            graphics_->SetDepthStencil(GetRenderSurfaceFromTexture(depthTexture));
         }
         }
     }
     }
 
 
@@ -1703,36 +1698,14 @@ bool View::SetTextures(RenderPathCommand& command)
             continue;
             continue;
         }
         }
         
         
-        // Bind a rendertarget
-        HashMap<StringHash, Texture2D*>::ConstIterator j = renderTargets_.Find(command.textureNames_[i]);
-        if (j != renderTargets_.End())
+        Texture* texture = FindNamedTexture(command.textureNames_[i], false, i == TU_VOLUMEMAP);
+        if (texture)
         {
         {
-            graphics_->SetTexture(i, j->second_);
+            graphics_->SetTexture(i, texture);
             // Check if the current depth stencil is being sampled
             // Check if the current depth stencil is being sampled
-            if (graphics_->GetDepthStencil() && j->second_ == graphics_->GetDepthStencil()->GetParentTexture())
+            if (graphics_->GetDepthStencil() && texture == graphics_->GetDepthStencil()->GetParentTexture())
                 allowDepthWrite = false;
                 allowDepthWrite = false;
-            continue;
-        }
-        
-        // Bind a texture from the resource system
-        Texture* texture;
-
-        // Detect cube/3D textures by file extension: they are defined by an XML file
-        if (GetExtension(command.textureNames_[i]) == ".xml")
-        {
-            // Assume 3D textures are only bound to the volume map unit, otherwise it's a cube texture
-            #ifdef DESKTOP_GRAPHICS
-            if (i == TU_VOLUMEMAP)
-                texture = cache->GetResource<Texture3D>(command.textureNames_[i]);
-            else
-            #endif
-                texture = cache->GetResource<TextureCube>(command.textureNames_[i]);
         }
         }
-        else
-            texture = cache->GetResource<Texture2D>(command.textureNames_[i]);
-
-        if (texture)
-            graphics_->SetTexture(i, texture);
         else
         else
         {
         {
             // If requesting a texture fails, clear the texture name to prevent redundant attempts
             // If requesting a texture fails, clear the texture name to prevent redundant attempts
@@ -1805,7 +1778,7 @@ void View::RenderQuad(RenderPathCommand& command)
 
 
 bool View::IsNecessary(const RenderPathCommand& command)
 bool View::IsNecessary(const RenderPathCommand& command)
 {
 {
-    return command.enabled_ && command.outputNames_.Size() && (command.type_ != CMD_SCENEPASS ||
+    return command.enabled_ && command.outputs_.Size() && (command.type_ != CMD_SCENEPASS ||
         !batchQueues_[command.passIndex_].IsEmpty());
         !batchQueues_[command.passIndex_].IsEmpty());
 }
 }
 
 
@@ -1822,9 +1795,9 @@ bool View::CheckViewportRead(const RenderPathCommand& command)
 
 
 bool View::CheckViewportWrite(const RenderPathCommand& command)
 bool View::CheckViewportWrite(const RenderPathCommand& command)
 {
 {
-    for (unsigned i = 0; i < command.outputNames_.Size(); ++i)
+    for (unsigned i = 0; i < command.outputs_.Size(); ++i)
     {
     {
-        if (!command.outputNames_[i].Compare("viewport", false))
+        if (!command.outputs_[i].first_.Compare("viewport", false))
             return true;
             return true;
     }
     }
     
     
@@ -1877,7 +1850,7 @@ void View::AllocateScreenBuffers()
             const RenderPathCommand& command = renderPath_->commands_[i];
             const RenderPathCommand& command = renderPath_->commands_[i];
             if (!IsNecessary(command))
             if (!IsNecessary(command))
                 continue;
                 continue;
-            if (command.depthStencilName_.Length() && command.outputNames_.Size() && !command.outputNames_[0].Compare("viewport",
+            if (command.depthStencilName_.Length() && command.outputs_.Size() && !command.outputs_[0].first_.Compare("viewport",
                 false))
                 false))
             {
             {
                 needSubstitute = true;
                 needSubstitute = true;
@@ -1906,11 +1879,11 @@ void View::AllocateScreenBuffers()
                     continue;
                     continue;
                 if (command.depthStencilName_.Length())
                 if (command.depthStencilName_.Length())
                     needSubstitute = true;
                     needSubstitute = true;
-                if (!needSubstitute && command.outputNames_.Size() > 1)
+                if (!needSubstitute && command.outputs_.Size() > 1)
                 {
                 {
-                    for (unsigned j = 0; j < command.outputNames_.Size(); ++j)
+                    for (unsigned j = 0; j < command.outputs_.Size(); ++j)
                     {
                     {
-                        if (!command.outputNames_[j].Compare("viewport", false))
+                        if (!command.outputs_[j].first_.Compare("viewport", false))
                         {
                         {
                             needSubstitute = true;
                             needSubstitute = true;
                             break;
                             break;
@@ -1982,16 +1955,16 @@ void View::AllocateScreenBuffers()
     // Allocate screen buffers with filtering active in case the quad commands need that
     // Allocate screen buffers with filtering active in case the quad commands need that
     // Follow the sRGB mode of the destination render target
     // Follow the sRGB mode of the destination render target
     bool sRGB = renderTarget_ ? renderTarget_->GetParentTexture()->GetSRGB() : graphics_->GetSRGB();
     bool sRGB = renderTarget_ ? renderTarget_->GetParentTexture()->GetSRGB() : graphics_->GetSRGB();
-    substituteRenderTarget_ = needSubstitute ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, true, 
-        sRGB)->GetRenderSurface() : (RenderSurface*)0;
+    substituteRenderTarget_ = needSubstitute ? GetRenderSurfaceFromTexture(renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_,
+        format, false, true, sRGB)) : (RenderSurface*)0;
     for (unsigned i = 0; i < MAX_VIEWPORT_TEXTURES; ++i)
     for (unsigned i = 0; i < MAX_VIEWPORT_TEXTURES; ++i)
     {
     {
-        viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, true, sRGB) :
-            (Texture2D*)0;
+        viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, false, true, sRGB) :
+            (Texture*)0;
     }
     }
     // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
     // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
     if (numViewportTextures == 1 && substituteRenderTarget_)
     if (numViewportTextures == 1 && substituteRenderTarget_)
-        viewportTextures_[1] = static_cast<Texture2D*>(substituteRenderTarget_->GetParentTexture());
+        viewportTextures_[1] = substituteRenderTarget_->GetParentTexture();
     
     
     // Allocate extra render targets defined by the rendering path
     // Allocate extra render targets defined by the rendering path
     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
@@ -2018,12 +1991,12 @@ void View::AllocateScreenBuffers()
         int intHeight = (int)(height + 0.5f);
         int intHeight = (int)(height + 0.5f);
         
         
         // If the rendertarget is persistent, key it with a hash derived from the RT name and the view's pointer
         // If the rendertarget is persistent, key it with a hash derived from the RT name and the view's pointer
-        renderTargets_[rtInfo.name_] = renderer_->GetScreenBuffer(intWidth, intHeight, rtInfo.format_, rtInfo.filtered_,
+        renderTargets_[rtInfo.name_] = renderer_->GetScreenBuffer(intWidth, intHeight, rtInfo.format_, rtInfo.cubemap_, rtInfo.filtered_,
             rtInfo.sRGB_, rtInfo.persistent_ ? StringHash(rtInfo.name_).Value() + (unsigned)(size_t)this : 0);
             rtInfo.sRGB_, rtInfo.persistent_ ? StringHash(rtInfo.name_).Value() + (unsigned)(size_t)this : 0);
     }
     }
 }
 }
 
 
-void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool depthWrite)
+void View::BlitFramebuffer(Texture* source, RenderSurface* destination, bool depthWrite)
 {
 {
     if (!source)
     if (!source)
         return;
         return;
@@ -2036,7 +2009,7 @@ void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool d
     IntVector2 destSize = destination ? IntVector2(destination->GetWidth(), destination->GetHeight()) : IntVector2(
     IntVector2 destSize = destination ? IntVector2(destination->GetWidth(), destination->GetHeight()) : IntVector2(
         graphics_->GetWidth(), graphics_->GetHeight());
         graphics_->GetWidth(), graphics_->GetHeight());
     
     
-    IntRect srcRect = (source->GetRenderSurface() == renderTarget_) ? viewRect_ : IntRect(0, 0, srcSize.x_, srcSize.y_);
+    IntRect srcRect = (GetRenderSurfaceFromTexture(source) == renderTarget_) ? viewRect_ : IntRect(0, 0, srcSize.x_, srcSize.y_);
     IntRect destRect = (destination == renderTarget_) ? viewRect_ : IntRect(0, 0, destSize.x_, destSize.y_);
     IntRect destRect = (destination == renderTarget_) ? viewRect_ : IntRect(0, 0, destSize.x_, destSize.y_);
     
     
     graphics_->SetBlendMode(BLEND_REPLACE);
     graphics_->SetBlendMode(BLEND_REPLACE);
@@ -2961,4 +2934,56 @@ RenderSurface* View::GetDepthStencil(RenderSurface* renderTarget)
     return depthStencil;
     return depthStencil;
 }
 }
 
 
+RenderSurface* View::GetRenderSurfaceFromTexture(Texture* texture, CubeMapFace face)
+{
+    if (!texture)
+        return 0;
+
+    if (texture->GetType() == Texture2D::GetTypeStatic())
+        return static_cast<Texture2D*>(texture)->GetRenderSurface();
+    else if (texture->GetType() == TextureCube::GetTypeStatic())
+        return static_cast<TextureCube*>(texture)->GetRenderSurface(face);
+    else
+        return 0;
+}
+
+Texture* View::FindNamedTexture(const String& name, bool isRenderTarget, bool isVolumeMap)
+{
+    // Check rendertargets first
+    StringHash nameHash(name);
+    if (renderTargets_.Contains(nameHash))
+        return renderTargets_[nameHash];
+
+    // Then the resource system
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    // Check existing resources first. This does not load resources, so we can afford to guess the resource type wrong
+    // without having to rely on the file extension
+    Texture* texture = cache->GetExistingResource<Texture2D>(name);
+    if (!texture)
+        texture = cache->GetExistingResource<TextureCube>(name);
+    if (!texture)
+        texture = cache->GetExistingResource<Texture3D>(name);
+    if (texture)
+        return texture;
+
+    // If not a rendertarget (which will never be loaded from a file), finally also try to load the texture
+    // This will log an error if not found; the texture binding will be cleared in that case to not constantly spam the log
+    if (!isRenderTarget)
+    {
+        if (GetExtension(name) == ".xml")
+        {
+            // Assume 3D textures are only bound to the volume map unit, otherwise it's a cube texture
+            #ifdef DESKTOP_GRAPHICS
+            if (isVolumeMap)
+                return cache->GetResource<Texture3D>(name);
+            else
+            #endif
+                return cache->GetResource<TextureCube>(name);
+        }
+        else
+            return cache->GetResource<Texture2D>(name);
+    }
+}
+
 }
 }

+ 11 - 6
Source/Urho3D/Graphics/View.h

@@ -44,6 +44,7 @@ class Renderer;
 class RenderPath;
 class RenderPath;
 class RenderSurface;
 class RenderSurface;
 class Technique;
 class Technique;
+class Texture;
 class Texture2D;
 class Texture2D;
 class Viewport;
 class Viewport;
 class Zone;
 class Zone;
@@ -193,7 +194,7 @@ private:
     /// Allocate needed screen buffers.
     /// Allocate needed screen buffers.
     void AllocateScreenBuffers();
     void AllocateScreenBuffers();
     /// Blit the viewport from one surface to another.
     /// Blit the viewport from one surface to another.
-    void BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool depthWrite);
+    void BlitFramebuffer(Texture* source, RenderSurface* destination, bool depthWrite);
     /// Draw a fullscreen quad. Shaders and renderstates must have been set beforehand.
     /// Draw a fullscreen quad. Shaders and renderstates must have been set beforehand.
     void DrawFullscreenQuad(bool nearQuad);
     void DrawFullscreenQuad(bool nearQuad);
     /// Query for occluders as seen from a camera.
     /// Query for occluders as seen from a camera.
@@ -232,7 +233,11 @@ private:
     void RenderShadowMap(const LightBatchQueue& queue);
     void RenderShadowMap(const LightBatchQueue& queue);
     /// Return the proper depth-stencil surface to use for a rendertarget.
     /// Return the proper depth-stencil surface to use for a rendertarget.
     RenderSurface* GetDepthStencil(RenderSurface* renderTarget);
     RenderSurface* GetDepthStencil(RenderSurface* renderTarget);
-    
+    /// Helper function to get the render surface from a texture. 2D textures will always return the first face only.
+    RenderSurface* GetRenderSurfaceFromTexture(Texture* texture, CubeMapFace face = FACE_POSITIVE_X);
+    /// Get a named texture from the rendertarget list or from the resource cache, to be either used as a rendertarget or texture binding.
+    Texture* FindNamedTexture(const String& name, bool isRenderTarget, bool isVolumeMap = false);
+
     /// Return the drawable's zone, or camera zone if it has override mode enabled.
     /// Return the drawable's zone, or camera zone if it has override mode enabled.
     Zone* GetZone(Drawable* drawable)
     Zone* GetZone(Drawable* drawable)
     {
     {
@@ -286,13 +291,13 @@ private:
     /// Substitute rendertarget for deferred rendering. Allocated if necessary.
     /// Substitute rendertarget for deferred rendering. Allocated if necessary.
     RenderSurface* substituteRenderTarget_;
     RenderSurface* substituteRenderTarget_;
     /// Texture(s) for sampling the viewport contents. Allocated if necessary.
     /// Texture(s) for sampling the viewport contents. Allocated if necessary.
-    Texture2D* viewportTextures_[MAX_VIEWPORT_TEXTURES];
+    Texture* viewportTextures_[MAX_VIEWPORT_TEXTURES];
     /// Color rendertarget active for the current renderpath command.
     /// Color rendertarget active for the current renderpath command.
     RenderSurface* currentRenderTarget_;
     RenderSurface* currentRenderTarget_;
     /// Texture containing the latest viewport texture.
     /// Texture containing the latest viewport texture.
-    Texture2D* currentViewportTexture_;
+    Texture* currentViewportTexture_;
     /// Dummy texture for D3D9 depth only rendering.
     /// Dummy texture for D3D9 depth only rendering.
-    Texture2D* depthOnlyDummyTexture_;
+    Texture* depthOnlyDummyTexture_;
     /// Viewport rectangle.
     /// Viewport rectangle.
     IntRect viewRect_;
     IntRect viewRect_;
     /// Viewport size.
     /// Viewport size.
@@ -351,7 +356,7 @@ private:
     /// Drawables that limit their maximum light count.
     /// Drawables that limit their maximum light count.
     HashSet<Drawable*> maxLightsDrawables_;
     HashSet<Drawable*> maxLightsDrawables_;
     /// Rendertargets defined by the renderpath.
     /// Rendertargets defined by the renderpath.
-    HashMap<StringHash, Texture2D*> renderTargets_;
+    HashMap<StringHash, Texture*> renderTargets_;
     /// Intermediate light processing results.
     /// Intermediate light processing results.
     Vector<LightQueryResult> lightQueryResults_;
     Vector<LightQueryResult> lightQueryResults_;
     /// Info for scene render passes defined by the renderpath.
     /// Info for scene render passes defined by the renderpath.

+ 34 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/RenderPath.pkg

@@ -11,6 +11,37 @@ enum RenderCommandType
     CMD_RENDERUI
     CMD_RENDERUI
 };
 };
 
 
+enum RenderCommandSortMode
+{
+    SORT_FRONTTOBACK = 0,
+    SORT_BACKTOFRONT
+};
+
+enum RenderTargetSizeMode
+{
+    SIZE_ABSOLUTE = 0,
+    SIZE_VIEWPORTDIVISOR,
+    SIZE_VIEWPORTMULTIPLIER
+};
+
+struct RenderTargetInfo
+{
+    RenderTargetInfo();
+
+    void Load(const XMLElement& element);
+
+    String name_ @ name;
+    String tag_ @ tag;
+    unsigned format_ @ format;
+    Vector2 size_ @ size;
+    RenderTargetSizeMode sizeMode_ @ sizeMode;
+    bool enabled_ @ enabled;
+    bool cubemap_ @ cubemap;
+    bool filtered_ @ filtered;
+    bool sRGB_ @ sRGB;
+    bool persistent_ @ persistent;
+};
+
 struct RenderPathCommand
 struct RenderPathCommand
 {
 {
     RenderPathCommand();
     RenderPathCommand();
@@ -20,13 +51,16 @@ struct RenderPathCommand
     void SetShaderParameter(const String name, const Variant& value);
     void SetShaderParameter(const String name, const Variant& value);
     void RemoveShaderParameter(const String name);
     void RemoveShaderParameter(const String name);
     void SetNumOutputs(unsigned num);
     void SetNumOutputs(unsigned num);
+    void SetOutput(unsigned index, const String name, CubeMapFace face);
     void SetOutputName(unsigned index, const String name);
     void SetOutputName(unsigned index, const String name);
+    void SetOutputFace(unsigned index, CubeMapFace face);
     void SetDepthStencilName(const String name);
     void SetDepthStencilName(const String name);
 
 
     const String GetTextureName(TextureUnit unit) const;
     const String GetTextureName(TextureUnit unit) const;
     const Variant& GetShaderParameter(const String name) const;
     const Variant& GetShaderParameter(const String name) const;
     unsigned GetNumOutputs() const;
     unsigned GetNumOutputs() const;
     const String GetOutputName(unsigned index) const;
     const String GetOutputName(unsigned index) const;
+    CubeMapFace GetOutputFace(unsigned index) const;
     const String GetDepthStencilName() const;
     const String GetDepthStencilName() const;
 
 
     String tag_ @ tag;
     String tag_ @ tag;

+ 1 - 0
Source/Urho3D/LuaScript/pkgs/Resource/ResourceCache.pkg

@@ -17,6 +17,7 @@ class ResourceCache
     tolua_outside File* ResourceCacheGetFile @ GetFile(const String name);
     tolua_outside File* ResourceCacheGetFile @ GetFile(const String name);
 
 
     Resource* GetResource(const String type, const String name, bool sendEventOnFailure = true);
     Resource* GetResource(const String type, const String name, bool sendEventOnFailure = true);
+    Resource* GetExistingResource(const String type, const String name);
     tolua_outside bool ResourceCacheBackgroundLoadResource @ BackgroundLoadResource(const String type, const String name, bool sendEventOnFailure = true);
     tolua_outside bool ResourceCacheBackgroundLoadResource @ BackgroundLoadResource(const String type, const String name, bool sendEventOnFailure = true);
     unsigned GetNumBackgroundLoadResources() const;
     unsigned GetNumBackgroundLoadResources() const;
     const Vector<String>& GetResourceDirs() const;
     const Vector<String>& GetResourceDirs() const;

+ 22 - 2
Source/Urho3D/Resource/ResourceCache.cpp

@@ -499,16 +499,36 @@ SharedPtr<File> ResourceCache::GetFile(const String& nameIn, bool sendEventOnFai
     return SharedPtr<File>();
     return SharedPtr<File>();
 }
 }
 
 
+Resource* ResourceCache::GetExistingResource(StringHash type, const String& nameIn)
+{
+    String name = SanitateResourceName(nameIn);
+
+    if (!Thread::IsMainThread())
+    {
+        LOGERROR("Attempted to get resource " + name + " from outside the main thread");
+        return 0;
+    }
+
+    // If empty name, return null pointer immediately
+    if (name.Empty())
+        return 0;
+
+    StringHash nameHash(name);
+
+    const SharedPtr<Resource>& existing = FindResource(type, nameHash);
+    return existing;
+}
+
 Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool sendEventOnFailure)
 Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool sendEventOnFailure)
 {
 {
     String name = SanitateResourceName(nameIn);
     String name = SanitateResourceName(nameIn);
-    
+
     if (!Thread::IsMainThread())
     if (!Thread::IsMainThread())
     {
     {
         LOGERROR("Attempted to get resource " + name + " from outside the main thread");
         LOGERROR("Attempted to get resource " + name + " from outside the main thread");
         return 0;
         return 0;
     }
     }
-    
+
     // If empty name, return null pointer immediately
     // If empty name, return null pointer immediately
     if (name.Empty())
     if (name.Empty())
         return 0;
         return 0;

+ 10 - 0
Source/Urho3D/Resource/ResourceCache.h

@@ -141,6 +141,8 @@ public:
     unsigned GetNumBackgroundLoadResources() const;
     unsigned GetNumBackgroundLoadResources() const;
     /// Return all loaded resources of a specific type.
     /// Return all loaded resources of a specific type.
     void GetResources(PODVector<Resource*>& result, StringHash type) const;
     void GetResources(PODVector<Resource*>& result, StringHash type) const;
+    /// Return an already loaded resource of specific type & name, or null if not found. Will not load if does not exist.
+    Resource* GetExistingResource(StringHash type, const String& name);
     /// Return all loaded resources.
     /// Return all loaded resources.
     const HashMap<StringHash, ResourceGroup>& GetAllResources() const { return resourceGroups_; }
     const HashMap<StringHash, ResourceGroup>& GetAllResources() const { return resourceGroups_; }
     /// Return added resource load directories.
     /// Return added resource load directories.
@@ -149,6 +151,8 @@ public:
     const Vector<SharedPtr<PackageFile> >& GetPackageFiles() const { return packages_; }
     const Vector<SharedPtr<PackageFile> >& GetPackageFiles() const { return packages_; }
     /// Template version of returning a resource by name.
     /// Template version of returning a resource by name.
     template <class T> T* GetResource(const String& name, bool sendEventOnFailure = true);
     template <class T> T* GetResource(const String& name, bool sendEventOnFailure = true);
+    /// Template version of returning an existing resource by name.
+    template <class T> T* GetExistingResource(const String& name);
     /// Template version of loading a resource without storing it to the cache.
     /// Template version of loading a resource without storing it to the cache.
     template <class T> SharedPtr<T> GetTempResource(const String& name, bool sendEventOnFailure = true);
     template <class T> SharedPtr<T> GetTempResource(const String& name, bool sendEventOnFailure = true);
     /// Template version of queueing a resource background load.
     /// Template version of queueing a resource background load.
@@ -229,6 +233,12 @@ private:
     int finishBackgroundResourcesMs_;
     int finishBackgroundResourcesMs_;
 };
 };
 
 
+template <class T> T* ResourceCache::GetExistingResource(const String& name)
+{
+    StringHash type = T::GetTypeStatic();
+    return static_cast<T*>(GetExistingResource(type, name));
+}
+
 template <class T> T* ResourceCache::GetResource(const String& name, bool sendEventOnFailure)
 template <class T> T* ResourceCache::GetResource(const String& name, bool sendEventOnFailure)
 {
 {
     StringHash type = T::GetTypeStatic();
     StringHash type = T::GetTypeStatic();

+ 16 - 8
Source/Urho3D/Script/GraphicsAPI.cpp

@@ -261,6 +261,14 @@ static RenderPathCommand* RenderPathGetCommand(unsigned index, RenderPath* ptr)
 
 
 static void RegisterRenderPath(asIScriptEngine* engine)
 static void RegisterRenderPath(asIScriptEngine* engine)
 {
 {
+    engine->RegisterEnum("CubeMapFace");
+    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_X", FACE_POSITIVE_X);
+    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_X", FACE_NEGATIVE_X);
+    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_Y", FACE_POSITIVE_Y);
+    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_Y", FACE_NEGATIVE_Y);
+    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_Z", FACE_POSITIVE_Z);
+    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_Z", FACE_NEGATIVE_Z);
+
     engine->RegisterEnum("RenderCommandType");
     engine->RegisterEnum("RenderCommandType");
     engine->RegisterEnumValue("RenderCommandType", "CMD_NONE", CMD_NONE);
     engine->RegisterEnumValue("RenderCommandType", "CMD_NONE", CMD_NONE);
     engine->RegisterEnumValue("RenderCommandType", "CMD_CLEAR", CMD_CLEAR);
     engine->RegisterEnumValue("RenderCommandType", "CMD_CLEAR", CMD_CLEAR);
@@ -302,6 +310,10 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     #endif
     #endif
     engine->RegisterEnumValue("TextureUnit", "MAX_MATERIAL_TEXTURE_UNITS", MAX_MATERIAL_TEXTURE_UNITS);
     engine->RegisterEnumValue("TextureUnit", "MAX_MATERIAL_TEXTURE_UNITS", MAX_MATERIAL_TEXTURE_UNITS);
     engine->RegisterEnumValue("TextureUnit", "MAX_TEXTURE_UNITS", MAX_TEXTURE_UNITS);
     engine->RegisterEnumValue("TextureUnit", "MAX_TEXTURE_UNITS", MAX_TEXTURE_UNITS);
+
+    engine->RegisterGlobalProperty("uint CLEAR_COLOR", (void*)&CLEAR_COLOR);
+    engine->RegisterGlobalProperty("uint CLEAR_DEPTH", (void*)&CLEAR_DEPTH);
+    engine->RegisterGlobalProperty("uint CLEAR_STENCIL", (void*)&CLEAR_STENCIL);
     
     
     engine->RegisterObjectType("RenderTargetInfo", sizeof(RenderTargetInfo), asOBJ_VALUE | asOBJ_APP_CLASS_C);
     engine->RegisterObjectType("RenderTargetInfo", sizeof(RenderTargetInfo), asOBJ_VALUE | asOBJ_APP_CLASS_C);
     engine->RegisterObjectBehaviour("RenderTargetInfo", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructRenderTargetInfo), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("RenderTargetInfo", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructRenderTargetInfo), asCALL_CDECL_OBJLAST);
@@ -314,6 +326,7 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     engine->RegisterObjectProperty("RenderTargetInfo", "Vector2 size", offsetof(RenderTargetInfo, size_));
     engine->RegisterObjectProperty("RenderTargetInfo", "Vector2 size", offsetof(RenderTargetInfo, size_));
     engine->RegisterObjectProperty("RenderTargetInfo", "RenderTargetSizeMode sizeMode", offsetof(RenderTargetInfo, sizeMode_));
     engine->RegisterObjectProperty("RenderTargetInfo", "RenderTargetSizeMode sizeMode", offsetof(RenderTargetInfo, sizeMode_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool enabled", offsetof(RenderTargetInfo, enabled_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool enabled", offsetof(RenderTargetInfo, enabled_));
+    engine->RegisterObjectProperty("RenderTargetInfo", "bool cubemap", offsetof(RenderTargetInfo, cubemap_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool filtered", offsetof(RenderTargetInfo, filtered_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool filtered", offsetof(RenderTargetInfo, filtered_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool sRGB", offsetof(RenderTargetInfo, sRGB_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool sRGB", offsetof(RenderTargetInfo, sRGB_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool persistent", offsetof(RenderTargetInfo, persistent_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool persistent", offsetof(RenderTargetInfo, persistent_));
@@ -323,6 +336,7 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     engine->RegisterObjectBehaviour("RenderPathCommand", asBEHAVE_CONSTRUCT, "void f(const RenderPathCommand&in)", asFUNCTION(ConstructRenderPathCommandCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("RenderPathCommand", asBEHAVE_CONSTRUCT, "void f(const RenderPathCommand&in)", asFUNCTION(ConstructRenderPathCommandCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("RenderPathCommand", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructRenderPathCommand), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("RenderPathCommand", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructRenderPathCommand), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("RenderPathCommand", "void RemoveShaderParameter(const String&in)", asMETHOD(RenderPathCommand, RemoveShaderParameter), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void RemoveShaderParameter(const String&in)", asMETHOD(RenderPathCommand, RemoveShaderParameter), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPathCommand", "void SetOutput(uint, const String&in, CubeMapFace face = FACE_POSITIVE_X)", asMETHOD(RenderPathCommand, SetOutput), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "RenderPathCommand& opAssign(const RenderPathCommand&in)", asMETHODPR(RenderPathCommand, operator =, (const RenderPathCommand&), RenderPathCommand&), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "RenderPathCommand& opAssign(const RenderPathCommand&in)", asMETHODPR(RenderPathCommand, operator =, (const RenderPathCommand&), RenderPathCommand&), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_textureNames(TextureUnit, const String&in)", asMETHOD(RenderPathCommand, SetTextureName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_textureNames(TextureUnit, const String&in)", asMETHOD(RenderPathCommand, SetTextureName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_textureNames(TextureUnit) const", asMETHOD(RenderPathCommand, GetTextureName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_textureNames(TextureUnit) const", asMETHOD(RenderPathCommand, GetTextureName), asCALL_THISCALL);
@@ -332,6 +346,8 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RenderPathCommand", "uint get_numOutputs() const", asMETHOD(RenderPathCommand, GetNumOutputs), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "uint get_numOutputs() const", asMETHOD(RenderPathCommand, GetNumOutputs), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_outputNames(uint, const String&in)", asMETHOD(RenderPathCommand, SetOutputName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_outputNames(uint, const String&in)", asMETHOD(RenderPathCommand, SetOutputName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_outputNames(uint) const", asMETHOD(RenderPathCommand, GetOutputName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_outputNames(uint) const", asMETHOD(RenderPathCommand, GetOutputName), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPathCommand", "void set_outputFaces(uint, CubeMapFace)", asMETHOD(RenderPathCommand, SetOutputFace), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderPathCommand", "CubeMapFace get_outputFaces(uint) const", asMETHOD(RenderPathCommand, GetOutputFace), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_depthStencilName(const String&in)", asMETHOD(RenderPathCommand, SetDepthStencilName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "void set_depthStencilName(const String&in)", asMETHOD(RenderPathCommand, SetDepthStencilName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_depthStencilName() const", asMETHOD(RenderPathCommand, GetDepthStencilName), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderPathCommand", "const String& get_depthStencilName() const", asMETHOD(RenderPathCommand, GetDepthStencilName), asCALL_THISCALL);
     engine->RegisterObjectProperty("RenderPathCommand", "String tag", offsetof(RenderPathCommand, tag_));
     engine->RegisterObjectProperty("RenderPathCommand", "String tag", offsetof(RenderPathCommand, tag_));
@@ -406,14 +422,6 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterEnumValue("TextureCoordinate", "COORD_V", COORD_V);
     engine->RegisterEnumValue("TextureCoordinate", "COORD_V", COORD_V);
     engine->RegisterEnumValue("TextureCoordinate", "COORD_W", COORD_W);
     engine->RegisterEnumValue("TextureCoordinate", "COORD_W", COORD_W);
     
     
-    engine->RegisterEnum("CubeMapFace");
-    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_X", FACE_POSITIVE_X);
-    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_X", FACE_NEGATIVE_X);
-    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_Y", FACE_POSITIVE_Y);
-    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_Y", FACE_NEGATIVE_Y);
-    engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_Z", FACE_POSITIVE_Z);
-    engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_Z", FACE_NEGATIVE_Z);
-    
     engine->RegisterEnum("RenderSurfaceUpdateMode");
     engine->RegisterEnum("RenderSurfaceUpdateMode");
     engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_MANUALUPDATE", SURFACE_MANUALUPDATE);
     engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_MANUALUPDATE", SURFACE_MANUALUPDATE);
     engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_UPDATEVISIBLE", SURFACE_UPDATEVISIBLE);
     engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_UPDATEVISIBLE", SURFACE_UPDATEVISIBLE);

+ 7 - 0
Source/Urho3D/Script/ResourceAPI.cpp

@@ -42,6 +42,11 @@ static Resource* ResourceCacheGetResource(const String& type, const String& name
     return ptr->GetResource(StringHash(type), name, sendEventOnFailure);
     return ptr->GetResource(StringHash(type), name, sendEventOnFailure);
 }
 }
 
 
+static Resource* ResourceCacheGetExistingResource(const String& type, const String& name, ResourceCache* ptr)
+{
+    return ptr->GetResource(StringHash(type), name);
+}
+
 static File* ResourceCacheGetFile(const String& name, ResourceCache* ptr)
 static File* ResourceCacheGetFile(const String& name, ResourceCache* ptr)
 {
 {
     SharedPtr<File> file = ptr->GetFile(name);
     SharedPtr<File> file = ptr->GetFile(name);
@@ -122,6 +127,8 @@ static void RegisterResourceCache(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ResourceCache", "String GetResourceFileName(const String&in) const", asMETHOD(ResourceCache, GetResourceFileName), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "String GetResourceFileName(const String&in) const", asMETHOD(ResourceCache, GetResourceFileName), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetResource(const String&in, const String&in, bool sendEventOnFailure = true)", asFUNCTION(ResourceCacheGetResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetResource(const String&in, const String&in, bool sendEventOnFailure = true)", asFUNCTION(ResourceCacheGetResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetResource(StringHash, const String&in, bool sendEventOnFailure = true)", asMETHODPR(ResourceCache, GetResource, (StringHash, const String&, bool), Resource*), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetResource(StringHash, const String&in, bool sendEventOnFailure = true)", asMETHODPR(ResourceCache, GetResource, (StringHash, const String&, bool), Resource*), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetExistingResource(const String&in, const String&in)", asFUNCTION(ResourceCacheGetExistingResource), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("ResourceCache", "Resource@+ GetExistingResource(StringHash, const String&in)", asMETHODPR(ResourceCache, GetExistingResource, (StringHash, const String&), Resource*), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceCache", "bool BackgroundLoadResource(const String&in, const String&in, bool sendEventOnFailure = true)", asFUNCTION(ResourceCacheBackgroundLoadResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "bool BackgroundLoadResource(const String&in, const String&in, bool sendEventOnFailure = true)", asFUNCTION(ResourceCacheBackgroundLoadResource), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void set_memoryBudget(const String&in, uint)", asFUNCTION(ResourceCacheSetMemoryBudget), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "void set_memoryBudget(const String&in, uint)", asFUNCTION(ResourceCacheSetMemoryBudget), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "uint get_memoryBudget(const String&in) const", asFUNCTION(ResourceCacheGetMemoryBudget), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceCache", "uint get_memoryBudget(const String&in) const", asFUNCTION(ResourceCacheGetMemoryBudget), asCALL_CDECL_OBJLAST);