Refactor physics some more, also fix bug with terrain atlas sometimes causing crash. More work on occlusion, still broken.

This commit is contained in:
UnknownShadow200 2015-10-10 10:17:13 +11:00
parent 2674cabfaa
commit f99a01cba9
11 changed files with 453 additions and 389 deletions

View File

@ -19,7 +19,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> <PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<BaseAddress>4194304</BaseAddress> <BaseAddress>4194304</BaseAddress>
<RegisterForComInterop>False</RegisterForComInterop> <RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
@ -108,6 +108,7 @@
<Compile Include="Entities\Particles\Particle.cs" /> <Compile Include="Entities\Particles\Particle.cs" />
<Compile Include="Entities\Particles\ParticleManager.cs" /> <Compile Include="Entities\Particles\ParticleManager.cs" />
<Compile Include="Entities\Particles\TerrainParticle.cs" /> <Compile Include="Entities\Particles\TerrainParticle.cs" />
<Compile Include="Entities\PhysicsEntity.cs" />
<Compile Include="Entities\Player.cs" /> <Compile Include="Entities\Player.cs" />
<Compile Include="Entities\Player.Rendering.cs" /> <Compile Include="Entities\Player.Rendering.cs" />
<Compile Include="Game\ChatLog.cs" /> <Compile Include="Game\ChatLog.cs" />
@ -159,12 +160,12 @@
<Compile Include="Commands\CommandManager.cs" /> <Compile Include="Commands\CommandManager.cs" />
<Compile Include="Commands\CommandReader.cs" /> <Compile Include="Commands\CommandReader.cs" />
<Compile Include="Physics\BoundingBox.cs" /> <Compile Include="Physics\BoundingBox.cs" />
<Compile Include="Physics\Entity.Physics.cs" />
<Compile Include="Physics\IntersectionUtils.cs" /> <Compile Include="Physics\IntersectionUtils.cs" />
<Compile Include="Physics\Picking.cs" /> <Compile Include="Physics\Picking.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rendering\EnvRenderer.cs" /> <Compile Include="Rendering\EnvRenderer.cs" />
<Compile Include="Rendering\MapRenderer.Occlusion.cs" />
<Compile Include="Rendering\MapRenderer.Rendering.cs" /> <Compile Include="Rendering\MapRenderer.Rendering.cs" />
<Compile Include="Rendering\MinimalEnvRenderer.cs" /> <Compile Include="Rendering\MinimalEnvRenderer.cs" />
<Compile Include="Rendering\FrustumCulling.cs" /> <Compile Include="Rendering\FrustumCulling.cs" />

View File

