Add IPostProcessWorldShader for custom effect render passes.
This commit is contained in:
@@ -157,6 +157,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
void SetData(byte[] colors, int width, int height);
|
void SetData(byte[] colors, int width, int height);
|
||||||
void SetFloatData(float[] data, int width, int height);
|
void SetFloatData(float[] data, int width, int height);
|
||||||
|
void SetDataFromReadBuffer(Rectangle rect);
|
||||||
byte[] GetData();
|
byte[] GetData();
|
||||||
Size Size { get; }
|
Size Size { get; }
|
||||||
TextureScaleFilter ScaleFilter { get; set; }
|
TextureScaleFilter ScaleFilter { get; set; }
|
||||||
|
|||||||
37
OpenRA.Game/Graphics/RenderPostProcessPassVertex.cs
Normal file
37
OpenRA.Game/Graphics/RenderPostProcessPassVertex.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright (c) The OpenRA Developers and Contributors
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace OpenRA.Graphics
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public readonly struct RenderPostProcessPassVertex
|
||||||
|
{
|
||||||
|
public readonly float X, Y;
|
||||||
|
|
||||||
|
public RenderPostProcessPassVertex(float x, float y)
|
||||||
|
{
|
||||||
|
X = x; Y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RenderPostProcessPassShaderBindings : ShaderBindings
|
||||||
|
{
|
||||||
|
public RenderPostProcessPassShaderBindings(string name)
|
||||||
|
: base("postprocess", "postprocess_" + name) { }
|
||||||
|
|
||||||
|
public override ShaderVertexAttribute[] Attributes { get; } = new[]
|
||||||
|
{
|
||||||
|
new ShaderVertexAttribute("aVertexPosition", 2, 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,11 +39,14 @@ namespace OpenRA.Graphics
|
|||||||
public abstract ShaderVertexAttribute[] Attributes { get; }
|
public abstract ShaderVertexAttribute[] Attributes { get; }
|
||||||
|
|
||||||
protected ShaderBindings(string name)
|
protected ShaderBindings(string name)
|
||||||
|
: this(name, name) { }
|
||||||
|
|
||||||
|
protected ShaderBindings(string vertexName, string fragmentName)
|
||||||
{
|
{
|
||||||
Stride = Attributes.Sum(a => a.Components * 4);
|
Stride = Attributes.Sum(a => a.Components * 4);
|
||||||
VertexShaderName = name;
|
VertexShaderName = vertexName;
|
||||||
VertexShaderCode = GetShaderCode(VertexShaderName + ".vert");
|
VertexShaderCode = GetShaderCode(VertexShaderName + ".vert");
|
||||||
FragmentShaderName = name;
|
FragmentShaderName = fragmentName;
|
||||||
FragmentShaderCode = GetShaderCode(FragmentShaderName + ".frag");
|
FragmentShaderCode = GetShaderCode(FragmentShaderName + ".frag");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
readonly List<IRenderable> renderablesBuffer = new();
|
readonly List<IRenderable> renderablesBuffer = new();
|
||||||
readonly IRenderer[] renderers;
|
readonly IRenderer[] renderers;
|
||||||
|
readonly IRenderPostProcessPass[] postProcessPasses;
|
||||||
|
readonly ITexture postProcessTexture;
|
||||||
|
|
||||||
internal WorldRenderer(ModData modData, World world)
|
internal WorldRenderer(ModData modData, World world)
|
||||||
{
|
{
|
||||||
@@ -71,6 +73,10 @@ namespace OpenRA.Graphics
|
|||||||
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
|
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
|
||||||
|
|
||||||
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
||||||
|
|
||||||
|
postProcessPasses = world.WorldActor.TraitsImplementing<IRenderPostProcessPass>().ToArray();
|
||||||
|
if (postProcessPasses.Length > 0)
|
||||||
|
postProcessTexture = Game.Renderer.Context.CreateTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginFrame()
|
public void BeginFrame()
|
||||||
@@ -284,6 +290,8 @@ namespace OpenRA.Graphics
|
|||||||
if (enableDepthBuffer)
|
if (enableDepthBuffer)
|
||||||
Game.Renderer.ClearDepthBuffer();
|
Game.Renderer.ClearDepthBuffer();
|
||||||
|
|
||||||
|
ApplyPostProcessing(PostProcessPassType.AfterActors);
|
||||||
|
|
||||||
World.ApplyToActorsWithTrait<IRenderAboveWorld>((actor, trait) =>
|
World.ApplyToActorsWithTrait<IRenderAboveWorld>((actor, trait) =>
|
||||||
{
|
{
|
||||||
if (actor.IsInWorld && !actor.Disposed)
|
if (actor.IsInWorld && !actor.Disposed)
|
||||||
@@ -293,6 +301,8 @@ namespace OpenRA.Graphics
|
|||||||
if (enableDepthBuffer)
|
if (enableDepthBuffer)
|
||||||
Game.Renderer.ClearDepthBuffer();
|
Game.Renderer.ClearDepthBuffer();
|
||||||
|
|
||||||
|
ApplyPostProcessing(PostProcessPassType.AfterWorld);
|
||||||
|
|
||||||
World.ApplyToActorsWithTrait<IRenderShroud>((actor, trait) => trait.RenderShroud(this));
|
World.ApplyToActorsWithTrait<IRenderShroud>((actor, trait) => trait.RenderShroud(this));
|
||||||
|
|
||||||
if (enableDepthBuffer)
|
if (enableDepthBuffer)
|
||||||
@@ -306,9 +316,27 @@ namespace OpenRA.Graphics
|
|||||||
foreach (var r in g)
|
foreach (var r in g)
|
||||||
r.Render(this);
|
r.Render(this);
|
||||||
|
|
||||||
|
ApplyPostProcessing(PostProcessPassType.AfterShroud);
|
||||||
|
|
||||||
Game.Renderer.Flush();
|
Game.Renderer.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplyPostProcessing(PostProcessPassType type)
|
||||||
|
{
|
||||||
|
var size = Game.Renderer.WorldFrameBufferSize;
|
||||||
|
var rect = new Rectangle(0, 0, size.Width, size.Height);
|
||||||
|
foreach (var pass in postProcessPasses)
|
||||||
|
{
|
||||||
|
if (pass.Type != type || !pass.Enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Make a copy of the world texture to avoid reading and writing on the same buffer
|
||||||
|
Game.Renderer.Flush();
|
||||||
|
postProcessTexture.SetDataFromReadBuffer(rect);
|
||||||
|
pass.Draw(this, postProcessTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void DrawAnnotations()
|
public void DrawAnnotations()
|
||||||
{
|
{
|
||||||
Game.Renderer.EnableAntialiasingFilter();
|
Game.Renderer.EnableAntialiasingFilter();
|
||||||
|
|||||||
@@ -459,6 +459,16 @@ namespace OpenRA.Traits
|
|||||||
bool SpatiallyPartitionable { get; }
|
bool SpatiallyPartitionable { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PostProcessPassType { AfterShroud, AfterWorld, AfterActors }
|
||||||
|
|
||||||
|
[RequireExplicitImplementation]
|
||||||
|
public interface IRenderPostProcessPass
|
||||||
|
{
|
||||||
|
PostProcessPassType Type { get; }
|
||||||
|
bool Enabled { get; }
|
||||||
|
void Draw(WorldRenderer wr, ITexture worldTexture);
|
||||||
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum SelectionPriorityModifiers
|
public enum SelectionPriorityModifiers
|
||||||
{
|
{
|
||||||
|
|||||||
56
OpenRA.Mods.Common/Traits/World/RenderPostProcessPassBase.cs
Normal file
56
OpenRA.Mods.Common/Traits/World/RenderPostProcessPassBase.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright (c) The OpenRA Developers and Contributors
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Traits
|
||||||
|
{
|
||||||
|
public abstract class RenderPostProcessPassBase : IRenderPostProcessPass
|
||||||
|
{
|
||||||
|
readonly Renderer renderer;
|
||||||
|
readonly IShader shader;
|
||||||
|
readonly IVertexBuffer<RenderPostProcessPassVertex> buffer;
|
||||||
|
readonly PostProcessPassType type;
|
||||||
|
|
||||||
|
protected RenderPostProcessPassBase(string name, PostProcessPassType type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
renderer = Game.Renderer;
|
||||||
|
shader = renderer.CreateShader(new RenderPostProcessPassShaderBindings(name));
|
||||||
|
var vertices = new RenderPostProcessPassVertex[]
|
||||||
|
{
|
||||||
|
new(-1, -1),
|
||||||
|
new(1, -1),
|
||||||
|
new(1, 1),
|
||||||
|
new(1, 1),
|
||||||
|
new(-1, 1),
|
||||||
|
new(-1, -1)
|
||||||
|
};
|
||||||
|
|
||||||
|
buffer = renderer.CreateVertexBuffer<RenderPostProcessPassVertex>(6);
|
||||||
|
buffer.SetData(ref vertices, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessPassType IRenderPostProcessPass.Type => type;
|
||||||
|
bool IRenderPostProcessPass.Enabled => Enabled;
|
||||||
|
void IRenderPostProcessPass.Draw(WorldRenderer wr, ITexture worldTexture)
|
||||||
|
{
|
||||||
|
shader.PrepareRender();
|
||||||
|
shader.SetTexture("WorldTexture", worldTexture);
|
||||||
|
PrepareRender(wr, shader);
|
||||||
|
renderer.DrawBatch(buffer, shader, 0, 6, PrimitiveType.TriangleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool Enabled { get; }
|
||||||
|
protected abstract void PrepareRender(WorldRenderer wr, IShader shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -442,6 +442,10 @@ namespace OpenRA.Platforms.Default
|
|||||||
int width, int height, int border, int format, int type, IntPtr pixels);
|
int width, int height, int border, int format, int type, IntPtr pixels);
|
||||||
public static TexImage2D glTexImage2D { get; private set; }
|
public static TexImage2D glTexImage2D { get; private set; }
|
||||||
|
|
||||||
|
public delegate void CopyTexImage2D(int target, int level, int internalFormat,
|
||||||
|
int x, int y, int width, int height, int border);
|
||||||
|
public static CopyTexImage2D glCopyTexImage2D { get; private set; }
|
||||||
|
|
||||||
public delegate void GetTexImage(int target, int level,
|
public delegate void GetTexImage(int target, int level,
|
||||||
int format, int type, IntPtr pixels);
|
int format, int type, IntPtr pixels);
|
||||||
public static GetTexImage glGetTexImage { get; private set; }
|
public static GetTexImage glGetTexImage { get; private set; }
|
||||||
@@ -607,6 +611,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
glBindTexture = Bind<BindTexture>("glBindTexture");
|
glBindTexture = Bind<BindTexture>("glBindTexture");
|
||||||
glActiveTexture = Bind<ActiveTexture>("glActiveTexture");
|
glActiveTexture = Bind<ActiveTexture>("glActiveTexture");
|
||||||
glTexImage2D = Bind<TexImage2D>("glTexImage2D");
|
glTexImage2D = Bind<TexImage2D>("glTexImage2D");
|
||||||
|
glCopyTexImage2D = Bind<CopyTexImage2D>("glCopyTexImage2D");
|
||||||
glTexParameteri = Bind<TexParameteri>("glTexParameteri");
|
glTexParameteri = Bind<TexParameteri>("glTexParameteri");
|
||||||
glTexParameterf = Bind<TexParameterf>("glTexParameterf");
|
glTexParameterf = Bind<TexParameterf>("glTexParameterf");
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,19 @@ namespace OpenRA.Platforms.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDataFromReadBuffer(Rectangle rect)
|
||||||
|
{
|
||||||
|
VerifyThreadAffinity();
|
||||||
|
if (!Exts.IsPowerOf2(rect.Width) || !Exts.IsPowerOf2(rect.Height))
|
||||||
|
throw new InvalidDataException($"Non-power-of-two rectangle {rect.Width}x{rect.Height}");
|
||||||
|
|
||||||
|
PrepareTexture();
|
||||||
|
|
||||||
|
var glInternalFormat = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_BGRA : OpenGL.GL_RGBA8;
|
||||||
|
OpenGL.glCopyTexImage2D(OpenGL.GL_TEXTURE_2D, 0, glInternalFormat, rect.X, rect.Y, rect.Width, rect.Height, 0);
|
||||||
|
OpenGL.CheckGLError();
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] GetData()
|
public byte[] GetData()
|
||||||
{
|
{
|
||||||
VerifyThreadAffinity();
|
VerifyThreadAffinity();
|
||||||
|
|||||||
@@ -648,6 +648,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
readonly Func<object, object> setData2;
|
readonly Func<object, object> setData2;
|
||||||
readonly Action<object> setData3;
|
readonly Action<object> setData3;
|
||||||
readonly Func<object, object> setData4;
|
readonly Func<object, object> setData4;
|
||||||
|
readonly Action<object> setData5;
|
||||||
readonly Action dispose;
|
readonly Action dispose;
|
||||||
|
|
||||||
public ThreadedTexture(ThreadedGraphicsContext device, ITextureInternal texture)
|
public ThreadedTexture(ThreadedGraphicsContext device, ITextureInternal texture)
|
||||||
@@ -663,6 +664,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
setData2 = tuple => { setData1(tuple); return null; };
|
setData2 = tuple => { setData1(tuple); return null; };
|
||||||
setData3 = tuple => { var t = ((float[], int, int))tuple; texture.SetFloatData(t.Item1, t.Item2, t.Item3); };
|
setData3 = tuple => { var t = ((float[], int, int))tuple; texture.SetFloatData(t.Item1, t.Item2, t.Item3); };
|
||||||
setData4 = tuple => { setData3(tuple); return null; };
|
setData4 = tuple => { setData3(tuple); return null; };
|
||||||
|
setData5 = rect => texture.SetDataFromReadBuffer((Rectangle)rect);
|
||||||
dispose = texture.Dispose;
|
dispose = texture.Dispose;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,6 +727,11 @@ namespace OpenRA.Platforms.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDataFromReadBuffer(Rectangle rect)
|
||||||
|
{
|
||||||
|
device.Post(setData5, rect);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
device.Post(dispose);
|
device.Post(dispose);
|
||||||
|
|||||||
12
glsl/postprocess.vert
Normal file
12
glsl/postprocess.vert
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#version {VERSION}
|
||||||
|
|
||||||
|
#if __VERSION__ == 120
|
||||||
|
attribute vec2 aVertexPosition;
|
||||||
|
#else
|
||||||
|
in vec2 aVertexPosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(aVertexPosition, 0, 1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user