Added explosion
This commit is contained in:
parent
59ae0bc41a
commit
7f7a21c110
8 changed files with 345 additions and 0 deletions
|
|
@ -11,12 +11,18 @@
|
|||
<Compile Remove="Assemblies\**" />
|
||||
<EmbeddedResource Remove="Assemblies\**" />
|
||||
<None Remove="Assemblies\**" />
|
||||
<None Remove="Classes\Rendering\Particles\explosion.cfx" />
|
||||
<EmbeddedResource Include="Classes\Rendering\Particles\explosion.cfx" />
|
||||
<None Update="Resources\NoiseTexture.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AStarLite" Version="1.1.0" />
|
||||
<PackageReference Include="NAudio" Version="2.1.0" />
|
||||
<PackageReference Include="NAudio.Lame" Version="2.0.1" />
|
||||
<PackageReference Include="NETEasing" Version="1.0.1" />
|
||||
<PackageReference Include="SimplexNoise" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
@ -162,4 +168,8 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Classes\Rendering\Particles\explosion.fx" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
131
CityGame/Classes/Rendering/Particles/Explosion.cs
Normal file
131
CityGame/Classes/Rendering/Particles/Explosion.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using CityGame.Classes.Rendering.Shaders;
|
||||
using CityGame.Classes.World;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using OrpticonGameHelper;
|
||||
using OrpticonGameHelper.Classes.Particles;
|
||||
using OrpticonGameHelper.Classes.Utils;
|
||||
using static CityGame.MainWindow;
|
||||
|
||||
namespace CityGame.Classes.Rendering.Particles;
|
||||
|
||||
public sealed class Explosion : ParticleEmitter, IParticleHandler
|
||||
{
|
||||
internal const string SHADER_PATH = "CityGame.Classes.Rendering.Particles.explosion.cfx";
|
||||
private const float PIXEL_TO_TILE_RATIO = 1 / 2f;
|
||||
|
||||
private static Effect? _explosionShader = null;
|
||||
|
||||
public Color Color = Color.Yellow;
|
||||
|
||||
public Explosion()
|
||||
: base(Explosion.GetShader())
|
||||
{
|
||||
this.ParticleHandler = this;
|
||||
}
|
||||
|
||||
private static Effect GetShader()
|
||||
{
|
||||
if (Explosion._explosionShader == null)
|
||||
{
|
||||
Explosion._explosionShader = ShaderLoader.Get(Explosion.SHADER_PATH);
|
||||
}
|
||||
|
||||
return Explosion._explosionShader;
|
||||
}
|
||||
|
||||
public ICollection<IParticle> Generate()
|
||||
{
|
||||
List<IParticle> particles = new List<IParticle>();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ExplosionParticle particle = new ExplosionParticle();
|
||||
particle.OriginalPosition = particle.Position = Vector2.Zero;
|
||||
|
||||
Vector2 direction = new Vector2(Random.Shared.NextSingle(-1, 1), Random.Shared.NextSingle(-1,1));
|
||||
particle.Direction = direction;
|
||||
|
||||
particle.LifeTime = EmissionTime;
|
||||
particle.LifeTimeScale = Random.Shared.NextSingle(1, 1.5f);
|
||||
|
||||
particle.UVOffset = new Vector2(Random.Shared.NextSingle(1), Random.Shared.NextSingle(1));
|
||||
|
||||
particles.Add(particle);
|
||||
}
|
||||
|
||||
return particles;
|
||||
}
|
||||
|
||||
public void Move(ICollection<IParticle> particles, float deltatime, ParticleEmitter emitter)
|
||||
{
|
||||
|
||||
foreach (IParticle particle in particles)
|
||||
{
|
||||
float time = emitter.NormalizedTime;
|
||||
if (particle is ExplosionParticle explParticle)
|
||||
{
|
||||
explParticle.ReduceLifeTime(deltatime);
|
||||
time = ParticleEmitter.CalculateNormalizedReversedTime(explParticle.LifeTime, this.EmissionTime);
|
||||
}
|
||||
|
||||
float distanceCalc = Easing.Easing.OutExpo(time);
|
||||
float distance = distanceCalc * emitter.MaxParticleDistance;
|
||||
particle.Position = particle.OriginalPosition + particle.Direction * distance;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(ref ParticleDrawContext context)
|
||||
{
|
||||
context.Effect.Parameters["view_projection"].SetValue(context.View * context.Projection);
|
||||
context.Effect.Parameters["color"].SetValue(this.Color.ToVector3());
|
||||
|
||||
|
||||
|
||||
context.SpriteBatch.Begin(
|
||||
effect: Effect,
|
||||
blendState: BlendState.NonPremultiplied,
|
||||
sortMode: SpriteSortMode.Immediate,
|
||||
samplerState: SamplerState.LinearWrap
|
||||
);
|
||||
|
||||
float pixelSize = MainWindow.TileSize * Explosion.PIXEL_TO_TILE_RATIO;
|
||||
float sizeToTileRatio = (float)context.Size / MainWindow.TileSize;
|
||||
int resultingPixelSize = (int)Math.Ceiling(pixelSize * sizeToTileRatio);
|
||||
|
||||
context.Effect.Parameters["size"].SetValue(context.Size / 2);
|
||||
context.Effect.Parameters["pixelSize"].SetValue(resultingPixelSize / 2);
|
||||
foreach (IParticle particle in context.Particles)
|
||||
{
|
||||
if (!particle.IsActive)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float normTime = NormalizedTime;
|
||||
if (particle is ExplosionParticle explParticle)
|
||||
{
|
||||
normTime = ParticleEmitter.CalculateNormalizedReversedTime(explParticle.LifeTime, this.EmissionTime);
|
||||
context.Effect.Parameters["noiseOffset"].SetValue(explParticle.UVOffset);
|
||||
}
|
||||
float time = Easing.Easing.OutExpo(normTime);
|
||||
|
||||
context.Effect.Parameters["timing"].SetValue(time);
|
||||
context.Effect.Parameters["position"].SetValue(context.EmitterCenterPosition + particle.Position);
|
||||
int x = (int)(context.EmitterRenderPosition.X + particle.Position.X);
|
||||
int y = (int)(context.EmitterRenderPosition.Y + particle.Position.Y);
|
||||
|
||||
context.SpriteBatch.Draw(
|
||||
Window._textures[Environment.CurrentDirectory + "\\Resources\\NoiseTexture.png"],
|
||||
new Rectangle(x, y, context.Size, context.Size),
|
||||
Color.White
|
||||
);
|
||||
}
|
||||
|
||||
context.SpriteBatch.End();
|
||||
}
|
||||
}
|
||||
21
CityGame/Classes/Rendering/Particles/ExplosionParticle.cs
Normal file
21
CityGame/Classes/Rendering/Particles/ExplosionParticle.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using OrpticonGameHelper.Classes.Particles;
|
||||
|
||||
namespace CityGame.Classes.Rendering.Particles;
|
||||
|
||||
public sealed class ExplosionParticle: Particle
|
||||
{
|
||||
public float LifeTimeScale = 1;
|
||||
public float LifeTime = 1;
|
||||
public Vector2 UVOffset = Vector2.Zero;
|
||||
|
||||
public void ReduceLifeTime(float deltatime)
|
||||
{
|
||||
this.LifeTime -= deltatime * this.LifeTimeScale;
|
||||
|
||||
if (this.LifeTime <= 0)
|
||||
{
|
||||
this.IsActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
CityGame/Classes/Rendering/Particles/explosion.cfx
Normal file
BIN
CityGame/Classes/Rendering/Particles/explosion.cfx
Normal file
Binary file not shown.
121
CityGame/Classes/Rendering/Particles/explosion.fx
Normal file
121
CityGame/Classes/Rendering/Particles/explosion.fx
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#if OPENGL
|
||||
#define VS_SHADERMODEL vs_3_0
|
||||
#define PS_SHADERMODEL ps_3_0
|
||||
#else
|
||||
#define VS_SHADERMODEL vs_4_0_level_9_1
|
||||
#define PS_SHADERMODEL ps_4_0_level_9_1
|
||||
#endif
|
||||
|
||||
sampler TextureSampler : register(s0);
|
||||
|
||||
float4x4 view_projection;
|
||||
float2 position;
|
||||
int size;
|
||||
int pixelSize;
|
||||
float timing;
|
||||
float2 noiseOffset;
|
||||
|
||||
float3 color;
|
||||
|
||||
#define BLUR_KERNEL_X_MIN -0.5
|
||||
#define BLUR_KERNEL_X_MAX 0.5
|
||||
#define BLUR_KERNEL_X_JUMP 0.5
|
||||
#define BLUR_KERNEL_Y_MIN -0.5
|
||||
#define BLUR_KERNEL_Y_MAX 0.5
|
||||
#define BLUR_KERNEL_Y_JUMP 0.5
|
||||
|
||||
struct VertexInput {
|
||||
float4 Position : POSITION0;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
};
|
||||
struct PixelInput {
|
||||
float4 Position : SV_Position0;
|
||||
float2 RelativePos : TEXCOORD0;
|
||||
float2 NoiseTextureCoord : TEXCOORD1;
|
||||
};
|
||||
|
||||
float2 pixelizeCoord(float2 coord)
|
||||
{
|
||||
float2 uv = coord / size;
|
||||
float2 scaledUV = uv * pixelSize;
|
||||
|
||||
return scaledUV;
|
||||
}
|
||||
float2 scalePixelCoordBack(float2 coord)
|
||||
{
|
||||
return round(coord) / pixelSize;
|
||||
}
|
||||
|
||||
PixelInput SpriteVertexShader(VertexInput v) {
|
||||
PixelInput output;
|
||||
|
||||
output.RelativePos = pixelizeCoord(v.Position.xy - position);
|
||||
output.Position = mul(v.Position, view_projection);
|
||||
output.NoiseTextureCoord = (v.TexCoord + noiseOffset) * (pixelSize * 2);
|
||||
|
||||
return output;
|
||||
}
|
||||
float2 linear_mix(float t, float2 val1, float2 val2)
|
||||
{
|
||||
float2 outval = val1;
|
||||
|
||||
if (val2[0] > 0.5)
|
||||
outval[0] = val1[0] + t * (2.0 * (val2[0] - 0.5));
|
||||
else
|
||||
outval[0] = val1[0] + t * (2.0 * (val2[0]) - 1.0);
|
||||
|
||||
if (val2[1] > 0.5)
|
||||
outval[1] = val1[1] + t * (2.0 * (val2[1] - 0.5));
|
||||
else
|
||||
outval[1] = val1[1] + t * (2.0 * (val2[1]) - 1.0);
|
||||
|
||||
return outval;
|
||||
}
|
||||
|
||||
float2 getNoise(float2 uv)
|
||||
{
|
||||
return tex2D(TextureSampler, uv).rg;
|
||||
}
|
||||
|
||||
float calculateAlpha(float2 uv, float2 noiseUV)
|
||||
{
|
||||
float2 actualUV = scalePixelCoordBack(uv);
|
||||
|
||||
float2 lengthUV = linear_mix(0.5, actualUV, getNoise(actualUV + noiseOffset));
|
||||
|
||||
return step(length(lengthUV), timing);
|
||||
}
|
||||
|
||||
float4 SpritePixelShader(PixelInput p) : SV_TARGET {
|
||||
|
||||
float2 noiseCoord = round(p.NoiseTextureCoord) / (pixelSize * 2);
|
||||
float2 relPos = abs(p.RelativePos);
|
||||
|
||||
int sampleAmount = 0;
|
||||
float val = 0;
|
||||
for(float x = BLUR_KERNEL_X_MIN; x <= BLUR_KERNEL_X_MAX; x += BLUR_KERNEL_X_JUMP)
|
||||
{
|
||||
for(float y = BLUR_KERNEL_X_MIN; y <= BLUR_KERNEL_X_MAX; y += BLUR_KERNEL_Y_JUMP)
|
||||
{
|
||||
float2 alphaUV = relPos + float2(x,y);
|
||||
val += calculateAlpha(alphaUV, noiseCoord);
|
||||
sampleAmount++;
|
||||
}
|
||||
}
|
||||
|
||||
val = val / sampleAmount;
|
||||
|
||||
val = val + calculateAlpha(relPos, noiseCoord);
|
||||
|
||||
float noiseResult = getNoise(noiseCoord).r;
|
||||
float3 resultColor = color * lerp(0.5, 1, noiseResult);
|
||||
|
||||
return float4(resultColor,val);
|
||||
}
|
||||
|
||||
technique SpriteBatch {
|
||||
pass {
|
||||
VertexShader = compile VS_SHADERMODEL SpriteVertexShader();
|
||||
PixelShader = compile PS_SHADERMODEL SpritePixelShader();
|
||||
}
|
||||
}
|
||||
58
CityGame/Classes/Rendering/Shaders/ShaderLoader.cs
Normal file
58
CityGame/Classes/Rendering/Shaders/ShaderLoader.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using CityGame.Classes.Rendering.Particles;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using OrpticonGameHelper;
|
||||
using OrpticonGameHelper.Classes;
|
||||
using OrpticonGameHelper.Classes.Utils;
|
||||
|
||||
namespace CityGame.Classes.Rendering.Shaders;
|
||||
|
||||
public sealed class ShaderLoader : ILoadable
|
||||
{
|
||||
private static string[] _shaderPaths = new[]
|
||||
{
|
||||
Explosion.SHADER_PATH
|
||||
};
|
||||
|
||||
private static ShaderLoader _instance;
|
||||
|
||||
private Dictionary<string, Effect> _effects = new Dictionary<string, Effect>();
|
||||
|
||||
private ShaderLoader() {}
|
||||
|
||||
public static ShaderLoader Create()
|
||||
{
|
||||
if (ShaderLoader._instance != null)
|
||||
{
|
||||
throw new Exception("There can only be one ShaderLoader.");
|
||||
}
|
||||
|
||||
ShaderLoader._instance = new ShaderLoader();
|
||||
return ShaderLoader._instance;
|
||||
}
|
||||
|
||||
public void Load(Window window)
|
||||
{
|
||||
|
||||
foreach (string shaderPath in ShaderLoader._shaderPaths)
|
||||
{
|
||||
byte[] shaderCode = AssemblyUtility.ReadAssemblyFileAsByte(shaderPath);
|
||||
|
||||
Effect shader = new Effect(window.GraphicsDevice, shaderCode);
|
||||
this._effects[shaderPath] = shader;
|
||||
}
|
||||
}
|
||||
|
||||
public static Effect Get(string path)
|
||||
{
|
||||
if (!ShaderLoader._instance._effects.ContainsKey(path))
|
||||
{
|
||||
throw new Exception("No effect loaded with the path '" + path + "'.");
|
||||
}
|
||||
|
||||
return ShaderLoader._instance._effects[path];
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ using SimplexNoise;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CityGame.Classes.Rendering.Particles;
|
||||
using CityGame.Classes.Rendering.Shaders;
|
||||
using OrpticonGameHelper;
|
||||
using OrpticonGameHelper.Classes.Elements;
|
||||
using static CityGame.Classes.Entities.Car;
|
||||
|
|
@ -160,6 +162,8 @@ namespace CityGame
|
|||
ImageConverter.ChangeColor("Car", "NPCCar", ColorConversionMaps.CarToNPCCar);
|
||||
ImageConverter.ChangeColor("Car", "PoliceCar", ColorConversionMaps.CarToPoliceCar);
|
||||
#endregion
|
||||
|
||||
LoadableContent.Add(ShaderLoader.Create());
|
||||
|
||||
int seed = 8;
|
||||
|
||||
|
|
|
|||
BIN
CityGame/Resources/NoiseTexture.png
Normal file
BIN
CityGame/Resources/NoiseTexture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Loading…
Add table
Add a link
Reference in a new issue