@ -4,7 +4,7 @@ using OpenTK;
namespace ClassicalSharp { namespace ClassicalSharp {
public abstract partial class Entity { public abstract class Entity {
public Entity( Game game ) { public Entity( Game game ) {
map = game.Map; map = game.Map;
@ -19,6 +19,8 @@ namespace ClassicalSharp {
public Vector3 Velocity; public Vector3 Velocity;
public float YawDegrees, PitchDegrees; public float YawDegrees, PitchDegrees;
protected float StepSize; protected float StepSize;
protected Map map;
protected BlockInfo info;
public float YawRadians { public float YawRadians {
get { return YawDegrees * Utils.Deg2Rad; } get { return YawDegrees * Utils.Deg2Rad; }
@ -87,5 +89,7 @@ namespace ClassicalSharp {
} }
return false; return false;
} }
public const float Adjustment = 0.001f;
} }
} }

View File

@ -143,7 +143,7 @@ namespace ClassicalSharp {
const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f; const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f;
void PhysicsTick( float xMoving, float zMoving ) { 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(); float modifier = LowestSpeedModifier();
multiply *= modifier; multiply *= modifier;
@ -155,7 +155,7 @@ namespace ClassicalSharp {
Move( xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, 1 ); Move( xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, 1 );
} else { } else {
float factor = !flying && onGround ? 0.1f : 0.02f; 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; float gravity = useLiquidGravity ? liquidGrav : normalGrav;
Move( xMoving, zMoving, factor * multiply, normalDrag, gravity, yMul ); Move( xMoving, zMoving, factor * multiply, normalDrag, gravity, yMul );

View File

@ -1,221 +1,221 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using OpenTK; using OpenTK;
namespace ClassicalSharp { namespace ClassicalSharp {
public partial class Entity { public abstract class PhysicsEntity : Entity {
protected bool onGround, collideX, collideY, collideZ; public PhysicsEntity( Game game ) : base( game ) {
protected Map map; }
protected BlockInfo info; protected bool onGround, collideX, collideY, collideZ;
public const float Adjustment = 0.001f;
protected byte GetPhysicsBlockId( int x, int y, int z ) {
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( x < 0 || x >= map.Width || z < 0 || z >= map.Length || y < 0 ) return (byte)Block.Bedrock; if( y >= map.Height ) return (byte)Block.Air;
if( y >= map.Height ) return (byte)Block.Air; return map.GetBlock( x, y, z );
return map.GetBlock( x, y, z ); }
}
bool GetBoundingBox( byte block, int x, int y, int z, out BoundingBox box ) {
bool GetBoundingBox( byte block, int x, int y, int z, out BoundingBox box ) { box = new BoundingBox( Vector3.Zero, Vector3.Zero );
box = new BoundingBox( Vector3.Zero, Vector3.Zero ); if( info.CollideType[block] != BlockCollideType.Solid ) return false;
if( info.CollideType[block] != BlockCollideType.Solid ) return false; Vector3 min = new Vector3( x, y, z );
Vector3 min = new Vector3( x, y, z ); Vector3 max = new Vector3( x + 1, y + info.Height[block], z + 1 );
Vector3 max = new Vector3( x + 1, y + info.Height[block], z + 1 ); box = new BoundingBox( min, max );
box = new BoundingBox( min, max ); return true;
return true; }
}
struct State {
struct State { public BoundingBox BlockBB;
public BoundingBox BlockBB; public byte Block;
public byte Block; public float tSquared;
public float tSquared;
public State( BoundingBox bb, byte block, float tSquared ) {
public State( BoundingBox bb, byte block, float tSquared ) { BlockBB = bb;
BlockBB = bb; Block = block;
Block = block; this.tSquared = tSquared;
this.tSquared = tSquared; }
} }
}
// TODO: test for corner cases, and refactor this.
// TODO: test for corner cases, and refactor this. static State[] stateCache = new State[0];
static State[] stateCache = new State[0]; class StateComparer : IComparer<State> {
class StateComparer : IComparer<State> { public int Compare( State x, State y ) {
public int Compare( State x, State y ) { return x.tSquared.CompareTo( y.tSquared );
return x.tSquared.CompareTo( y.tSquared ); }
} }
}
static StateComparer comparer = new StateComparer(); static StateComparer comparer = new StateComparer();
protected void MoveAndWallSlide() { protected void MoveAndWallSlide() {
if( Velocity == Vector3.Zero ) if( Velocity == Vector3.Zero )
return; return;
Vector3 size = CollisionSize; Vector3 size = CollisionSize;
BoundingBox entityBB, entityExtentBB; BoundingBox entityBB, entityExtentBB;
int count = 0; int count = 0;
FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB ); FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB );
CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB ); CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB );
} }
void FindReachableBlocks( ref int count, ref Vector3 size, void FindReachableBlocks( ref int count, ref Vector3 size,
out BoundingBox entityBB, out BoundingBox entityExtentBB ) { out BoundingBox entityBB, out BoundingBox entityExtentBB ) {
Vector3 vel = Velocity; Vector3 vel = Velocity;
Vector3 pos = Position; Vector3 pos = Position;
entityBB = new BoundingBox( entityBB = new BoundingBox(
pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2, 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 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. // Exact maximum extent the entity can reach, and the equivalent map coordinates.
entityExtentBB = new BoundingBox( entityExtentBB = new BoundingBox(
vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X,
vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y,
vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z,
vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X,
vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y,
vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z
); );
Vector3I min = Vector3I.Floor( entityExtentBB.Min ); Vector3I min = Vector3I.Floor( entityExtentBB.Min );
Vector3I max = Vector3I.Floor( entityExtentBB.Max ); Vector3I max = Vector3I.Floor( entityExtentBB.Max );
int elements = ( max.X + 1 - min.X ) * ( max.Y + 1 - min.Y ) * ( max.Z + 1 - min.Z ); int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z);
if( elements > stateCache.Length ) { if( elements > stateCache.Length ) {
stateCache = new State[elements]; stateCache = new State[elements];
} }
for( int x = min.X; x <= max.X; x++ ) { for( int x = min.X; x <= max.X; x++ ) {
for( int y = min.Y; y <= max.Y; y++ ) { for( int y = min.Y; y <= max.Y; y++ ) {
for( int z = min.Z; z <= max.Z; z++ ) { for( int z = min.Z; z <= max.Z; z++ ) {
byte blockId = GetPhysicsBlockId( x, y, z ); byte blockId = GetPhysicsBlockId( x, y, z );
BoundingBox blockBB; BoundingBox blockBB;
if( !GetBoundingBox( blockId, x, y, z, out blockBB ) ) continue; if( !GetBoundingBox( blockId, x, y, z, out blockBB ) ) continue;
if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for some non whole blocks. (slabs) if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for some non whole blocks. (slabs)
float tx = 0, ty = 0, tz = 0; float tx = 0, ty = 0, tz = 0;
CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz ); CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz );
if( tx > 1 || ty > 1 || tz > 1 ) continue; if( tx > 1 || ty > 1 || tz > 1 ) continue;
float tSquared = tx * tx + ty * ty + tz * tz; float tSquared = tx * tx + ty * ty + tz * tz;
stateCache[count++] = new State( blockBB, blockId, tSquared ); stateCache[count++] = new State( blockBB, blockId, tSquared );
} }
} }
} }
} }
void CollideWithReachableBlocks( int count, ref Vector3 size, void CollideWithReachableBlocks( int count, ref Vector3 size,
ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
bool wasOn = onGround; bool wasOn = onGround;
onGround = false; onGround = false;
Array.Sort( stateCache, 0, count, comparer ); Array.Sort( stateCache, 0, count, comparer );
collideX = false; collideY = false; collideZ = false; collideX = false; collideY = false; collideZ = false;
for( int i = 0; i < count; i++ ) { for( int i = 0; i < count; i++ ) {
State state = stateCache[i]; State state = stateCache[i];
BoundingBox blockBB = state.BlockBB; BoundingBox blockBB = state.BlockBB;
if( !entityExtentBB.Intersects( blockBB ) ) continue; if( !entityExtentBB.Intersects( blockBB ) ) continue;
float tx = 0, ty = 0, tz = 0; float tx = 0, ty = 0, tz = 0;
CalcTime( ref Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz ); CalcTime( ref Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz );
if( tx > 1 || ty > 1 || tz > 1 ) if( tx > 1 || ty > 1 || tz > 1 )
Utils.LogWarning( "t > 1 in physics calculation.. this shouldn't have happened." ); Utils.LogWarning( "t > 1 in physics calculation.. this shouldn't have happened." );
BoundingBox finalBB = entityBB.Offset( Velocity * new Vector3( tx, ty, tz ) ); BoundingBox finalBB = entityBB.Offset( Velocity * new Vector3( tx, ty, tz ) );
if( finalBB.Min.Y >= blockBB.Max.Y ) { if( finalBB.Min.Y >= blockBB.Max.Y ) {
Position.Y = blockBB.Max.Y + Adjustment; Position.Y = blockBB.Max.Y + Adjustment;
onGround = true; onGround = true;
ClipY( ref size, ref entityBB, ref entityExtentBB ); ClipY( ref size, ref entityBB, ref entityExtentBB );
} else if( finalBB.Max.Y <= blockBB.Min.Y ) { } else if( finalBB.Max.Y <= blockBB.Min.Y ) {
Position.Y = blockBB.Min.Y - size.Y - Adjustment; Position.Y = blockBB.Min.Y - size.Y - Adjustment;
ClipY( ref size, ref entityBB, ref entityExtentBB ); ClipY( ref size, ref entityBB, ref entityExtentBB );
} else if( finalBB.Min.X >= blockBB.Max.X ) { } else if( finalBB.Min.X >= blockBB.Max.X ) {
if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) { if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
Position.X = blockBB.Max.X + size.X / 2 + Adjustment; Position.X = blockBB.Max.X + size.X / 2 + Adjustment;
ClipX( ref size, ref entityBB, ref entityExtentBB ); ClipX( ref size, ref entityBB, ref entityExtentBB );
} }
} else if( finalBB.Max.X <= blockBB.Min.X ) { } else if( finalBB.Max.X <= blockBB.Min.X ) {
if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) { if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
Position.X = blockBB.Min.X - size.X / 2 - Adjustment; Position.X = blockBB.Min.X - size.X / 2 - Adjustment;
ClipX( ref size, ref entityBB, ref entityExtentBB ); ClipX( ref size, ref entityBB, ref entityExtentBB );
} }
} else if( finalBB.Min.Z >= blockBB.Max.Z ) { } else if( finalBB.Min.Z >= blockBB.Max.Z ) {
if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) { if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment; Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment;
ClipZ( ref size, ref entityBB, ref entityExtentBB ); ClipZ( ref size, ref entityBB, ref entityExtentBB );
} }
} else if( finalBB.Max.Z <= blockBB.Min.Z ) { } else if( finalBB.Max.Z <= blockBB.Min.Z ) {
if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) { if( !wasOn || !DidSlide( ref blockBB, ref size, ref finalBB, ref entityBB, ref entityExtentBB ) ) {
Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment; Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment;
ClipZ( ref size, ref entityBB, ref entityExtentBB ); ClipZ( ref size, ref entityBB, ref entityExtentBB );
} }
} }
} }
} }
bool DidSlide( ref BoundingBox blockBB, ref Vector3 size, ref BoundingBox finalBB, bool DidSlide( ref BoundingBox blockBB, ref Vector3 size, ref BoundingBox finalBB,
ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
float yDist = blockBB.Max.Y - entityBB.Min.Y; float yDist = blockBB.Max.Y - entityBB.Min.Y;
if( yDist > 0 && yDist <= StepSize + 0.01f ) { if( yDist > 0 && yDist <= StepSize + 0.01f ) {
// Adjust entity bounding box to include the block being tested // Adjust entity bounding box to include the block being tested
BoundingBox adjFinalBB = finalBB; BoundingBox adjFinalBB = finalBB;
adjFinalBB.Min.X = Math.Min( finalBB.Min.X, blockBB.Min.X + Adjustment ); 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.Max.X = Math.Max( finalBB.Max.X, blockBB.Max.X - Adjustment );
adjFinalBB.Min.Y = (float)Math.Ceiling( blockBB.Max.Y ) + Adjustment; adjFinalBB.Min.Y = (float)Math.Ceiling( blockBB.Max.Y ) + Adjustment;
adjFinalBB.Max.Y = adjFinalBB.Min.Y + size.Y; adjFinalBB.Max.Y = adjFinalBB.Min.Y + size.Y;
adjFinalBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment ); adjFinalBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment );
adjFinalBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment ); adjFinalBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment );
Vector3I min = Vector3I.Floor( adjFinalBB.Min ); Vector3I min = Vector3I.Floor( adjFinalBB.Min );
Vector3I max = Vector3I.Floor( adjFinalBB.Max ); Vector3I max = Vector3I.Floor( adjFinalBB.Max );
for( int x = min.X; x <= max.X; x++ ) { for( int x = min.X; x <= max.X; x++ ) {
for( int y = min.Y; y <= max.Y; y++ ) { for( int y = min.Y; y <= max.Y; y++ ) {
for( int z = min.Z; z <= max.Z; z++ ) { for( int z = min.Z; z <= max.Z; z++ ) {
if( info.CollideType[GetPhysicsBlockId( x, y, z )] == BlockCollideType.Solid ) if( info.CollideType[GetPhysicsBlockId( x, y, z )] == BlockCollideType.Solid )
return false; return false;
} }
} }
} }
Position.Y = blockBB.Max.Y + Adjustment; Position.Y = blockBB.Max.Y + Adjustment;
onGround = true; onGround = true;
ClipY( ref size, ref entityBB, ref entityExtentBB ); ClipY( ref size, ref entityBB, ref entityExtentBB );
return true; return true;
} }
return false; return false;
} }
void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
Velocity.X = 0; Velocity.X = 0;
entityBB.Min.X = entityExtentBB.Min.X = Position.X - size.X / 2; entityBB.Min.X = entityExtentBB.Min.X = Position.X - size.X / 2;
entityBB.Max.X = entityExtentBB.Max.X = Position.X + size.X / 2; entityBB.Max.X = entityExtentBB.Max.X = Position.X + size.X / 2;
collideX = true; collideX = true;
} }
void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
Velocity.Y = 0; Velocity.Y = 0;
entityBB.Min.Y = entityExtentBB.Min.Y = Position.Y; entityBB.Min.Y = entityExtentBB.Min.Y = Position.Y;
entityBB.Max.Y = entityExtentBB.Max.Y = Position.Y + size.Y; entityBB.Max.Y = entityExtentBB.Max.Y = Position.Y + size.Y;
collideY = true; collideY = true;
} }
void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
Velocity.Z = 0; Velocity.Z = 0;
entityBB.Min.Z = entityExtentBB.Min.Z = Position.Z - size.Z / 2; entityBB.Min.Z = entityExtentBB.Min.Z = Position.Z - size.Z / 2;
entityBB.Max.Z = entityExtentBB.Max.Z = Position.Z + size.Z / 2; entityBB.Max.Z = entityExtentBB.Max.Z = Position.Z + size.Z / 2;
collideZ = true; collideZ = true;
} }
static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB, static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB,
out float tx, out float ty, out float tz ) { 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 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 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; 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 ); tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs( dx / vel.X );
ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y ); ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y );
tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z ); tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z );
if( entityBB.XIntersects( blockBB ) ) tx = 0; if( entityBB.XIntersects( blockBB ) ) tx = 0;
if( entityBB.YIntersects( blockBB ) ) ty = 0; if( entityBB.YIntersects( blockBB ) ) ty = 0;
if( entityBB.ZIntersects( blockBB ) ) tz = 0; if( entityBB.ZIntersects( blockBB ) ) tz = 0;
} }
} }
} }

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Drawing; using System.Drawing;
using OpenTK; using ClassicalSharp.Network;
using ClassicalSharp.Network; using OpenTK;
using ClassicalSharp.Model;
using ClassicalSharp.Renderers;
namespace ClassicalSharp { namespace ClassicalSharp {
public abstract partial class Player : Entity { public abstract partial class Player : PhysicsEntity {
/// <summary> Gets the position of the player's eye in the world. </summary> /// <summary> Gets the position of the player's eye in the world. </summary>
public Vector3 EyePosition { public Vector3 EyePosition {
@ -36,9 +34,8 @@ namespace ClassicalSharp {
} }
Block GetBlock( Vector3 coords ) { Block GetBlock( Vector3 coords ) {
Vector3I blockCoords = Vector3I.Floor( coords ); Vector3I p = Vector3I.Floor( coords );
return map.IsValidPos( blockCoords ) ? return (Block)map.SafeGetBlock( p.X, p.Y, p.Z );
(Block)map.GetBlock( blockCoords ) : Block.Air;
} }
public abstract void Tick( double delta ); public abstract void Tick( double delta );

View File

@ -246,6 +246,7 @@ namespace ClassicalSharp.GraphicsAPI {
LoadIdentityMatrix(); LoadIdentityMatrix();
AlphaBlending = true; AlphaBlending = true;
if( setFog ) Fog = false; if( setFog ) Fog = false;
//OpenTK.Graphics.OpenGL.GL.PolygonMode( 0x0408, 0x1B02 );
} }
protected virtual void LoadOrthoMatrix( float width, float height ) { protected virtual void LoadOrthoMatrix( float width, float height ) {
@ -262,6 +263,7 @@ namespace ClassicalSharp.GraphicsAPI {
DepthTest = true; DepthTest = true;
AlphaBlending = false; AlphaBlending = false;
if( setFog ) Fog = true; if( setFog ) Fog = true;
//OpenTK.Graphics.OpenGL.GL.PolygonMode( 0x0408, 0x1B01 );
} }
internal unsafe int MakeDefaultIb() { internal unsafe int MakeDefaultIb() {

View File

@ -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;
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace ClassicalSharp {
void RenderNormalBatch( int batch ) { void RenderNormalBatch( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) { for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[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]; ChunkPartInfo part = info.NormalParts[batch];
if( part.IndicesCount == 0 ) continue; if( part.IndicesCount == 0 ) continue;
@ -33,7 +33,7 @@ namespace ClassicalSharp {
void RenderTranslucentBatch( int batch ) { void RenderTranslucentBatch( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) { for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[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]; ChunkPartInfo part = info.TranslucentParts[batch];
if( part.IndicesCount == 0 ) continue; if( part.IndicesCount == 0 ) continue;
@ -45,7 +45,7 @@ namespace ClassicalSharp {
void RenderTranslucentBatchDepthPass( int batch ) { void RenderTranslucentBatchDepthPass( int batch ) {
for( int i = 0; i < chunks.Length; i++ ) { for( int i = 0; i < chunks.Length; i++ ) {
ChunkInfo info = chunks[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]; ChunkPartInfo part = info.TranslucentParts[batch];
if( part.IndicesCount == 0 ) continue; if( part.IndicesCount == 0 ) continue;

View File

@ -11,8 +11,8 @@ namespace ClassicalSharp {
class ChunkInfo { class ChunkInfo {
public ushort CentreX, CentreY, CentreZ; public ushort CentreX, CentreY, CentreZ;
public bool Visible = true; public bool Visible = true, Occluded = false;
public bool Empty = false; public bool Visited = false, Empty = false;
public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop; public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop;
public byte OcclusionFlags, VisibilityFlags; public byte OcclusionFlags, VisibilityFlags;
@ -216,7 +216,6 @@ namespace ClassicalSharp {
if( chunks == null ) return; if( chunks == null ) return;
UpdateSortOrder(); UpdateSortOrder();
UpdateChunks( deltaTime ); UpdateChunks( deltaTime );
//SimpleOcclusionCulling();
RenderNormal(); RenderNormal();
game.MapEnvRenderer.Render( deltaTime ); game.MapEnvRenderer.Render( deltaTime );
@ -255,6 +254,7 @@ namespace ClassicalSharp {
info.DrawBottom = !(dY1 <= 0 && dY2 <= 0); info.DrawBottom = !(dY1 <= 0 && dY2 <= 0);
info.DrawTop = !(dY1 >= 0 && dY2 >= 0); info.DrawTop = !(dY1 >= 0 && dY2 >= 0);
} }
//SimpleOcclusionCulling();
} }
int chunksTarget = 4; int chunksTarget = 4;
@ -332,141 +332,5 @@ namespace ClassicalSharp {
api.AlphaBlending = false; api.AlphaBlending = false;
api.Texturing = 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;
}
} }
} }

View File

@ -7,7 +7,7 @@ namespace ClassicalSharp {
/// <summary> Represents a 2D packed texture atlas that has been converted into an array of 1D atlases. </summary> /// <summary> Represents a 2D packed texture atlas that has been converted into an array of 1D atlases. </summary>
public sealed class TerrainAtlas1D : IDisposable { public sealed class TerrainAtlas1D : IDisposable {
int usedElementsPerAtlas1D; int elementsPerAtlas1D;
internal int elementsPerBitmap; internal int elementsPerBitmap;
public float invElementSize; public float invElementSize;
public int[] TexIds; public int[] TexIds;
@ -18,38 +18,39 @@ namespace ClassicalSharp {
} }
public TextureRec GetTexRec( int texId, int uCount, out int index ) { public TextureRec GetTexRec( int texId, int uCount, out int index ) {
index = texId / usedElementsPerAtlas1D; index = texId / elementsPerAtlas1D;
int y = texId % usedElementsPerAtlas1D; int y = texId % elementsPerAtlas1D;
return new TextureRec( 0, y * invElementSize, uCount, invElementSize ); return new TextureRec( 0, y * invElementSize, uCount, invElementSize );
} }
public int Get1DIndex( int texId ) { public int Get1DIndex( int texId ) {
return texId / usedElementsPerAtlas1D; return texId / elementsPerAtlas1D;
} }
public int Get1DRowId( int texId ) { public int Get1DRowId( int texId ) {
return texId % usedElementsPerAtlas1D; return texId % elementsPerAtlas1D;
} }
public void UpdateState( TerrainAtlas2D atlas2D ) { public void UpdateState( TerrainAtlas2D atlas2D ) {
int maxVerSize = Math.Min( 4096, graphics.MaxTextureDimensions ); int maxVerSize = Math.Min( 4096, graphics.MaxTextureDimensions );
int verElements = maxVerSize / atlas2D.elementSize; int elemPerFullAtlas = maxVerSize / atlas2D.elementSize;
int totalElements = TerrainAtlas2D.RowsCount * TerrainAtlas2D.ElementsPerRow; int totalElements = TerrainAtlas2D.RowsCount * TerrainAtlas2D.ElementsPerRow;
int elemSize = atlas2D.elementSize; int elemSize = atlas2D.elementSize;
int atlasesCount = Utils.CeilDiv( totalElements, verElements ); int atlasesCount = Utils.CeilDiv( totalElements, elemPerFullAtlas );
usedElementsPerAtlas1D = Math.Min( verElements, totalElements ); elementsPerAtlas1D = Math.Min( elemPerFullAtlas, totalElements );
int atlas1DHeight = Utils.NextPowerOf2( usedElementsPerAtlas1D * atlas2D.elementSize ); int atlas1DHeight = Utils.NextPowerOf2( elementsPerAtlas1D * elemSize );
int index = 0; int index = 0;
TexIds = new int[atlasesCount]; 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 ) ) { using( FastBitmap atlas = new FastBitmap( atlas2D.AtlasBitmap, true ) ) {
for( int i = 0; i < TexIds.Length; i++ ) { for( int i = 0; i < TexIds.Length; i++ ) {
Bitmap atlas1d = new Bitmap( atlas2D.elementSize, atlas1DHeight ); Bitmap atlas1d = new Bitmap( atlas2D.elementSize, atlas1DHeight );
using( FastBitmap dst = new FastBitmap( atlas1d, true ) ) { 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 x = index & 0x0F;
int y = index >> 4; int y = index >> 4;
FastBitmap.MovePortion( x * elemSize, y * elemSize, 0, y_1D * elemSize, atlas, dst, elemSize ); FastBitmap.MovePortion( x * elemSize, y * elemSize, 0, y_1D * elemSize, atlas, dst, elemSize );

View File

@ -71,7 +71,7 @@ namespace ClassicalSharp {
/// <summary> Returns a pointer to the start of the y'th scanline. </summary> /// <summary> Returns a pointer to the start of the y'th scanline. </summary>
public int* GetRowPtr( int y ) { 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 ) { public static void MovePortion( int srcX, int srcY, int dstX, int dstY, FastBitmap src, FastBitmap dst, int size ) {