207 lines
8.0 KiB
C#

using System;
using ClassicalSharp.GraphicsAPI;
using ClassicalSharp.Renderers;
using OpenTK;
namespace ClassicalSharp.Model {
public class BlockModel : IModel {
byte block = (byte)Block.Air;
float blockHeight;
TerrainAtlas1D atlas;
const float extent = 0.5f, adjust = 0.5f/16f;
bool bright;
Vector3 minBB, maxBB;
public BlockModel( Game game ) : base( game ) {
}
public override bool Bobbing {
get { return false; }
}
public override float NameYOffset {
get { return blockHeight + 0.075f; }
}
public override float GetEyeY( Player player ) {
byte block = Byte.Parse( player.ModelName );
return block == 0 ? 1 : game.BlockInfo.Height[block];
}
public override Vector3 CollisionSize {
get { return (maxBB - minBB) - new Vector3( adjust ); }
}
public override BoundingBox PickingBounds {
get { return new BoundingBox( -extent, 0f, -extent, extent, blockHeight, extent ); }
}
public void CalcState( byte block ) {
if( block == 0 ) {
blockHeight = 1;
bright = false;
minBB = Vector3.Zero;
maxBB = Vector3.One;
} else {
blockHeight = game.BlockInfo.Height[block];
bright = game.BlockInfo.FullBright[block];
minBB = game.BlockInfo.MinBB[block];
maxBB = game.BlockInfo.MaxBB[block];
}
}
int lastTexId = -1;
protected override void DrawPlayerModel( Player p ) {
// TODO: using 'is' is ugly, but means we can avoid creating
// a string every single time held block changes.
if( p is FakePlayer ) {
Vector3I eyePos = Vector3I.Floor( game.LocalPlayer.EyePosition );
FastColour baseCol = game.Map.IsLit( eyePos ) ? game.Map.Sunlight : game.Map.Shadowlight;
col = FastColour.Scale( baseCol, 0.8f );
block = ((FakePlayer)p).Block;
} else {
block = Byte.Parse( p.ModelName );
}
CalcState( block );
if( block == 0 )
return;
lastTexId = -1;
atlas = game.TerrainAtlas1D;
if( game.BlockInfo.IsSprite[block] ) {
SpriteXQuad( TileSide.Right, true );
SpriteZQuad( TileSide.Back, false );
SpriteXQuad( TileSide.Right, false );
SpriteZQuad( TileSide.Back, true );
} else {
YQuad( 0f, TileSide.Bottom, FastColour.ShadeYBottom );
XQuad( extent, TileSide.Left, -extent, extent, true, FastColour.ShadeX );
ZQuad( -extent, TileSide.Front, -extent, extent, true, FastColour.ShadeZ );
ZQuad( extent, TileSide.Back, -extent, extent, false, FastColour.ShadeZ );
YQuad( blockHeight, TileSide.Top, 1.0f );
XQuad( -extent, TileSide.Right, -extent, extent, true, FastColour.ShadeX );
}
if( index > 0 ) {
graphics.BindTexture( lastTexId );
TransformVertices();
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, cache.vb, cache.vertices, index, index * 6 / 4 );
}
}
void YQuad( float y, int side, float shade ) {
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
FlushIfNotSame( texIndex );
FastColour col = bright ? FastColour.White : FastColour.Scale( this.col, shade );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( -extent, y, -extent, rec.U1, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( extent, y, -extent, rec.U2, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( extent, y, extent, rec.U2, rec.V2, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( -extent, y, extent, rec.U1, rec.V2, col );
}
void ZQuad( float z, int side, float x1, float x2, bool swapU, float shade ) {
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
FlushIfNotSame( texIndex );
FastColour col = bright ? FastColour.White : FastColour.Scale( this.col, shade );
if( blockHeight != 1 )
rec.V2 = rec.V1 + blockHeight * atlas.invElementSize * (15.99f/16f);
if( swapU ) rec.SwapU();
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x1, 0f, z, rec.U1, rec.V2, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x1, blockHeight, z, rec.U1, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x2, blockHeight, z, rec.U2, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x2, 0f, z, rec.U2, rec.V2, col );
}
void XQuad( float x, int side, float z1, float z2, bool swapU, float shade ) {
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
FlushIfNotSame( texIndex );
FastColour col = bright ? FastColour.White : FastColour.Scale( this.col, shade );
if( blockHeight != 1 )
rec.V2 = rec.V1 + blockHeight * atlas.invElementSize * (15.99f/16f);
if( swapU ) rec.SwapU();
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x, 0f, z1, rec.U1, rec.V2, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x, blockHeight, z1, rec.U1, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x, blockHeight, z2, rec.U2, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x, 0f, z2, rec.U2, rec.V2, col );
}
void SpriteZQuad( int side, bool firstPart ) {
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
FlushIfNotSame( texIndex );
if( blockHeight != 1 )
rec.V2 = rec.V1 + blockHeight * atlas.invElementSize * (15.99f/16f);
FastColour col = bright ? FastColour.White : this.col;
float p1, p2;
if( firstPart ) { // Need to break into two quads for when drawing a sprite model in hand.
rec.U1 = 0.5f; p1 = -5.5f/16; p2 = 0.0f/16;
} else {
rec.U2 = 0.5f; p1 = 0.0f/16; p2 = 5.5f/16;
}
cache.vertices[index++] = new VertexPos3fTex2fCol4b( p1, 0f, p1, rec.U2, rec.V2, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( p1, blockHeight, p1, rec.U2, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( p2, blockHeight, p2, rec.U1, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( p2, 0f, p2, rec.U1, rec.V2, col );
}
void SpriteXQuad( int side, bool firstPart ) { // dis is broken
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
FlushIfNotSame( texIndex );
if( blockHeight != 1 )
rec.V2 = rec.V1 + blockHeight * atlas.invElementSize * (15.99f/16f);
FastColour col = bright ? FastColour.White : this.col;
float x1, x2, z1, z2;
if( firstPart ) {
rec.U1 = 0; rec.U2 = 0.5f; x1 = -5.5f/16;
x2 = 0.0f/16; z1 = 5.5f/16; z2 = 0.0f/16;
} else {
rec.U1 = 0.5f; rec.U2 = 1f; x1 = 0.0f/16;
x2 = 5.5f/16; z1 = 0.0f/16; z2 = -5.5f/16;
}
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x1, 0f, z1, rec.U1, rec.V2, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x1, blockHeight, z1, rec.U1, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x2, blockHeight, z2, rec.U2, rec.V1, col );
cache.vertices[index++] = new VertexPos3fTex2fCol4b( x2, 0f, z2, rec.U2, rec.V2, col );
}
void FlushIfNotSame( int texIndex ) {
int texId = game.TerrainAtlas1D.TexIds[texIndex];
if( texId == lastTexId ) return;
if( lastTexId != -1 ) {
graphics.BindTexture( lastTexId );
TransformVertices();
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, cache.vb,
cache.vertices, index, index * 6 / 4 );
}
lastTexId = texId;
index = 0;
}
void TransformVertices() {
for( int i = 0; i < index; i++ ) {
VertexPos3fTex2fCol4b vertex = cache.vertices[i];
Vector3 newPos = Utils.RotateY( vertex.X, vertex.Y, vertex.Z, cosA, sinA ) + pos;
vertex.X = newPos.X; vertex.Y = newPos.Y; vertex.Z = newPos.Z;
cache.vertices[i] = vertex;
}
}
}
}