Implement sonic blast rendering effect.

This commit is contained in:
Paul Chote
2025-03-28 16:26:27 +00:00
committed by Gustas Kažukauskas
parent 3111d06fcf
commit 5da2f1ba8a
6 changed files with 186 additions and 13 deletions

View File

@@ -0,0 +1,65 @@
#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;
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Mods.D2k.Traits;
using OpenRA.Primitives;
namespace OpenRA.Mods.D2k.Graphics
{
public class SonicBlastRenderable : IRenderable, IFinalizedRenderable
{
public static readonly IEnumerable<IRenderable> None = Array.Empty<IRenderable>();
readonly SonicBlastRenderer renderer;
readonly float3 r;
public WPos Pos { get; }
public int ZOffset => 0;
public SonicBlastRenderable(SonicBlastRenderer renderer, WPos pos)
{
this.renderer = renderer;
Pos = pos;
r = renderer.Info.Size * new float3(0.5f, 0.5f, 0);
}
public bool IsDecoration => false;
public IRenderable WithZOffset(int newOffset) => this;
public IRenderable OffsetBy(in WVec offset) => this;
public IRenderable AsDecoration() => this;
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
renderer.Draw(wr.Screen3DPxPosition(Pos));
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var pos = wr.Screen3DPxPosition(Pos);
var tl = wr.Viewport.WorldToViewPx(pos - r);
var br = wr.Viewport.WorldToViewPx(pos + r);
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)
{
var pos = wr.Screen3DPxPosition(Pos);
var tl = wr.Viewport.WorldToViewPx(pos - r);
var br = wr.Viewport.WorldToViewPx(pos + r);
return new Rectangle(tl.X, tl.Y, br.X - tl.X, br.Y - tl.Y);
}
}
}

View File

@@ -14,9 +14,9 @@ using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common; using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives; using OpenRA.Mods.D2k.Graphics;
using OpenRA.Mods.D2k.Traits;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.D2k.Projectiles namespace OpenRA.Mods.D2k.Projectiles
@@ -30,9 +30,6 @@ namespace OpenRA.Mods.D2k.Projectiles
[Desc("The number of ticks between the blast causing warhead impacts in its area of effect.")] [Desc("The number of ticks between the blast causing warhead impacts in its area of effect.")]
public readonly int DamageInterval = 1; public readonly int DamageInterval = 1;
[Desc("Equivalent to sequence ZOffset. Controls Z sorting.")]
public readonly int ZOffset = 0;
[Desc("The minimum distance the blast travels.")] [Desc("The minimum distance the blast travels.")]
public readonly WDist MinDistance = WDist.Zero; public readonly WDist MinDistance = WDist.Zero;
@@ -57,9 +54,6 @@ namespace OpenRA.Mods.D2k.Projectiles
[Desc("Can this projectile be blocked when hitting actors with an nameof(BlocksProjectiles) trait.")] [Desc("Can this projectile be blocked when hitting actors with an nameof(BlocksProjectiles) trait.")]
public readonly bool Blockable = false; public readonly bool Blockable = false;
[Desc("Color of the blast.")]
public readonly Color Color = Color.SkyBlue;
public IProjectile Create(ProjectileArgs args) public IProjectile Create(ProjectileArgs args)
{ {
return new SonicBlast(this, args); return new SonicBlast(this, args);
@@ -72,6 +66,7 @@ namespace OpenRA.Mods.D2k.Projectiles
readonly ProjectileArgs args; readonly ProjectileArgs args;
readonly WDist speed; readonly WDist speed;
readonly SonicBlastRenderer renderer;
[Sync] [Sync]
WPos pos, lastPos; WPos pos, lastPos;
@@ -85,6 +80,8 @@ namespace OpenRA.Mods.D2k.Projectiles
this.info = info; this.info = info;
this.args = args; this.args = args;
var world = args.SourceActor.World; var world = args.SourceActor.World;
renderer = world.WorldActor.Trait<SonicBlastRenderer>();
if (info.Speed.Length > 1) if (info.Speed.Length > 1)
speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length)); speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
else else
@@ -151,10 +148,7 @@ namespace OpenRA.Mods.D2k.Projectiles
public IEnumerable<IRenderable> Render(WorldRenderer wr) public IEnumerable<IRenderable> Render(WorldRenderer wr)
{ {
if (!wr.World.FogObscures(pos)) if (!wr.World.FogObscures(pos))
{ return new[] { (IRenderable)new SonicBlastRenderable(renderer, pos) };
var blastRender = new BeamRenderable(pos, info.ZOffset, lastPos - pos, BeamRenderableShape.Cylindrical, info.Width, info.Color);
return new[] { (IRenderable)blastRender };
}
return SpriteRenderable.None; return SpriteRenderable.None;
} }

