Skip to content

Commit 407e5fa

Browse files
Implemented worm and sonic tank drawing
1 parent 3d65b91 commit 407e5fa

12 files changed

Lines changed: 828 additions & 54 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright 2007-2020 The d2 mod Developers (see AUTHORS)
4+
* This file is part of OpenRA, which is free software. It is made
5+
* available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of
7+
* the License, or (at your option) any later version. For more
8+
* information, see COPYING.
9+
*/
10+
#endregion
11+
12+
using System;
13+
using System.Linq;
14+
using OpenRA.Graphics;
15+
using OpenRA.Primitives;
16+
17+
namespace OpenRA.Mods.D2.Graphics
18+
{
19+
public enum D2DistortionStyle { Sand, Sonic }
20+
21+
public readonly struct D2DistortionRenderable : IRenderable, IFinalizedRenderable
22+
{
23+
readonly Sprite sprite;
24+
readonly D2DistortionStyle style;
25+
26+
public D2DistortionRenderable(WPos pos, Sprite sprite, D2DistortionStyle style)
27+
{
28+
Pos = pos;
29+
this.sprite = sprite;
30+
this.style = style;
31+
}
32+
33+
public WPos Pos { get; }
34+
public int ZOffset => 0;
35+
public bool IsDecoration => true;
36+
37+
public IRenderable WithZOffset(int newOffset) { return this; }
38+
public IRenderable OffsetBy(in WVec vec)
39+
{
40+
return new D2DistortionRenderable(Pos + vec, sprite, style);
41+
}
42+
43+
public IRenderable AsDecoration() { return this; }
44+
public IFinalizedRenderable PrepareRender(WorldRenderer wr)
45+
{
46+
// Queue during PrepareRender instead of Render so each renderer has its work ready
47+
// when its configured post-process pass runs. Sand currently uses AfterActors; sonic
48+
// remains later on AfterWorld so its wave can bend the completed world image.
49+
var renderStyle = style;
50+
var renderer = wr.World.WorldActor.TraitsImplementing<D2DistortionRenderer>().FirstOrDefault(r => r.Accepts(renderStyle));
51+
if (renderer == null)
52+
return this;
53+
54+
renderer.DrawSprite(wr.Screen3DPxPosition(Pos), sprite, style);
55+
56+
return this;
57+
}
58+
59+
public void Render(WorldRenderer wr) { }
60+
61+
public void RenderDebugGeometry(WorldRenderer wr)
62+
{
63+
var bounds = ScreenBounds(wr);
64+
if (!bounds.IsEmpty)
65+
Game.Renderer.RgbaColorRenderer.DrawRect(
66+
new float3(bounds.Left, bounds.Top, 0),
67+
new float3(bounds.Right, bounds.Bottom, 0), 1, Color.Red);
68+
}
69+
70+
public Rectangle ScreenBounds(WorldRenderer wr)
71+
{
72+
var center = wr.Viewport.WorldToViewPx(wr.Screen3DPxPosition(Pos));
73+
var width = (int)Math.Ceiling(sprite.Size.X);
74+
var height = (int)Math.Ceiling(sprite.Size.Y);
75+
return new Rectangle(center.X - width / 2, center.Y - height / 2, width, height);
76+
}
77+
}
78+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright 2007-2020 The d2 mod Developers (see AUTHORS)
4+
* This file is part of OpenRA, which is free software. It is made
5+
* available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of
7+
* the License, or (at your option) any later version. For more
8+
* information, see COPYING.
9+
*/
10+
#endregion
11+
12+
using System.Collections.Generic;
13+
using System.IO;
14+
using System.Linq;
15+
using OpenRA.Graphics;
16+
using OpenRA.Traits;
17+
18+
namespace OpenRA.Mods.D2.Graphics
19+
{
20+
[TraitLocation(SystemActors.World)]
21+
[Desc("Renders Dune 2 style screen-space pixel distortion effects.")]
22+
public class D2DistortionRendererInfo : TraitInfo
23+
{
24+
public readonly string FragmentShader = "d2|glsl/postprocess_textured_distortion.frag";
25+
public readonly PostProcessPassType PassType = PostProcessPassType.AfterWorld;
26+
public readonly HashSet<D2DistortionStyle> Styles = new() { D2DistortionStyle.Sand, D2DistortionStyle.Sonic };
27+
28+
public override object Create(ActorInitializer init) { return new D2DistortionRenderer(this); }
29+
}
30+
31+
public sealed class D2DistortionRenderer : IRenderPostProcessPass, INotifyActorDisposing
32+
{
33+
// Renderables enqueue these records during normal world rendering. The post-process pass
34+
// then replays OpenDUNE-style blurred sprites against the completed world texture.
35+
readonly struct Distortion
36+
{
37+
public readonly D2DistortionStyle Style;
38+
public readonly float3 Pos;
39+
public readonly Sprite Sprite;
40+
public readonly int BlurOffset;
41+
42+
public Distortion(D2DistortionStyle style, float3 pos, Sprite sprite, int blurOffset)
43+
{
44+
Style = style;
45+
Pos = pos;
46+
Sprite = sprite;
47+
BlurOffset = blurOffset;
48+
}
49+
}
50+
51+
sealed class D2DistortionShaderBindings : IShaderBindings
52+
{
53+
public D2DistortionShaderBindings(string fragmentShader)
54+
{
55+
// Reuse the engine's textured post-process vertex shader. It interprets vertex
56+
// positions as local screen-space pixel offsets from the Pos uniform and passes
57+
// through sprite-sheet UVs for the fragment shader's mask lookup.
58+
VertexShaderName = "postprocess_textured";
59+
VertexShaderCode = ShaderBindings.GetShaderCode("postprocess_textured.vert");
60+
FragmentShaderName = fragmentShader;
61+
62+
using (var stream = Game.ModData.DefaultFileSystem.Open(fragmentShader))
63+
using (var reader = new StreamReader(stream))
64+
FragmentShaderCode = reader.ReadToEnd();
65+
}
66+
67+
public string VertexShaderName { get; }
68+
public string VertexShaderCode { get; }
69+
public string FragmentShaderName { get; }
70+
public string FragmentShaderCode { get; }
71+
public int Stride => Attributes.Sum(a => a.Components * 4);
72+
73+
public ShaderVertexAttribute[] Attributes { get; } =
74+
{
75+
new("aVertexPosition", ShaderVertexAttributeType.Float, 2, 0),
76+
new("aVertexTexCoord", ShaderVertexAttributeType.Float, 2, 8)
77+
};
78+
}
79+
80+
readonly Renderer renderer;
81+
readonly IShader shader;
82+
readonly D2DistortionRendererInfo info;
83+
readonly IVertexBuffer<RenderPostProcessPassTexturedVertex> buffer;
84+
readonly List<Distortion> distortions = new();
85+
readonly RenderPostProcessPassTexturedVertex[] vertices = new RenderPostProcessPassTexturedVertex[6];
86+
static readonly int[] BlurOffsets = { 1, 3, 2, 5, 4, 3, 2, 1 };
87+
static int blurIndex;
88+
static int blurTickCounter;
89+
const int BlurTickInterval = 6;
90+
91+
public D2DistortionRenderer(D2DistortionRendererInfo info)
92+
{
93+
this.info = info;
94+
renderer = Game.Renderer;
95+
shader = renderer.CreateShader(new D2DistortionShaderBindings(info.FragmentShader));
96+
buffer = renderer.CreateVertexBuffer<RenderPostProcessPassTexturedVertex>(6);
97+
}
98+
99+
public bool Accepts(D2DistortionStyle style)
100+
{
101+
return info.Styles.Contains(style);
102+
}
103+
104+
public void DrawSprite(float3 pos, Sprite sprite, D2DistortionStyle style)
105+
{
106+
// BlurOffset больше не вычисляется здесь — только в Draw(),
107+
// чтобы blurIndex продвигался один раз за рендер-пасс, а не за спрайт.
108+
distortions.Add(new Distortion(style, pos, sprite, 0));
109+
}
110+
111+
PostProcessPassType IRenderPostProcessPass.Type => info.PassType;
112+
bool IRenderPostProcessPass.Enabled => distortions.Count > 0;
113+
114+
void UpdateVertices(in Distortion d)
115+
{
116+
var halfWidth = d.Sprite.Size.X / 2;
117+
var halfHeight = d.Sprite.Size.Y / 2;
118+
vertices[0] = new RenderPostProcessPassTexturedVertex(-halfWidth, -halfHeight, d.Sprite.Left, d.Sprite.Top);
119+
vertices[1] = new RenderPostProcessPassTexturedVertex(halfWidth, -halfHeight, d.Sprite.Right, d.Sprite.Top);
120+
vertices[2] = new RenderPostProcessPassTexturedVertex(halfWidth, halfHeight, d.Sprite.Right, d.Sprite.Bottom);
121+
vertices[3] = new RenderPostProcessPassTexturedVertex(halfWidth, halfHeight, d.Sprite.Right, d.Sprite.Bottom);
122+
vertices[4] = new RenderPostProcessPassTexturedVertex(-halfWidth, halfHeight, d.Sprite.Left, d.Sprite.Bottom);
123+
vertices[5] = new RenderPostProcessPassTexturedVertex(-halfWidth, -halfHeight, d.Sprite.Left, d.Sprite.Top);
124+
}
125+
126+
void IRenderPostProcessPass.Draw(WorldRenderer wr)
127+
{
128+
if (++blurTickCounter >= BlurTickInterval)
129+
{
130+
blurTickCounter = 0;
131+
blurIndex = (blurIndex + 1) % BlurOffsets.Length;
132+
}
133+
134+
var scroll = wr.Viewport.TopLeft;
135+
var size = renderer.WorldFrameBufferSize;
136+
var width = 2f / (renderer.WorldDownscaleFactor * size.Width);
137+
var height = 2f / (renderer.WorldDownscaleFactor * size.Height);
138+
139+
shader.SetVec("Scroll", scroll.X, scroll.Y);
140+
shader.SetVec("p1", width, height);
141+
shader.SetVec("p2", -1, -1);
142+
shader.SetTexture("WorldTexture", Game.Renderer.WorldBufferSnapshot());
143+
shader.PrepareRender();
144+
145+
for (var i = 0; i < distortions.Count; i++)
146+
{
147+
var d = distortions[i];
148+
// Каждый сегмент получает следующий шаг таблицы — как три последовательных
149+
// вызова GUI_DrawSprite в оригинале в рамках одного игрового кадра.
150+
var blurOffset = BlurOffsets[(blurIndex + i) % BlurOffsets.Length];
151+
152+
UpdateVertices(d);
153+
buffer.SetData(vertices, 6);
154+
155+
shader.SetVec("Pos", d.Pos.X, d.Pos.Y);
156+
shader.SetVec("Style", (float)d.Style);
157+
shader.SetVec("BlurOffset", blurOffset);
158+
shader.SetVec("MaskChannel", (float)d.Sprite.Channel);
159+
shader.SetTexture("MaskTexture", d.Sprite.Sheet.GetTexture());
160+
renderer.DrawBatch(buffer, shader, 0, 6, PrimitiveType.TriangleList);
161+
}
162+
163+
distortions.Clear();
164+
}
165+
166+
void INotifyActorDisposing.Disposing(Actor self)
167+
{
168+
buffer.Dispose();
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)