Add rain particles, use particle pooling.

This commit is contained in:
UnknownShadow200 2015-12-17 18:35:05 +11:00
parent bd82a60d51
commit fa358f9ad2
12 changed files with 115 additions and 71 deletions

View File

@ -35,7 +35,7 @@
<DefineConstants>DEBUG;TRACE;</DefineConstants>
<StartAction>Project</StartAction>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<StartArguments>wwwf mppass 127.0.0.1 25565</StartArguments>
<StartArguments>wwwf sdf 127.0.0.1 25565</StartArguments>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>..\output\release\</OutputPath>

View File

@ -279,7 +279,7 @@ namespace ClassicalSharp {
/// <summary> Unpacks the given index into the map's block array into its original world coordinates. </summary>
public Vector3I GetCoords( int index ) {
if( index < 0 || index >= mapData.Length )
return new Vector3I( -1, -1, -1 );
return new Vector3I( -1 );
int x = index % Width;
int y = index / oneY; // index / (width * length)
int z = (index / Width) % Length;

View File

@ -148,6 +148,10 @@ namespace ClassicalSharp {
NetWriter writer;
void SendPacket() {
if( Disconnected ) {
writer.index = 0;
return;
}
try {
writer.Send( Disconnected );
} catch( IOException ex ) {
@ -163,9 +167,9 @@ namespace ClassicalSharp {
void ReadPacket( byte opcode ) {
reader.Remove( 1 ); // remove opcode
lastOpcode = (PacketId)opcode;
Action handler;
Action handler = handlers[opcode];
if( opcode >= maxHandledPacket || (handler = handlers[opcode]) == null)
if( handler == null )
throw new NotImplementedException( "Unsupported packet:" + (PacketId)opcode );
handler();
}

View File

@ -53,10 +53,8 @@ namespace ClassicalSharp {
}
public void Send( bool disconnected ) {
int packetLength = index;
Stream.Write( buffer, 0, index );
index = 0;
if( !disconnected )
Stream.Write( buffer, 0, packetLength );
}
}
}

View File

@ -5,17 +5,22 @@ namespace ClassicalSharp.Particles {
public abstract class CollidableParticle : Particle {
const float gravity = 3.4f;
//const float gravity = 0.7f; // TODO: temp debug
protected bool hitTerrain = false;
public CollidableParticle( Game game ) : base( game) { }
public CollidableParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime )
: base( game, pos, velocity, lifetime ) {
public void ResetState( Vector3 pos, Vector3 velocity, double lifetime ) {
Position = lastPos = nextPos = pos;
Velocity = velocity;
Lifetime = (float)lifetime;
hitTerrain = false;
}
public override bool Tick( double delta ) {
protected bool Tick( float gravity, double delta ) {
hitTerrain = false;
lastPos = Position = nextPos;
byte curBlock = game.Map.SafeGetBlock( (int)Position.X, (int)Position.Y, (int)Position.Z );
if( !CanPassThrough( curBlock ) ) return true;
float blockY = (int)Position.Y + game.BlockInfo.Height[curBlock];
if( !CanPassThrough( curBlock ) && Position.Y < blockY ) return true;
Velocity.Y -= gravity * (float)delta;
int startY = (int)Math.Floor( Position.Y );
@ -25,7 +30,8 @@ namespace ClassicalSharp.Particles {
Utils.Clamp( ref Position.Z, 0, game.Map.Length - 0.01f );
if( Velocity.Y > 0 ) {
for( int y = startY; y <= endY && TestY( y, false ); y++ );
// don't test block we are already in
for( int y = startY + 1; y <= endY && TestY( y, false ); y++ );
} else {
for( int y = startY; y >= endY && TestY( y, true ); y-- );
}
@ -38,21 +44,20 @@ namespace ClassicalSharp.Particles {
if( y < 0 ) {
Position.Y = nextPos.Y = lastPos.Y = 0 + Entity.Adjustment;
Velocity = Vector3.Zero;
hitTerrain = true;
return false;
}
byte block = game.Map.SafeGetBlock( (int)Position.X, y, (int)Position.Z );
if( CanPassThrough( block ) ) return true;
float collideY = y;
if( topFace )
collideY += game.BlockInfo.Height[block];
float collideY = y + (topFace ? game.BlockInfo.Height[block] : 0);
bool collide = topFace ? (Position.Y < collideY) : (Position.Y > collideY);
if( collide ) {
float adjust = topFace ? Entity.Adjustment : -Entity.Adjustment;
Position.Y = nextPos.Y = lastPos.Y = collideY + adjust;
Velocity = Vector3.Zero;
hitTerrain = true;
return false;
}
return true;

View File

@ -13,12 +13,7 @@ namespace ClassicalSharp.Particles {
public abstract void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index );
public Particle( Game game, Vector3 pos, Vector3 velocity, double lifetime ) {
this.game = game;
Position = lastPos = nextPos = pos;
Velocity = velocity;
Lifetime = (float)lifetime;
}
public Particle( Game game ) { this.game = game; }
public virtual bool Tick( double delta ) {
Lifetime -= (float)delta;

View File

@ -8,32 +8,35 @@ namespace ClassicalSharp.Particles {
public class ParticleManager : IDisposable {
public int ParticlesTexId;
List<Particle> terrainParticles = new List<Particle>();
List<Particle> rainParticles = new List<Particle>();
TerrainParticle[] terrainParticles = new TerrainParticle[maxParticles];
RainParticle[] rainParticles = new RainParticle[maxParticles];
int terrainCount, rainCount;
Game game;
Random rnd;
int vb;
const int maxParticles = 600;
public ParticleManager( Game game ) {
this.game = game;
rnd = new Random();
vb = game.Graphics.CreateDynamicVb( VertexFormat.Pos3fTex2fCol4b, 1000 );
vb = game.Graphics.CreateDynamicVb( VertexFormat.Pos3fTex2fCol4b, maxParticles * 4 );
}
VertexPos3fTex2fCol4b[] vertices = new VertexPos3fTex2fCol4b[0];
public void Render( double delta, float t ) {
if( terrainParticles.Count == 0 && rainParticles.Count == 0 ) return;
if( terrainCount == 0 && rainCount == 0 ) return;
IGraphicsApi graphics = game.Graphics;
graphics.Texturing = true;
graphics.AlphaTest = true;
graphics.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
int count = RenderParticles( terrainParticles, delta, t );
int count = RenderParticles( terrainParticles, terrainCount, delta, t );
if( count > 0 ) {
graphics.BindTexture( game.TerrainAtlas.TexId );
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, vb, vertices, count, count * 6 / 4 );
}
count = RenderParticles( rainParticles, delta, t );
count = RenderParticles( rainParticles, rainCount, delta, t );
if( count > 0 ) {
graphics.BindTexture( ParticlesTexId );
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, vb, vertices, count, count * 6 / 4 );
@ -43,27 +46,27 @@ namespace ClassicalSharp.Particles {
graphics.Texturing = false;
}
int RenderParticles( List<Particle> particles, double delta, float t ) {
int count = particles.Count * 4;
int RenderParticles( Particle[] particles, int elems, double delta, float t ) {
int count = elems * 4;
if( count > vertices.Length )
vertices = new VertexPos3fTex2fCol4b[count];
int index = 0;
for( int i = 0; i < particles.Count; i++ )
for( int i = 0; i < elems; i++ )
particles[i].Render( delta, t, vertices, ref index );
return Math.Min( count, 1000 );
return Math.Min( count, maxParticles * 4 );
}
public void Tick( double delta ) {
TickParticles( terrainParticles, delta );
TickParticles( rainParticles, delta );
TickParticles( terrainParticles, ref terrainCount, delta );
TickParticles( rainParticles, ref rainCount, delta );
}
void TickParticles( List<Particle> particles, double delta ) {
for( int i = 0; i < particles.Count; i++ ) {
void TickParticles( Particle[] particles, ref int count, double delta ) {
for( int i = 0; i < count; i++ ) {
Particle particle = particles[i];
if( particle.Tick( delta ) ) {
particles.RemoveAt( i );
RemoveAt( i, particles, ref count );
i--;
}
}
@ -103,27 +106,56 @@ namespace ClassicalSharp.Particles {
particleRec.V2 = particleRec.V1 + elemSize;
double life = 1.5 - rnd.NextDouble();
terrainParticles.Add( new TerrainParticle( game, pos, velocity, life, particleRec ) );
TerrainParticle p = AddParticle( terrainParticles, ref terrainCount, false );
p.ResetState( pos, velocity, life );
p.rec = particleRec;
}
}
public void AddRainParticle( Vector3 pos ) {
Vector3 startPos = pos;
for( int i = 0; i < 5; i++ ) {
for( int i = 0; i < 2; i++ ) {
double velX = rnd.NextDouble() * 0.8 - 0.4; // [-0.4, 0.4]
double velZ = rnd.NextDouble() * 0.8 - 0.4;
double velY = rnd.NextDouble() + 0.5;
double velY = rnd.NextDouble() + 0.4;
Vector3 velocity = new Vector3( (float)velX, (float)velY, (float)velZ );
double xOffset = rnd.NextDouble() - 0.5; // [-0.5, 0.5]
double yOffset = 0.01;
double yOffset = rnd.NextDouble() * 0.1 + 0.01;
double zOffset = rnd.NextDouble() - 0.5;
pos = startPos + new Vector3( 0.5f + (float)xOffset,
(float)yOffset, 0.5f + (float)zOffset );
double life = 3.5 - rnd.NextDouble();
rainParticles.Add( new RainParticle( game, pos, velocity, life ) );
double life = 40;
RainParticle p = AddParticle( rainParticles, ref rainCount, true );
p.ResetState( pos, velocity, life );
p.Big = rnd.Next( 0, 20 ) >= 18;
p.Tiny = rnd.Next( 0, 30 ) >= 28;
}
}
T AddParticle<T>( T[] particles, ref int count, bool rain ) where T : Particle {
if( count == maxParticles )
RemoveAt( 0, particles, ref count );
count++;
T old = particles[count - 1];
if( old != null ) return old;
T newT = rain ? (T)(object)new RainParticle( game )
: (T)(object)new TerrainParticle( game );
particles[count - 1] = newT;
return newT;
}
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--;
}
}
}

View File

@ -5,16 +5,25 @@ namespace ClassicalSharp.Particles {
public sealed class RainParticle : CollidableParticle {
static Vector2 rainSize = new Vector2( 1/8f, 1/8f );
static Vector2 bigSize = new Vector2( 1/16f, 1/16f );
static Vector2 smallSize = new Vector2( 0.75f/16f, 0.75f/16f );
static Vector2 tinySize = new Vector2( 0.5f/16f, 0.5f/16f );
static TextureRec rec = new TextureRec( 2/128f, 14/128f, 3/128f, 2/128f );
public RainParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime )
: base( game, pos, velocity, lifetime ) {
public RainParticle( Game game ) : base( game ) { }
public bool Big, Tiny;
public override bool Tick( double delta ) {
bool dies = Tick( 3.5f, delta );
return hitTerrain ? true : dies;
}
public override void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index ) {
Position = Vector3.Lerp( lastPos, nextPos, t );
Vector3 p111, p121, p212, p222;
Utils.CalcBillboardPoints( rainSize, Position, ref game.View,
Vector2 size = Big ? bigSize : (Tiny ? tinySize : smallSize );
Utils.CalcBillboardPoints( size, Position, ref game.View,
out p111, out p121, out p212, out p222 );
Map map = game.Map;
FastColour col = map.IsLit( Vector3I.Floor( Position ) ) ? map.Sunlight : map.Shadowlight;

View File

@ -6,10 +6,12 @@ namespace ClassicalSharp.Particles {
public sealed class TerrainParticle : CollidableParticle {
static Vector2 terrainSize = new Vector2( 1/8f, 1/8f );
TextureRec rec;
public TerrainParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime, TextureRec rec )
: base( game, pos, velocity, lifetime ) {
this.rec = rec;
internal TextureRec rec;
public TerrainParticle( Game game ) : base( game ) { }
public override bool Tick( double delta ) {
return Tick( 5.4f, delta );
}
public override void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index ) {

View File

@ -24,6 +24,7 @@ namespace ClassicalSharp {
const int extent = 4;
VertexPos3fTex2fCol4b[] vertices = new VertexPos3fTex2fCol4b[8 * (extent * 2 + 1) * (extent * 2 + 1)];
double rainAcc;
Vector3I lastPos = new Vector3I( Int32.MinValue );
public void Render( double deltaTime ) {
Weather weather = map.Weather;
@ -33,7 +34,9 @@ namespace ClassicalSharp {
graphics.BindTexture( weather == Weather.Rainy ? game.RainTexId : game.SnowTexId );
Vector3 eyePos = game.LocalPlayer.EyePosition;
Vector3 camPos = game.Camera.GetCameraPos( eyePos );
Vector3I pos = Vector3I.Floor( eyePos ); // TODO: cam pos
Vector3I pos = Vector3I.Floor( camPos );
bool moved = pos != lastPos;
lastPos = pos;
float speed = weather == Weather.Rainy ? 1f : 0.25f;
vOffset = -(float)game.accumulator * speed;
@ -49,16 +52,15 @@ namespace ClassicalSharp {
float height = Math.Max( game.Map.Height, pos.Y + 64 ) - rainY;
if( height <= 0 ) continue;
//if( rainAcc >= 2 )
// game.ParticleManager.AddRainParticle( new Vector3( pos.X + dx, rainY, pos.Z + dz ) );
if( rainAcc >= 0.25 || moved )
game.ParticleManager.AddRainParticle( new Vector3( pos.X + dx, rainY, pos.Z + dz ) );
col.A = (byte)Math.Max( 0, AlphaAt( dx * dx + dz * dz ) );
MakeRainForSquare( pos.X + dx, rainY, height, pos.Z + dz, col, ref index );
}
}
if( rainAcc >= 2 )
if( rainAcc >= 0.25 || moved )
rainAcc = 0;
// fixes crashing on nVidia cards in OpenGL builds.
if( index > 0 ) {
graphics.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, weatherVb, vertices, index, index * 6 / 4 );
@ -92,6 +94,7 @@ namespace ClassicalSharp {
int length, width, maxY, oneY;
void OnNewMap( object sender, EventArgs e ) {
heightmap = null;
lastPos = new Vector3I( Int32.MaxValue );
}
void OnNewMapLoaded( object sender, EventArgs e ) {

View File

@ -104,7 +104,7 @@ namespace ClassicalSharp.Singleplayer {
this.generator = generator;
game.SetNewScreen( new LoadingMapScreen( game, "Single player", "Generating.." ) );
generator.GenerateAsync( game, width, height, length, 0 );
generator.GenerateAsync( game, width, height, length, seed );
}
unsafe void MapSet( int width, int length, byte* ptr, int yStart, int yEnd, byte block ) {

View File

@ -28,15 +28,11 @@ namespace ClassicalSharp {
}
public Vector3I( int x, int y, int z ) {
X = x;
Y = y;
Z = z;
X = x; Y = y; Z = z;
}
public Vector3I( int value ) {
X = value;
Y = value;
Z = value;
X = value; Y = value; Z = value;
}
public static Vector3I operator + ( Vector3I left, Vector3I right ) {