View File

@@ -0,0 +1,96 @@
#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.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.D2k.Traits
{
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
[Desc("Renders sonic blasts")]
public class SonicBlastRendererInfo : TraitInfo
{
[Desc("Diameter of the sonic effect circle.")]
public readonly int Size = 16;
[Desc("Amount to scale the visuals within the effect circle.")]
public readonly float Zoom = 2.5f;
public override object Create(ActorInitializer init) { return new SonicBlastRenderer(this); }
}
public sealed class SonicBlastRenderer : IRenderPostProcessPass, INotifyActorDisposing
{
public readonly SonicBlastRendererInfo Info;
readonly Renderer renderer;
readonly IShader shader;
readonly IVertexBuffer<RenderPostProcessPassTexturedVertex> buffer;
readonly List<float3> positions = new();
public SonicBlastRenderer(SonicBlastRendererInfo info)
{
Info = info;
renderer = Game.Renderer;
shader = renderer.CreateShader(new RenderPostProcessPassTexturedShaderBindings("sonic"));
buffer = renderer.CreateVertexBuffer<RenderPostProcessPassTexturedVertex>(6);
var r = 0.5f * info.Size;
shader.SetVec("Scale", r * (1f / info.Zoom - 1));
var vertices = new RenderPostProcessPassTexturedVertex[]
{
new(-r, -r, -1, -1),
new(r, -r, 1, -1),
new(r, r, 1, 1),
new(r, r, 1, 1),
new(-r, r, -1, 1),
new(-r, -r, -1, -1)
};
buffer.SetData(ref vertices, 6);
}
public void Draw(float3 pos)
{
positions.Add(pos);
}
PostProcessPassType IRenderPostProcessPass.Type => PostProcessPassType.AfterWorld;
bool IRenderPostProcessPass.Enabled => positions.Count > 0;
void IRenderPostProcessPass.Draw(WorldRenderer wr)
{
var scroll = wr.Viewport.TopLeft;
var size = renderer.WorldFrameBufferSize;
var width = 2f / (renderer.WorldDownscaleFactor * size.Width);
var height = 2f / (renderer.WorldDownscaleFactor * size.Height);
shader.SetVec("Scroll", scroll.X, scroll.Y);
shader.SetVec("p1", width, height);
shader.SetVec("p2", -1, -1);
shader.SetTexture("WorldTexture", Game.Renderer.WorldBufferSnapshot());
shader.PrepareRender();
foreach (var pos in positions)
{
shader.SetVec("Pos", pos.X, pos.Y);
renderer.DrawBatch(buffer, shader, 0, 6, PrimitiveType.TriangleList);
}
positions.Clear();
}
void INotifyActorDisposing.Disposing(Actor self)
{
buffer.Dispose();
}
}
}

View File

@@ -0,0 +1,18 @@
#version {VERSION}
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D WorldTexture;
uniform float Scale;
in vec2 vTexCoord;
out vec4 fragColor;
void main()
{
if (dot(vTexCoord, vTexCoord) >= 1.0)
discard;
fragColor = texelFetch(WorldTexture, ivec2(gl_FragCoord.xy + Scale * vTexCoord), 0);
}

View File

@@ -11,6 +11,7 @@
TerrainGeometryOverlay: TerrainGeometryOverlay:
DebugVisualizations: DebugVisualizations:
TerrainRenderer: TerrainRenderer:
SonicBlastRenderer:
ShroudRenderer: ShroudRenderer:
ShroudVariants: shrouda, shroudb, shroudc, shroudd ShroudVariants: shrouda, shroudb, shroudc, shroudd
FogVariants: foga, fogb, fogc, fogd FogVariants: foga, fogb, fogc, fogd

View File

@@ -10,7 +10,6 @@ Sound:
Falloff: 0, 0, 100, 0 Falloff: 0, 0, 100, 0
Range: 0, 0c512, 4c0, 6c0 Range: 0, 0c512, 4c0, 6c0
MinDistance: 5c0 MinDistance: 5c0
Color: 00FFFFC8
Warhead@1Dam: SpreadDamage Warhead@1Dam: SpreadDamage
Falloff: 100, 0 Falloff: 100, 0
Spread: 1c756 Spread: 1c756