diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj
index cfcc9f9c7..aba30613f 100644
--- a/ClassicalSharp/ClassicalSharp.csproj
+++ b/ClassicalSharp/ClassicalSharp.csproj
@@ -19,7 +19,7 @@
4
- AnyCPU
+ x86
4194304
False
Auto
@@ -108,6 +108,7 @@
+
@@ -159,12 +160,12 @@
-
+
diff --git a/ClassicalSharp/Entities/Entity.cs b/ClassicalSharp/Entities/Entity.cs
index d05c8fad6..d4c4f0c12 100644
--- a/ClassicalSharp/Entities/Entity.cs
+++ b/ClassicalSharp/Entities/Entity.cs
@@ -4,7 +4,7 @@ using OpenTK;
namespace ClassicalSharp {
- public abstract partial class Entity {
+ public abstract class Entity {
public Entity( Game game ) {
map = game.Map;
@@ -19,6 +19,8 @@ namespace ClassicalSharp {
public Vector3 Velocity;
public float YawDegrees, PitchDegrees;
protected float StepSize;
+ protected Map map;
+ protected BlockInfo info;
public float YawRadians {
get { return YawDegrees * Utils.Deg2Rad; }
@@ -87,5 +89,7 @@ namespace ClassicalSharp {
}
return false;
}
+
+ public const float Adjustment = 0.001f;
}
}
\ No newline at end of file
diff --git a/ClassicalSharp/Entities/LocalPlayer.cs b/ClassicalSharp/Entities/LocalPlayer.cs
index ecdee3863..6761d4e76 100644
--- a/ClassicalSharp/Entities/LocalPlayer.cs
+++ b/ClassicalSharp/Entities/LocalPlayer.cs
@@ -143,7 +143,7 @@ namespace ClassicalSharp {
const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f;
void PhysicsTick( float xMoving, float zMoving ) {
- float multiply = flying ? ( speeding ? 90 : 15 ) : ( speeding ? 10 : 1 );
+ float multiply = flying ? (speeding ? 90 : 15) : (speeding ? 10 : 1);
float modifier = LowestSpeedModifier();
multiply *= modifier;
@@ -155,7 +155,7 @@ namespace ClassicalSharp {
Move( xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, 1 );
} else {
float factor = !flying && onGround ? 0.1f : 0.02f;
- float yMul = modifier * Math.Max( 1, multiply / 5f );
+ float yMul = multiply == 1 ? 1 : multiply / 5f;
float gravity = useLiquidGravity ? liquidGrav : normalGrav;
Move( xMoving, zMoving, factor * multiply, normalDrag, gravity, yMul );
diff --git a/ClassicalSharp/Physics/Entity.Physics.cs b/ClassicalSharp/Entities/PhysicsEntity.cs
similarity index 94%
rename from ClassicalSharp/Physics/Entity.Physics.cs
rename to ClassicalSharp/Entities/PhysicsEntity.cs
index 6419cc135..cfb75e3a8 100644
--- a/ClassicalSharp/Physics/Entity.Physics.cs
+++ b/ClassicalSharp/Entities/PhysicsEntity.cs
@@ -1,221 +1,221 @@
-using System;
-using System.Collections.Generic;
-using OpenTK;
-
-namespace ClassicalSharp {
-
- public partial class Entity {
-
- protected bool onGround, collideX, collideY, collideZ;
- protected Map map;
- protected BlockInfo info;
- public const float Adjustment = 0.001f;
-
- protected byte GetPhysicsBlockId( int x, int y, int z ) {
- if( x < 0 || x >= map.Width || z < 0 || z >= map.Length || y < 0 ) return (byte)Block.Bedrock;
- if( y >= map.Height ) return (byte)Block.Air;
- return map.GetBlock( x, y, z );
- }
-
- bool GetBoundingBox( byte block, int x, int y, int z, out BoundingBox box ) {
- box = new BoundingBox( Vector3.Zero, Vector3.Zero );
- if( info.CollideType[block] != BlockCollideType.Solid ) return false;
- Vector3 min = new Vector3( x, y, z );
- Vector3 max = new Vector3( x + 1, y + info.Height[block], z + 1 );
- box = new BoundingBox( min, max );
- return true;
- }
-
- struct State {
- public BoundingBox BlockBB;
- public byte Block;
- public float tSquared;
-
- public State( BoundingBox bb, byte block, float tSquared ) {
- BlockBB = bb;
- Block = block;
- this.tSquared = tSquared;
- }
- }
-
- // TODO: test for corner cases, and refactor this.
- static State[] stateCache = new State[0];
- class StateComparer : IComparer {
- public int Compare( State x, State y ) {
- return x.tSquared.CompareTo( y.tSquared );
- }
- }
- static StateComparer comparer = new StateComparer();
- protected void MoveAndWallSlide() {
- if( Velocity == Vector3.Zero )
- return;
- Vector3 size = CollisionSize;
- BoundingBox entityBB, entityExtentBB;
- int count = 0;
- FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB );
- CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB );
- }
-
- void FindReachableBlocks( ref int count, ref Vector3 size,
- out BoundingBox entityBB, out BoundingBox entityExtentBB ) {
- Vector3 vel = Velocity;
- Vector3 pos = Position;
- entityBB = new BoundingBox(
- pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2,
- pos.X + size.X / 2, pos.Y + size.Y, pos.Z + size.Z / 2
- );
-
- // Exact maximum extent the entity can reach, and the equivalent map coordinates.
- entityExtentBB = new BoundingBox(
- vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X,
- vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y,
- vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z,
- vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X,
- vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y,
- vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z
- );
- Vector3I min = Vector3I.Floor( entityExtentBB.Min );
- Vector3I max = Vector3I.Floor( entityExtentBB.Max );
-
- int elements = ( max.X + 1 - min.X ) * ( max.Y + 1 - min.Y ) * ( max.Z + 1 - min.Z );
- if( elements > stateCache.Length ) {
- stateCache = new State[elements];
- }
-
- for( int x = min.X; x <= max.X; x++ ) {
- for( int y = min.Y; y <= max.Y; y++ ) {
- for( int z = min.Z; z <= max.Z; z++ ) {
- byte blockId = GetPhysicsBlockId( x, y, z );
- BoundingBox blockBB;
- if( !GetBoundingBox( blockId, x, y, z, out blockBB ) ) continue;
- if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for some non whole blocks. (slabs)
-
- float tx = 0, ty = 0, tz = 0;
- CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz );
- if( tx > 1 || ty > 1 || tz > 1 ) continue;
- float tSquared = tx * tx + ty * ty + tz * tz;
- stateCache[count++] = new State( blockBB, blockId, tSquared );
- }
- }
- }
- }
-
- void CollideWithReachableBlocks( int count, ref Vector3 size,
- ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
- bool wasOn = onGround;
- onGround = false;
- Array.Sort( stateCache, 0, count, comparer );
- collideX = false; collideY = false; collideZ = false;
-
- for( int i = 0; i < count; i++ ) {
- State state = stateCache[i];
- BoundingBox blockBB = state.BlockBB;
- if( !entityExtentBB.Intersects( blockBB ) ) continue;
-
- float tx = 0, ty = 0, tz = 0;
- CalcTime( ref Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz );
- if( tx > 1 || ty > 1 || tz > 1 )
- Utils.LogWarning( "t > 1 in physics calculation.. this shouldn't have happened." );
- BoundingBox finalBB = entityBB.Offset( Velocity * new Vector3( tx, ty, tz ) );
-
- if( finalBB.Min.Y >= blockBB.Max.Y ) {
- Position.Y = blockBB.Max.Y + Adjustment;
- onGround = true;
- ClipY( ref size, ref entityBB, ref entityExtentBB );
- } else if( finalBB.Max.Y <= blockBB.Min.Y ) {
- Position.Y = blockBB.Min.Y - size.Y - Adjustment;
- ClipY( ref size, ref entityBB, ref entityExtentBB );
- } else if( finalBB.Min.X >= blockBB.Max.X ) {
- if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
- Position.X = blockBB.Max.X + size.X / 2 + Adjustment;
- ClipX( ref size, ref entityBB, ref entityExtentBB );
- }
- } else if( finalBB.Max.X <= blockBB.Min.X ) {
- if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
- Position.X = blockBB.Min.X - size.X / 2 - Adjustment;
- ClipX( ref size, ref entityBB, ref entityExtentBB );
- }
- } else if( finalBB.Min.Z >= blockBB.Max.Z ) {
- if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
- Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment;
- ClipZ( ref size, ref entityBB, ref entityExtentBB );
- }
- } else if( finalBB.Max.Z <= blockBB.Min.Z ) {
- if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
- Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment;
- ClipZ( ref size, ref entityBB, ref entityExtentBB );
- }
- }
- }
- }
-
- bool DidSlide( ref BoundingBox blockBB, ref Vector3 size, ref BoundingBox finalBB,
- ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
- float yDist = blockBB.Max.Y - entityBB.Min.Y;
- if( yDist > 0 && yDist <= StepSize + 0.01f ) {
-
- // Adjust entity bounding box to include the block being tested
- BoundingBox adjFinalBB = finalBB;
- adjFinalBB.Min.X = Math.Min( finalBB.Min.X, blockBB.Min.X + Adjustment );
- adjFinalBB.Max.X = Math.Max( finalBB.Max.X, blockBB.Max.X - Adjustment );
- adjFinalBB.Min.Y = (float)Math.Ceiling( blockBB.Max.Y ) + Adjustment;
- adjFinalBB.Max.Y = adjFinalBB.Min.Y + size.Y;
- adjFinalBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment );
- adjFinalBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment );
-
- Vector3I min = Vector3I.Floor( adjFinalBB.Min );
- Vector3I max = Vector3I.Floor( adjFinalBB.Max );
- for( int x = min.X; x <= max.X; x++ ) {
- for( int y = min.Y; y <= max.Y; y++ ) {
- for( int z = min.Z; z <= max.Z; z++ ) {
- if( info.CollideType[GetPhysicsBlockId( x, y, z )] == BlockCollideType.Solid )
- return false;
- }
- }
- }
-
- Position.Y = blockBB.Max.Y + Adjustment;
- onGround = true;
- ClipY( ref size, ref entityBB, ref entityExtentBB );
- return true;
- }
- return false;
- }
-
- void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
- Velocity.X = 0;
- entityBB.Min.X = entityExtentBB.Min.X = Position.X - size.X / 2;
- entityBB.Max.X = entityExtentBB.Max.X = Position.X + size.X / 2;
- collideX = true;
- }
-
- void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
- Velocity.Y = 0;
- entityBB.Min.Y = entityExtentBB.Min.Y = Position.Y;
- entityBB.Max.Y = entityExtentBB.Max.Y = Position.Y + size.Y;
- collideY = true;
- }
-
- void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
- Velocity.Z = 0;
- entityBB.Min.Z = entityExtentBB.Min.Z = Position.Z - size.Z / 2;
- entityBB.Max.Z = entityExtentBB.Max.Z = Position.Z + size.Z / 2;
- collideZ = true;
- }
-
- static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB,
- out float tx, out float ty, out float tz ) {
- float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X;
- float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y;
- float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z;
-
- tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs( dx / vel.X );
- ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y );
- tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z );
-
- if( entityBB.XIntersects( blockBB ) ) tx = 0;
- if( entityBB.YIntersects( blockBB ) ) ty = 0;
- if( entityBB.ZIntersects( blockBB ) ) tz = 0;
- }
- }
+using System;
+using System.Collections.Generic;
+using OpenTK;
+
+namespace ClassicalSharp {
+
+ public abstract class PhysicsEntity : Entity {
+
+ public PhysicsEntity( Game game ) : base( game ) {
+ }
+ protected bool onGround, collideX, collideY, collideZ;
+
+ protected byte GetPhysicsBlockId( int x, int y, int z ) {
+ if( x < 0 || x >= map.Width || z < 0 || z >= map.Length || y < 0 ) return (byte)Block.Bedrock;
+ if( y >= map.Height ) return (byte)Block.Air;
+ return map.GetBlock( x, y, z );
+ }
+
+ bool GetBoundingBox( byte block, int x, int y, int z, out BoundingBox box ) {
+ box = new BoundingBox( Vector3.Zero, Vector3.Zero );
+ if( info.CollideType[block] != BlockCollideType.Solid ) return false;
+ Vector3 min = new Vector3( x, y, z );
+ Vector3 max = new Vector3( x + 1, y + info.Height[block], z + 1 );
+ box = new BoundingBox( min, max );
+ return true;
+ }
+
+ struct State {
+ public BoundingBox BlockBB;
+ public byte Block;
+ public float tSquared;
+
+ public State( BoundingBox bb, byte block, float tSquared ) {
+ BlockBB = bb;
+ Block = block;
+ this.tSquared = tSquared;
+ }
+ }
+
+ // TODO: test for corner cases, and refactor this.
+ static State[] stateCache = new State[0];
+ class StateComparer : IComparer {
+ public int Compare( State x, State y ) {
+ return x.tSquared.CompareTo( y.tSquared );
+ }
+ }
+
+ static StateComparer comparer = new StateComparer();
+ protected void MoveAndWallSlide() {
+ if( Velocity == Vector3.Zero )
+ return;
+ Vector3 size = CollisionSize;
+ BoundingBox entityBB, entityExtentBB;
+ int count = 0;
+ FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB );
+ CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB );
+ }
+
+ void FindReachableBlocks( ref int count, ref Vector3 size,
+ out BoundingBox entityBB, out BoundingBox entityExtentBB ) {
+ Vector3 vel = Velocity;
+ Vector3 pos = Position;
+ entityBB = new BoundingBox(
+ pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2,
+ pos.X + size.X / 2, pos.Y + size.Y, pos.Z + size.Z / 2
+ );
+
+ // Exact maximum extent the entity can reach, and the equivalent map coordinates.
+ entityExtentBB = new BoundingBox(
+ vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X,
+ vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y,
+ vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z,
+ vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X,
+ vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y,
+ vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z
+ );
+ Vector3I min = Vector3I.Floor( entityExtentBB.Min );
+ Vector3I max = Vector3I.Floor( entityExtentBB.Max );
+
+ int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z);
+ if( elements > stateCache.Length ) {
+ stateCache = new State[elements];
+ }
+
+ for( int x = min.X; x <= max.X; x++ ) {
+ for( int y = min.Y; y <= max.Y; y++ ) {
+ for( int z = min.Z; z <= max.Z; z++ ) {
+ byte blockId = GetPhysicsBlockId( x, y, z );
+ BoundingBox blockBB;
+ if( !GetBoundingBox( blockId, x, y, z, out blockBB ) ) continue;
+ if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for some non whole blocks. (slabs)
+
+ float tx = 0, ty = 0, tz = 0;
+ CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz );
+ if( tx > 1 || ty > 1 || tz > 1 ) continue;
+ float tSquared = tx * tx + ty * ty + tz * tz;
+ stateCache[count++] = new State( blockBB, blockId, tSquared );
+ }
+ }
+ }
+ }
+
+ void CollideWithReachableBlocks( int count, ref Vector3 size,
+ ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
+ bool wasOn = onGround;
+ onGround = false;
+ Array.Sort( stateCache, 0, count, comparer );
+ collideX = false; collideY = false; collideZ = false;
+
+ for( int i = 0; i < count; i++ ) {
+ State state = stateCache[i];
+ BoundingBox blockBB = state.BlockBB;
+ if( !entityExtentBB.Intersects( blockBB ) ) continue;
+
+ float tx = 0, ty = 0, tz = 0;
+ CalcTime( ref Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz );
+ if( tx > 1 || ty > 1 || tz > 1 )
+ Utils.LogWarning( "t > 1 in physics calculation.. this shouldn't have happened." );
+ BoundingBox finalBB = entityBB.Offset( Velocity * new Vector3( tx, ty, tz ) );
+
+ if( finalBB.Min.Y >= blockBB.Max.Y ) {
+ Position.Y = blockBB.Max.Y + Adjustment;
+ onGround = true;
+ ClipY( ref size, ref entityBB, ref entityExtentBB );
+ } else if( finalBB.Max.Y <= blockBB.Min.Y ) {
+ Position.Y = blockBB.Min.Y - size.Y - Adjustment;
+ ClipY( ref size, ref entityBB, ref entityExtentBB );
+ } else if( finalBB.Min.X >= blockBB.Max.X ) {
+ if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
+ Position.X = blockBB.Max.X + size.X / 2 + Adjustment;
+ ClipX( ref size, ref entityBB, ref entityExtentBB );
+ }
+ } else if( finalBB.Max.X <= blockBB.Min.X ) {
+ if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
+ Position.X = blockBB.Min.X - size.X / 2 - Adjustment;
+ ClipX( ref size, ref entityBB, ref entityExtentBB );
+ }
+ } else if( finalBB.Min.Z >= blockBB.Max.Z ) {
+ if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
+ Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment;
+ ClipZ( ref size, ref entityBB, ref entityExtentBB );
+ }
+ } else if( finalBB.Max.Z <= blockBB.Min.Z ) {
+ if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
+ Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment;
+ ClipZ( ref size, ref entityBB, ref entityExtentBB );
+ }
+ }
+ }
+ }
+
+ bool DidSlide( ref BoundingBox blockBB, ref Vector3 size, ref BoundingBox finalBB,
+ ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
+ float yDist = blockBB.Max.Y - entityBB.Min.Y;
+ if( yDist > 0 && yDist <= StepSize + 0.01f ) {
+
+ // Adjust entity bounding box to include the block being tested
+ BoundingBox adjFinalBB = finalBB;
+ adjFinalBB.Min.X = Math.Min( finalBB.Min.X, blockBB.Min.X + Adjustment );
+ adjFinalBB.Max.X = Math.Max( finalBB.Max.X, blockBB.Max.X - Adjustment );
+ adjFinalBB.Min.Y = (float)Math.Ceiling( blockBB.Max.Y ) + Adjustment;
+ adjFinalBB.Max.Y = adjFinalBB.Min.Y + size.Y;
+ adjFinalBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment );
+ adjFinalBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment );
+
+ Vector3I min = Vector3I.Floor( adjFinalBB.Min );
+ Vector3I max = Vector3I.Floor( adjFinalBB.Max );
+ for( int x = min.X; x <= max.X; x++ ) {
+ for( int y = min.Y; y <= max.Y; y++ ) {
+ for( int z = min.Z; z <= max.Z; z++ ) {
+ if( info.CollideType[GetPhysicsBlockId( x, y, z )] == BlockCollideType.Solid )
+ return false;
+ }
+ }
+ }
+
+ Position.Y = blockBB.Max.Y + Adjustment;
+ onGround = true;
+ ClipY( ref size, ref entityBB, ref entityExtentBB );
+ return true;
+ }
+ return false;
+ }
+
+ void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
+ Velocity.X = 0;
+ entityBB.Min.X = entityExtentBB.Min.X = Position.X - size.X / 2;
+ entityBB.Max.X = entityExtentBB.Max.X = Position.X + size.X / 2;
+ collideX = true;
+ }
+
+ void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
+ Velocity.Y = 0;
+ entityBB.Min.Y = entityExtentBB.Min.Y = Position.Y;
+ entityBB.Max.Y = entityExtentBB.Max.Y = Position.Y + size.Y;
+ collideY = true;
+ }
+
+ void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
+ Velocity.Z = 0;
+ entityBB.Min.Z = entityExtentBB.Min.Z = Position.Z - size.Z / 2;
+ entityBB.Max.Z = entityExtentBB.Max.Z = Position.Z + size.Z / 2;
+ collideZ = true;
+ }
+
+ static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB,
+ out float tx, out float ty, out float tz ) {
+ float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X;
+ float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y;
+ float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z;
+
+ tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs( dx / vel.X );
+ ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y );
+ tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z );
+
+ if( entityBB.XIntersects( blockBB ) ) tx = 0;
+ if( entityBB.YIntersects( blockBB ) ) ty = 0;
+ if( entityBB.ZIntersects( blockBB ) ) tz = 0;
+ }
+ }
}
\ No newline at end of file
diff --git a/ClassicalSharp/Entities/Player.cs b/ClassicalSharp/Entities/Player.cs
index 0434619c4..2e4d1d800 100644
--- a/ClassicalSharp/Entities/Player.cs
+++ b/ClassicalSharp/Entities/Player.cs
@@ -1,13 +1,11 @@
-using System;
-using System.Drawing;
-using OpenTK;
-using ClassicalSharp.Network;
-using ClassicalSharp.Model;
-using ClassicalSharp.Renderers;
+using System;
+using System.Drawing;
+using ClassicalSharp.Network;
+using OpenTK;
namespace ClassicalSharp {
- public abstract partial class Player : Entity {
+ public abstract partial class Player : PhysicsEntity {
/// Gets the position of the player's eye in the world.
public Vector3 EyePosition {
@@ -36,9 +34,8 @@ namespace ClassicalSharp {
}
Block GetBlock( Vector3 coords ) {
- Vector3I blockCoords = Vector3I.Floor( coords );
- return map.IsValidPos( blockCoords ) ?
- (Block)map.GetBlock( blockCoords ) : Block.Air;
+ Vector3I p = Vector3I.Floor( coords );
+ return (Block)map.SafeGetBlock( p.X, p.Y, p.Z );
}
public abstract void Tick( double delta );
diff --git a/ClassicalSharp/GraphicsAPI/IGraphicsApi.cs b/ClassicalSharp/GraphicsAPI/IGraphicsApi.cs
index e196597c6..be691a6bf 100644
--- a/ClassicalSharp/GraphicsAPI/IGraphicsApi.cs
+++ b/ClassicalSharp/GraphicsAPI/IGraphicsApi.cs
@@ -246,6 +246,7 @@ namespace ClassicalSharp.GraphicsAPI {
LoadIdentityMatrix();
AlphaBlending = true;
if( setFog ) Fog = false;
+ //OpenTK.Graphics.OpenGL.GL.PolygonMode( 0x0408, 0x1B02 );
}
protected virtual void LoadOrthoMatrix( float width, float height ) {
@@ -262,6 +263,7 @@ namespace ClassicalSharp.GraphicsAPI {
DepthTest = true;
AlphaBlending = false;
if( setFog ) Fog = true;
+ //OpenTK.Graphics.OpenGL.GL.PolygonMode( 0x0408, 0x1B01 );
}
internal unsafe int MakeDefaultIb() {
diff --git a/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs b/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs
new file mode 100644
index 000000000..e2b9cf3ca
--- /dev/null
+++ b/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs
@@ -0,0 +1,195 @@
+using System;
+using ClassicalSharp.GraphicsAPI;
+using OpenTK;
+
+namespace ClassicalSharp {
+
+ public partial class MapRenderer : IDisposable {
+
+ void SimpleOcclusionCulling() { // TODO: broken
+ Vector3 p = game.LocalPlayer.EyePosition;
+ Vector3I chunkLoc = Vector3I.Floor( p );
+ Utils.Clamp( ref chunkLoc.X, 0, game.Map.Width - 1 );
+ Utils.Clamp( ref chunkLoc.Y, 0, game.Map.Height - 1 );
+ Utils.Clamp( ref chunkLoc.Z, 0, game.Map.Length- 1 );
+
+ int cx = chunkLoc.X >> 4;
+ int cy = chunkLoc.Y >> 4;
+ int cz = chunkLoc.Z >> 4;
+
+ ChunkInfo chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
+ byte chunkInFlags = chunkIn.OcclusionFlags;
+ chunkIn.OcclusionFlags = 0;
+
+ ChunkQueue queue = new ChunkQueue( chunksX * chunksY * chunksZ );
+ for( int i = 0; i < chunks.Length; i++ ) {
+ chunks[i].Visited = false;
+ chunks[i].Occluded = false;
+ chunks[i].VisibilityFlags = 0;
+ }
+
+ chunkIn.Visited = true;
+ QueueChunk( cx - 1, cy, cz, queue );
+ QueueChunk( cx + 1, cy, cz, queue );
+ QueueChunk( cx, cy - 1, cz, queue );
+ QueueChunk( cx, cy + 1, cz, queue );
+ QueueChunk( cx, cy, cz - 1, queue );
+ QueueChunk( cx, cy, cz + 1, queue );
+
+ ProcessQueue( chunkIn, queue );
+ chunkIn.OcclusionFlags = chunkInFlags;
+ }
+
+ void ProcessQueue( ChunkInfo src, ChunkQueue queue ) {
+ Vector3I p = new Vector3I( src.CentreX, src.CentreY, src.CentreZ );
+ while( queue.Size > 0 ) {
+ ChunkInfo chunk = queue.Dequeue();
+ chunk.VisibilityFlags = chunk.OcclusionFlags;
+ int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8;
+ int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8;
+ int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8;
+ int cx = chunk.CentreX >> 4;
+ int cy = chunk.CentreY >> 4;
+ int cz = chunk.CentreZ >> 4;
+
+ int xOffset, yOffset, zOffset;
+ int dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
+ int dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
+ int dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
+ int distX, distY, distZ;
+
+ // X axis collisions
+ int dxLeft = Math.Abs( x1 - p.X ), dxRight = Math.Abs( x2 - p.X );
+ if( dxLeft < dxRight ) {
+ distX = dxLeft * dxLeft + dy * dy + dz * dz; xOffset = -1;
+ } else {
+ distX = dxRight * dxRight + dy * dy + dz * dz; xOffset = 1;
+ }
+
+ // Z axis collisions
+ int dxFront = Math.Abs( z1 - p.Z ), dxBack = Math.Abs( z2 - p.Z );
+ if( dxFront < dxBack ) {
+ distZ = dx * dx + dy * dy + dxFront * dxFront; zOffset = -1;
+ } else {
+ distZ = dx * dx + dy * dy + dxBack * dxBack; zOffset = 1;
+ }
+
+ // Y axis collisions
+ int dxBottom = Math.Abs( y1 - p.Y ), dxTop = Math.Abs( y2 - p.Y );
+ if( dxBottom < dxTop ) {
+ distY = dx * dx + dxBottom * dxBottom + dz * dz; yOffset = -1;
+ } else {
+ distY = dx * dx + dxTop * dxTop + dz * dz; yOffset = 1;
+ }
+
+ int distMin = Math.Min( distX, Math.Min( distY, distZ ) );
+ bool occlude = true;
+ byte flags = 0;
+ if( distMin == distX )
+ OccludeX( cx, cy, cz, xOffset, ref occlude, ref flags );
+ if( distMin == distZ )
+ OccludeZ( cx, cy, cz, zOffset, ref occlude, ref flags );
+ if( distMin == distY )
+ OccludeY( cx, cy, cz, yOffset, ref occlude, ref flags );
+
+ if( occlude )
+ chunk.Occluded = true;
+ chunk.VisibilityFlags = (byte)( flags | chunk.OcclusionFlags );
+ QueueChunk( cx - 1, cy, cz, queue );
+ QueueChunk( cx + 1, cy, cz, queue );
+ QueueChunk( cx, cy, cz - 1, queue );
+ QueueChunk( cx, cy, cz + 1, queue );
+ QueueChunk( cx, cy - 1, cz, queue );
+ QueueChunk( cx, cy + 1, cz, queue );
+ }
+ Console.WriteLine( "======================" );
+ }
+
+ void OccludeX( int cx, int cy, int cz, int xOffset, ref bool occlude, ref byte flags ) {
+ cx += xOffset;
+ if( cx >= 0 && cx < chunksX ) {
+ ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
+ if( (neighbour.VisibilityFlags & 1) == 0 )
+ occlude = false;
+ else
+ flags |= 1;
+ }
+ }
+
+ void OccludeZ( int cx, int cy, int cz, int zOffset, ref bool occlude, ref byte flags ) {
+ cz += zOffset;
+ if( cz >= 0 && cz < chunksZ ) {
+ ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
+ if( (neighbour.VisibilityFlags & 2) == 0 )
+ occlude = false;
+ else
+ flags |= 2;
+ }
+ }
+
+ void OccludeY( int cx, int cy, int cz, int yOffset, ref bool occlude, ref byte flags ) {
+ cy += yOffset;
+ if( cy >= 0 && cy< chunksY ) {
+ ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
+ if( (neighbour.VisibilityFlags & 4) == 0 )
+ occlude = false;
+ else
+ flags |= 4;
+ }
+ }
+
+ static float DistToRecSquared( Vector3 p, int x1, int y1, int z1, int x2, int y2, int z2 ) {
+ float dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
+ float dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
+ float dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+ void QueueChunk( int cx, int cy, int cz, ChunkQueue queue ) {
+ if( cx >= 0 && cy >= 0 && cz >= 0 && cx < chunksX && cy < chunksY && cz < chunksZ ) {
+ ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
+ if( !info.Visited )
+ queue.Enqueue( info );
+ info.Visited = true;
+ }
+ }
+
+ class ChunkQueue {
+ ChunkInfo[] array;
+ int head, tail;
+ public int Size;
+
+ public ChunkQueue( int capacity ) {
+ array = new ChunkInfo[capacity];
+ }
+
+ public void Clear() {
+ Array.Clear( array, 0, Size );
+ head = 0;
+ tail = 0;
+ Size = 0;
+ }
+
+ public void Enqueue( ChunkInfo item ) {
+ if( Size == array.Length )
+ throw new InvalidOperationException( "Queue limit reached" );
+
+ array[tail] = item;
+ tail = (tail + 1) % array.Length;
+ Size++;
+ }
+
+ public ChunkInfo Dequeue() {
+ if( Size == 0 )
+ throw new InvalidOperationException( "No elements left in queue" );
+
+ ChunkInfo item = array[head];
+ array[head] = null;
+ head = (head + 1) % array.Length;
+ Size--;
+ return item;
+ }
+ }
+ }
+}
+
diff --git a/ClassicalSharp/Rendering/MapRenderer.Rendering.cs b/ClassicalSharp/Rendering/MapRenderer.Rendering.cs
index c2b230dc7..b09995781 100644
--- a/ClassicalSharp/Rendering/MapRenderer.Rendering.cs
+++ b/ClassicalSharp/Rendering/MapRenderer.Rendering.cs
@@ -14,7 +14,7 @@ namespace ClassicalSharp {
void RenderNormalBatch( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[i];
- if( info.NormalParts == null || !info.Visible ) continue;
+ if( info.NormalParts == null || !info.Visible || info.Occluded ) continue;
ChunkPartInfo part = info.NormalParts[batch];
if( part.IndicesCount == 0 ) continue;
@@ -33,7 +33,7 @@ namespace ClassicalSharp {
void RenderTranslucentBatch( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[i];
- if( info.TranslucentParts == null || !info.Visible ) continue;
+ if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
ChunkPartInfo part = info.TranslucentParts[batch];
if( part.IndicesCount == 0 ) continue;
@@ -45,7 +45,7 @@ namespace ClassicalSharp {
void RenderTranslucentBatchDepthPass( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[i];
- if( info.TranslucentParts == null || !info.Visible ) continue;
+ if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
ChunkPartInfo part = info.TranslucentParts[batch];
if( part.IndicesCount == 0 ) continue;
diff --git a/ClassicalSharp/Rendering/MapRenderer.cs b/ClassicalSharp/Rendering/MapRenderer.cs
index a1e634d3a..7a39180f8 100644
--- a/ClassicalSharp/Rendering/MapRenderer.cs
+++ b/ClassicalSharp/Rendering/MapRenderer.cs
@@ -11,8 +11,8 @@ namespace ClassicalSharp {
class ChunkInfo {
public ushort CentreX, CentreY, CentreZ;
- public bool Visible = true;
- public bool Empty = false;
+ public bool Visible = true, Occluded = false;
+ public bool Visited = false, Empty = false;
public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop;
public byte OcclusionFlags, VisibilityFlags;
@@ -216,7 +216,6 @@ namespace ClassicalSharp {
if( chunks == null ) return;
UpdateSortOrder();
UpdateChunks( deltaTime );
- //SimpleOcclusionCulling();
RenderNormal();
game.MapEnvRenderer.Render( deltaTime );
@@ -255,6 +254,7 @@ namespace ClassicalSharp {
info.DrawBottom = !(dY1 <= 0 && dY2 <= 0);
info.DrawTop = !(dY1 >= 0 && dY2 >= 0);
}
+ //SimpleOcclusionCulling();
}
int chunksTarget = 4;
@@ -332,141 +332,5 @@ namespace ClassicalSharp {
api.AlphaBlending = false;
api.Texturing = false;
}
-
- void SimpleOcclusionCulling() { // TODO: broken
- Vector3 p = game.LocalPlayer.EyePosition;
- Vector3I chunkLoc = Vector3I.Floor( p );
- ChunkInfo chunkIn = null;
- byte chunkInFlags = 0;
- // We have to pretend that the chunk the player is in does no occlusion
- // (because for example, only X15 may be filled while X0 is air)
- if( game.Map.IsValidPos( chunkLoc ) ) {
- int cx = chunkLoc.X >> 4;
- int cy = chunkLoc.Y >> 4;
- int cz = chunkLoc.Z >> 4;
- chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
- chunkInFlags = chunkIn.OcclusionFlags;
- chunkIn.OcclusionFlags = 0;
- }
-
- for( int i = 0; i < chunks.Length; i++ ) {
- ChunkInfo chunk = chunks[i];
- chunk.VisibilityFlags = chunk.OcclusionFlags;
- int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8;
- int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8;
- int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8;
- int xOffset = 0, yOffset = 0, zOffset = 0;
- float dx = 0, dy = 0, dz = 0;
- float distX, distY, distZ;
-
- // TODO: two axes with same distance
-
- // X axis collisions
- dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
- dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
-
- float dxLeft = Math.Max( x1 - p.X, Math.Max( 0, p.X - x1 ) );
- float dxRight = Math.Max( x2 - p.X, Math.Max( 0, p.X - x2 ) );
- if( dxLeft < dxRight ) {
- xOffset = -1;
- distX = dxLeft * dxLeft + dy * dy + dz * dz;
- } else {
- xOffset = 1;
- distX = dxRight * dxRight + dy * dy + dz * dz;
- }
-
- // Z axis collisions
- dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
- dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
-
- float dxFront = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z1 ) );
- float dxBack = Math.Max( z2 - p.Z, Math.Max( 0, p.Z - z2 ) );
- if( dxFront < dxBack ) {
- zOffset = -1;
- distZ = dx * dx + dy * dy + dxFront * dxFront;
- } else {
- zOffset = 1;
- distZ = dx * dx + dy * dy + dxBack * dxBack;
- }
-
- // Y axis collisions
- dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
- dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
-
- float dxBottom = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y1 ) );
- float dxTop = Math.Max( y2 - p.Y, Math.Max( 0, p.Y - y2 ) );
- if( dxBottom < dxTop ) {
- yOffset = -1;
- distY = dx * dx + dxBottom * dxBottom + dz * dz;
- } else {
- yOffset = 1;
- distY = dx * dx + dxTop * dxTop + dz * dz;
- }
-
- int cx = chunk.CentreX >> 4;
- int cy = chunk.CentreY >> 4;
- int cz = chunk.CentreZ >> 4;
- float distMin = Math.Min( distX, Math.Min( distY, distZ ) );
-
- bool occlude = true;
- byte flags = 0;
- if( Math.Abs( distMin - distX ) < 0.00001f )
- OccludeX( cx, cy, cz, xOffset, ref occlude, ref flags );
- if( Math.Abs( distMin - distZ ) < 0.00001f )
- OccludeZ( cx, cy, cz, zOffset, ref occlude, ref flags );
- if( Math.Abs( distMin - distY ) < 0.00001f )
- OccludeY( cx, cy, cz, yOffset, ref occlude, ref flags );
-
- if( occlude ) {
- chunk.Visible = false;
- chunk.VisibilityFlags = flags;
- }
- }
-
- if( chunkIn != null ) {
- chunkIn.Visible = true;
- chunkIn.OcclusionFlags = chunkInFlags;
- }
- }
-
- void OccludeX( int cx, int cy, int cz, int xOffset, ref bool occlude, ref byte flags ) {
- cx += xOffset;
- if( cx >= 0 && cx < chunksX ) {
- ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
- if( (neighbour.VisibilityFlags & 0x1) == 0 )
- occlude = false;
- else
- flags |= 0x1;
- }
- }
-
- void OccludeZ( int cx, int cy, int cz, int zOffset, ref bool occlude, ref byte flags ) {
- cz += zOffset;
- if( cz >= 0 && cz < chunksZ ) {
- ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
- if( (neighbour.VisibilityFlags & 0x2) == 0 )
- occlude = false;
- else
- flags |= 0x2;
- }
- }
-
- void OccludeY( int cx, int cy, int cz, int yOffset, ref bool occlude, ref byte flags ) {
- cy += yOffset;
- if( cy >= 0 && cy< chunksY ) {
- ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
- if( (neighbour.VisibilityFlags & 0x4) == 0 )
- occlude = false;
- else
- flags |= 0x4;
- }
- }
-
- static float DistToRecSquared( Vector3 p, int x1, int y1, int z1, int x2, int y2, int z2 ) {
- float dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
- float dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
- float dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
- return dx * dx + dy * dy + dz * dz;
- }
}
}
\ No newline at end of file
diff --git a/ClassicalSharp/TexturePack/TerrainAtlas1D.cs b/ClassicalSharp/TexturePack/TerrainAtlas1D.cs
index 69f48f6f9..4dc373e2e 100644
--- a/ClassicalSharp/TexturePack/TerrainAtlas1D.cs
+++ b/ClassicalSharp/TexturePack/TerrainAtlas1D.cs
@@ -7,7 +7,7 @@ namespace ClassicalSharp {
/// Represents a 2D packed texture atlas that has been converted into an array of 1D atlases.
public sealed class TerrainAtlas1D : IDisposable {
- int usedElementsPerAtlas1D;
+ int elementsPerAtlas1D;
internal int elementsPerBitmap;
public float invElementSize;
public int[] TexIds;
@@ -18,38 +18,39 @@ namespace ClassicalSharp {
}
public TextureRec GetTexRec( int texId, int uCount, out int index ) {
- index = texId / usedElementsPerAtlas1D;
- int y = texId % usedElementsPerAtlas1D;
+ index = texId / elementsPerAtlas1D;
+ int y = texId % elementsPerAtlas1D;
return new TextureRec( 0, y * invElementSize, uCount, invElementSize );
}
public int Get1DIndex( int texId ) {
- return texId / usedElementsPerAtlas1D;
+ return texId / elementsPerAtlas1D;
}
public int Get1DRowId( int texId ) {
- return texId % usedElementsPerAtlas1D;
+ return texId % elementsPerAtlas1D;
}
public void UpdateState( TerrainAtlas2D atlas2D ) {
int maxVerSize = Math.Min( 4096, graphics.MaxTextureDimensions );
- int verElements = maxVerSize / atlas2D.elementSize;
+ int elemPerFullAtlas = maxVerSize / atlas2D.elementSize;
int totalElements = TerrainAtlas2D.RowsCount * TerrainAtlas2D.ElementsPerRow;
int elemSize = atlas2D.elementSize;
- int atlasesCount = Utils.CeilDiv( totalElements, verElements );
- usedElementsPerAtlas1D = Math.Min( verElements, totalElements );
- int atlas1DHeight = Utils.NextPowerOf2( usedElementsPerAtlas1D * atlas2D.elementSize );
+ int atlasesCount = Utils.CeilDiv( totalElements, elemPerFullAtlas );
+ elementsPerAtlas1D = Math.Min( elemPerFullAtlas, totalElements );
+ int atlas1DHeight = Utils.NextPowerOf2( elementsPerAtlas1D * elemSize );
int index = 0;
TexIds = new int[atlasesCount];
- Utils.LogDebug( "Loaded new atlas: {0} bmps, {1} per bmp", atlasesCount, usedElementsPerAtlas1D );
+ Utils.LogDebug( "Loaded new atlas: {0} bmps, {1} per bmp", atlasesCount, elementsPerAtlas1D );
using( FastBitmap atlas = new FastBitmap( atlas2D.AtlasBitmap, true ) ) {
for( int i = 0; i < TexIds.Length; i++ ) {
Bitmap atlas1d = new Bitmap( atlas2D.elementSize, atlas1DHeight );
using( FastBitmap dst = new FastBitmap( atlas1d, true ) ) {
- for( int y_1D = 0; y_1D < usedElementsPerAtlas1D; y_1D++ ) {
+ for( int y_1D = 0; y_1D < elementsPerAtlas1D; y_1D++ ) {
+ if( index >= 256 ) break;
int x = index & 0x0F;
int y = index >> 4;
FastBitmap.MovePortion( x * elemSize, y * elemSize, 0, y_1D * elemSize, atlas, dst, elemSize );
diff --git a/ClassicalSharp/Utils/FastBitmap.cs b/ClassicalSharp/Utils/FastBitmap.cs
index 96da49e1c..73c77ee10 100644
--- a/ClassicalSharp/Utils/FastBitmap.cs
+++ b/ClassicalSharp/Utils/FastBitmap.cs
@@ -71,7 +71,7 @@ namespace ClassicalSharp {
/// Returns a pointer to the start of the y'th scanline.
public int* GetRowPtr( int y ) {
- return (int*)( scan0Byte + ( y * Stride ) );
+ return (int*)(scan0Byte + (y * Stride));
}
public static void MovePortion( int srcX, int srcY, int dstX, int dstY, FastBitmap src, FastBitmap dst, int size ) {