mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-30 09:03:21 -04:00
160 lines
5.0 KiB
C#
160 lines
5.0 KiB
C#
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
using System;
|
|
using ClassicalSharp.Events;
|
|
using ClassicalSharp.GraphicsAPI;
|
|
|
|
namespace ClassicalSharp.Particles {
|
|
|
|
public partial class ParticleManager : IGameComponent {
|
|
|
|
public int ParticlesTexId;
|
|
TerrainParticle[] terrainParticles = new TerrainParticle[maxParticles];
|
|
RainParticle[] rainParticles = new RainParticle[maxParticles];
|
|
int terrainCount, rainCount;
|
|
int[] terrain1DCount, terrain1DIndices;
|
|
|
|
Game game;
|
|
Random rnd = new Random();
|
|
int vb;
|
|
const int maxParticles = 600;
|
|
|
|
public void Init(Game game) {
|
|
this.game = game;
|
|
game.Events.TerrainAtlasChanged += TerrainAtlasChanged;
|
|
game.UserEvents.BlockChanged += BreakBlockEffect;
|
|
game.Events.TextureChanged += TextureChanged;
|
|
|
|
ContextRecreated();
|
|
game.Graphics.ContextLost += ContextLost;
|
|
game.Graphics.ContextRecreated += ContextRecreated;
|
|
}
|
|
|
|
public void Ready(Game game) { }
|
|
public void Reset(Game game) { rainCount = 0; terrainCount = 0; }
|
|
public void OnNewMap(Game game) { rainCount = 0; terrainCount = 0; }
|
|
public void OnNewMapLoaded(Game game) { }
|
|
|
|
void TerrainAtlasChanged(object sender, EventArgs e) {
|
|
terrain1DCount = new int[game.TerrainAtlas1D.TexIds.Length];
|
|
terrain1DIndices = new int[game.TerrainAtlas1D.TexIds.Length];
|
|
}
|
|
|
|
void TextureChanged(object sender, TextureEventArgs e) {
|
|
if (e.Name == "particles.png")
|
|
game.UpdateTexture(ref ParticlesTexId, e.Name, e.Data, false);
|
|
}
|
|
|
|
VertexP3fT2fC4b[] vertices = new VertexP3fT2fC4b[0];
|
|
public void Render(double delta, float t) {
|
|
if (terrainCount == 0 && rainCount == 0) return;
|
|
if (game.Graphics.LostContext) return;
|
|
|
|
IGraphicsApi gfx = game.Graphics;
|
|
gfx.Texturing = true;
|
|
gfx.AlphaTest = true;
|
|
gfx.SetBatchFormat(VertexFormat.P3fT2fC4b);
|
|
|
|
RenderTerrainParticles(gfx, terrainParticles, terrainCount, delta, t);
|
|
RenderRainParticles(gfx, rainParticles, rainCount, delta, t);
|
|
|
|
gfx.AlphaTest = false;
|
|
gfx.Texturing = false;
|
|
}
|
|
|
|
void RenderTerrainParticles(IGraphicsApi gfx, Particle[] particles, int elems, double delta, float t) {
|
|
int count = elems * 4;
|
|
if (count > vertices.Length)
|
|
vertices = new VertexP3fT2fC4b[count];
|
|
|
|
Update1DCounts(particles, elems);
|
|
for (int i = 0; i < elems; i++) {
|
|
int index = particles[i].Get1DBatch(game);
|
|
particles[i].Render(game, delta, t, vertices, ref terrain1DIndices[index]);
|
|
}
|
|
int drawCount = Math.Min(count, maxParticles * 4);
|
|
if (drawCount == 0) return;
|
|
|
|
gfx.SetDynamicVbData(vb, vertices, drawCount);
|
|
int offset = 0;
|
|
for (int i = 0; i < terrain1DCount.Length; i++) {
|
|
int partCount = terrain1DCount[i];
|
|
if (partCount == 0) continue;
|
|
|
|
gfx.BindTexture(game.TerrainAtlas1D.TexIds[i]);
|
|
gfx.DrawIndexedVb(DrawMode.Triangles, partCount * 6 / 4, offset * 6 / 4);
|
|
offset += partCount;
|
|
}
|
|
}
|
|
|
|
void Update1DCounts(Particle[] particles, int elems) {
|
|
for (int i = 0; i < terrain1DCount.Length; i++) {
|
|
terrain1DCount[i] = 0;
|
|
terrain1DIndices[i] = 0;
|
|
}
|
|
for (int i = 0; i < elems; i++) {
|
|
int index = particles[i].Get1DBatch(game);
|
|
terrain1DCount[index] += 4;
|
|
}
|
|
for (int i = 1; i < terrain1DCount.Length; i++)
|
|
terrain1DIndices[i] = terrain1DIndices[i - 1] + terrain1DCount[i - 1];
|
|
}
|
|
|
|
void RenderRainParticles(IGraphicsApi gfx, Particle[] particles, int elems, double delta, float t) {
|
|
int count = elems * 4;
|
|
if (count > vertices.Length)
|
|
vertices = new VertexP3fT2fC4b[count];
|
|
|
|
int index = 0;
|
|
for (int i = 0; i < elems; i++)
|
|
particles[i].Render(game, delta, t, vertices, ref index);
|
|
|
|
int drawCount = Math.Min(count, maxParticles * 4);
|
|
if (drawCount == 0) return;
|
|
gfx.BindTexture(ParticlesTexId);
|
|
gfx.UpdateDynamicIndexedVb(DrawMode.Triangles, vb, vertices, drawCount);
|
|
}
|
|
|
|
public void Tick(ScheduledTask task) {
|
|
TickParticles(terrainParticles, ref terrainCount, task.Interval);
|
|
TickParticles(rainParticles, ref rainCount, task.Interval);
|
|
}
|
|
|
|
void TickParticles(Particle[] particles, ref int count, double delta) {
|
|
for (int i = 0; i < count; i++) {
|
|
Particle particle = particles[i];
|
|
if (particle.Tick(game, delta)) {
|
|
RemoveAt(i, particles, ref count);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose() {
|
|
game.Graphics.DeleteTexture(ref ParticlesTexId);
|
|
game.UserEvents.BlockChanged -= BreakBlockEffect;
|
|
game.Events.TerrainAtlasChanged -= TerrainAtlasChanged;
|
|
game.Events.TextureChanged -= TextureChanged;
|
|
|
|
ContextLost();
|
|
game.Graphics.ContextLost -= ContextLost;
|
|
game.Graphics.ContextRecreated -= ContextRecreated;
|
|
}
|
|
|
|
void RemoveAt<T>(int index, T[] particles, ref int count) where T : Particle {
|
|
T removed = particles[index];
|
|
for (int i = index; i < count - 1; i++) {
|
|
particles[i] = particles[i + 1];
|
|
}
|
|
|
|
particles[count - 1] = removed;
|
|
count--;
|
|
}
|
|
|
|
void ContextLost() { game.Graphics.DeleteVb(ref vb); }
|
|
|
|
void ContextRecreated() {
|
|
vb = game.Graphics.CreateDynamicVb(VertexFormat.P3fT2fC4b, maxParticles * 4);
|
|
}
|
|
}
|
|
}
|