mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-10-06 04:25:05 -04:00
289 lines
11 KiB
C#
289 lines
11 KiB
C#
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
|
using System;
|
|
using ClassicalSharp.Entities;
|
|
using ClassicalSharp.GraphicsAPI;
|
|
using ClassicalSharp.Renderers;
|
|
using ClassicalSharp.TexturePack;
|
|
using OpenTK;
|
|
|
|
namespace ClassicalSharp.Model {
|
|
|
|
public class BlockModel : IModel {
|
|
|
|
byte block = (byte)Block.Air;
|
|
float height;
|
|
TerrainAtlas1D atlas;
|
|
bool bright;
|
|
Vector3 minBB, maxBB;
|
|
public bool NoShade = false, SwitchOrder = false;
|
|
public float CosX = 1, SinX = 0;
|
|
ModelCache cache;
|
|
|
|
public BlockModel( Game game ) : base( game ) {
|
|
cache = game.ModelCache;
|
|
}
|
|
|
|
internal override void CreateParts() { }
|
|
|
|
public override bool Bobbing { get { return false; } }
|
|
|
|
public override float NameYOffset { get { return height + 0.075f; } }
|
|
|
|
public override float GetEyeY( Entity entity ) {
|
|
byte block = Byte.Parse( entity.ModelName );
|
|
float minY = game.BlockInfo.MinBB[block].Y;
|
|
float maxY = game.BlockInfo.MaxBB[block].Y;
|
|
return block == 0 ? 1 : (minY + maxY) / 2;
|
|
}
|
|
|
|
static Vector3 colShrink = new Vector3( 0.75f/16, 0.75f/16, 0.75f/16 );
|
|
public override Vector3 CollisionSize {
|
|
get { return (maxBB - minBB) - colShrink; } // to fit slightly inside
|
|
}
|
|
|
|
static Vector3 offset = new Vector3( -0.5f, -0.5f, -0.5f );
|
|
public override AABB PickingBounds {
|
|
get { return new AABB( minBB, maxBB ).Offset( offset ); }
|
|
}
|
|
|
|
public void CalcState( byte block ) {
|
|
if( game.BlockInfo.IsAir[block] ) {
|
|
bright = false;
|
|
minBB = Vector3.Zero;
|
|
maxBB = Vector3.One;
|
|
height = 1;
|
|
} else {
|
|
bright = game.BlockInfo.FullBright[block];
|
|
minBB = game.BlockInfo.MinBB[block];
|
|
maxBB = game.BlockInfo.MaxBB[block];
|
|
height = maxBB.Y - minBB.Y;
|
|
if( game.BlockInfo.IsSprite[block] )
|
|
height = 1;
|
|
}
|
|
}
|
|
|
|
public override bool ShouldRender( Entity p, FrustumCulling culling ) {
|
|
block = Utils.FastByte( p.ModelName );
|
|
CalcState( block );
|
|
return base.ShouldRender( p, culling );
|
|
}
|
|
|
|
public override float RenderDistance( Entity p ) {
|
|
block = Utils.FastByte( p.ModelName );
|
|
CalcState( block );
|
|
return base.RenderDistance( p );
|
|
}
|
|
|
|
int lastTexId = -1;
|
|
int colPacked;
|
|
protected override void DrawModel( 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 ) {
|
|
Player realP = game.LocalPlayer;
|
|
col = game.World.IsLit( realP.EyePosition )
|
|
? game.World.Env.Sunlight : game.World.Env.Shadowlight;
|
|
|
|
// Adjust pitch so angle when looking straight down is 0.
|
|
float adjPitch = realP.PitchDegrees - 90;
|
|
if( adjPitch < 0 ) adjPitch += 360;
|
|
|
|
// Adjust colour so held block is brighter when looking straght up
|
|
float t = Math.Abs( adjPitch - 180 ) / 180;
|
|
float colScale = Utils.Lerp( 0.9f, 0.7f, t);
|
|
col = FastColour.Scale( col, colScale );
|
|
block = ((FakePlayer)p).Block;
|
|
} else {
|
|
block = Utils.FastByte( p.ModelName );
|
|
}
|
|
|
|
CalcState( block );
|
|
colPacked = col.Pack();
|
|
if( game.BlockInfo.IsAir[block] ) return;
|
|
|
|
lastTexId = -1;
|
|
atlas = game.TerrainAtlas1D;
|
|
bool sprite = game.BlockInfo.IsSprite[block];
|
|
DrawParts( sprite );
|
|
if( index == 0 ) return;
|
|
|
|
IGraphicsApi api = game.Graphics;
|
|
api.BindTexture( lastTexId );
|
|
TransformVertices();
|
|
|
|
if( sprite ) api.FaceCulling = true;
|
|
UpdateVB();
|
|
if( sprite ) api.FaceCulling = false;
|
|
}
|
|
|
|
void FlushIfNotSame( int texIndex ) {
|
|
int texId = game.TerrainAtlas1D.TexIds[texIndex];
|
|
if( texId == lastTexId ) return;
|
|
|
|
if( lastTexId != -1 ) {
|
|
game.Graphics.BindTexture( lastTexId );
|
|
TransformVertices();
|
|
UpdateVB();
|
|
}
|
|
lastTexId = texId;
|
|
index = 0;
|
|
}
|
|
|
|
void TransformVertices() {
|
|
for( int i = 0; i < index; i++ ) {
|
|
VertexP3fT2fC4b v = cache.vertices[i];
|
|
float t = 0;
|
|
t = CosX * v.Y + SinX * v.Z; v.Z = -SinX * v.Y + CosX * v.Z; v.Y = t; // Inlined RotX
|
|
t = cosYaw * v.X - sinYaw * v.Z; v.Z = sinYaw * v.X + cosYaw * v.Z; v.X = t; // Inlined RotY
|
|
v.X *= scale; v.Y *= scale; v.Z *= scale;
|
|
v.X += pos.X; v.Y += pos.Y; v.Z += pos.Z;
|
|
cache.vertices[i] = v;
|
|
}
|
|
}
|
|
|
|
void DrawParts( bool sprite ) {
|
|
// SwitchOrder is needed for held block, which renders without depth testing
|
|
if( sprite ) {
|
|
if( SwitchOrder ) {
|
|
SpriteZQuad( Side.Back, false );
|
|
SpriteXQuad( Side.Right, false );
|
|
} else {
|
|
SpriteXQuad( Side.Right, false );
|
|
SpriteZQuad( Side.Back, false );
|
|
}
|
|
|
|
if( SwitchOrder ) {
|
|
SpriteXQuad( Side.Right, true );
|
|
SpriteZQuad( Side.Back, true );
|
|
} else {
|
|
SpriteZQuad( Side.Back, true );
|
|
SpriteXQuad( Side.Right, true );
|
|
}
|
|
} else {
|
|
YQuad( 0, Side.Bottom, FastColour.ShadeYBottom );
|
|
if( SwitchOrder ) {
|
|
XQuad( maxBB.X - 0.5f, Side.Right, true, FastColour.ShadeX );
|
|
ZQuad( maxBB.Z - 0.5f, Side.Back, false, FastColour.ShadeZ );
|
|
XQuad( minBB.X - 0.5f, Side.Left, false, FastColour.ShadeX );
|
|
ZQuad( minBB.Z - 0.5f, Side.Front, true, FastColour.ShadeZ );
|
|
} else {
|
|
ZQuad( minBB.Z - 0.5f, Side.Front, true, FastColour.ShadeZ );
|
|
XQuad( maxBB.X - 0.5f, Side.Right, true, FastColour.ShadeX );
|
|
ZQuad( maxBB.Z - 0.5f, Side.Back, false, FastColour.ShadeZ );
|
|
XQuad( minBB.X - 0.5f, Side.Left, false, FastColour.ShadeX );
|
|
}
|
|
YQuad( height, Side.Top, 1.0f );
|
|
}
|
|
}
|
|
|
|
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 );
|
|
int col = bright ? FastColour.WhitePacked : (NoShade ? colPacked : FastColour.Scale( this.col, shade ).Pack() );
|
|
|
|
float vOrigin = (texId % atlas.elementsPerAtlas1D) * atlas.invElementSize;
|
|
rec.U1 = minBB.X; rec.U2 = maxBB.X;
|
|
rec.V1 = vOrigin + minBB.Z * atlas.invElementSize;
|
|
rec.V2 = vOrigin + maxBB.Z * atlas.invElementSize * 15.99f/16f;
|
|
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( minBB.X - 0.5f, y, minBB.Z - 0.5f, rec.U1, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( maxBB.X - 0.5f, y, minBB.Z - 0.5f, rec.U2, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( maxBB.X - 0.5f, y, maxBB.Z - 0.5f, rec.U2, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( minBB.X - 0.5f, y, maxBB.Z - 0.5f, rec.U1, rec.V2, col );
|
|
}
|
|
|
|
void ZQuad( float z, int side, bool swapU, float shade ) {
|
|
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
|
|
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
|
|
FlushIfNotSame( texIndex );
|
|
int col = bright ? FastColour.WhitePacked : (NoShade ? colPacked : FastColour.Scale( this.col, shade ).Pack() );
|
|
|
|
float vOrigin = (texId % atlas.elementsPerAtlas1D) * atlas.invElementSize;
|
|
rec.U1 = minBB.X; rec.U2 = maxBB.X;
|
|
rec.V1 = vOrigin + (1 - minBB.Y) * atlas.invElementSize;
|
|
rec.V2 = vOrigin + (1 - maxBB.Y) * atlas.invElementSize * 15.99f/16f;
|
|
if( swapU ) rec.SwapU();
|
|
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( minBB.X - 0.5f, 0, z, rec.U1, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( minBB.X - 0.5f, height, z, rec.U1, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( maxBB.X - 0.5f, height, z, rec.U2, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( maxBB.X - 0.5f, 0, z, rec.U2, rec.V1, col );
|
|
}
|
|
|
|
void XQuad( float x, int side, bool swapU, float shade ) {
|
|
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
|
|
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
|
|
FlushIfNotSame( texIndex );
|
|
int col = bright ? FastColour.WhitePacked : (NoShade ? colPacked : FastColour.Scale( this.col, shade ).Pack() );
|
|
|
|
float vOrigin = (texId % atlas.elementsPerAtlas1D) * atlas.invElementSize;
|
|
rec.U1 = minBB.Z; rec.U2 = maxBB.Z;
|
|
rec.V1 = vOrigin + (1 - minBB.Y) * atlas.invElementSize;
|
|
rec.V2 = vOrigin + (1 - maxBB.Y) * atlas.invElementSize * 15.99f/16f;
|
|
if( swapU ) rec.SwapU();
|
|
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x, 0, minBB.Z - 0.5f, rec.U1, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x, height, minBB.Z - 0.5f, rec.U1, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x, height, maxBB.Z - 0.5f, rec.U2, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x, 0, maxBB.Z - 0.5f, rec.U2, rec.V1, col );
|
|
}
|
|
|
|
|
|
void SpriteZQuad( int side, bool firstPart ) {
|
|
SpriteZQuad( side, firstPart, false );
|
|
SpriteZQuad( side, firstPart, true );
|
|
}
|
|
|
|
void SpriteZQuad( int side, bool firstPart, bool mirror ) {
|
|
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
|
|
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
|
|
FlushIfNotSame( texIndex );
|
|
if( height != 1 )
|
|
rec.V2 = rec.V1 + height * atlas.invElementSize * (15.99f/16f);
|
|
int col = bright ? FastColour.WhitePacked : this.col.Pack();
|
|
|
|
float p1 = 0, p2 = 0;
|
|
if( firstPart ) { // Need to break into two quads for when drawing a sprite model in hand.
|
|
if( mirror ) { rec.U1 = 0.5f; p1 = -5.5f/16; }
|
|
else { rec.U2 = 0.5f; p2 = -5.5f/16; }
|
|
} else {
|
|
if( mirror ) { rec.U2 = 0.5f; p2 = 5.5f/16; }
|
|
else { rec.U1 = 0.5f; p1 = 5.5f/16; }
|
|
}
|
|
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( p1, 0, p1, rec.U2, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( p1, 1, p1, rec.U2, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( p2, 1, p2, rec.U1, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( p2, 0, p2, rec.U1, rec.V2, col );
|
|
}
|
|
|
|
void SpriteXQuad( int side, bool firstPart ) {
|
|
SpriteXQuad( side, firstPart, false );
|
|
SpriteXQuad( side, firstPart, true );
|
|
}
|
|
|
|
void SpriteXQuad( int side, bool firstPart, bool mirror ) {
|
|
int texId = game.BlockInfo.GetTextureLoc( block, side ), texIndex = 0;
|
|
TextureRec rec = atlas.GetTexRec( texId, 1, out texIndex );
|
|
FlushIfNotSame( texIndex );
|
|
if( height != 1 )
|
|
rec.V2 = rec.V1 + height * atlas.invElementSize * (15.99f/16f);
|
|
int col = bright ? FastColour.WhitePacked : this.col.Pack();
|
|
|
|
float x1 = 0, x2 = 0, z1 = 0, z2 = 0;
|
|
if( firstPart ) {
|
|
if( mirror ) { rec.U2 = 0.5f; x2 = -5.5f/16; z2 = 5.5f/16; }
|
|
else { rec.U1 = 0.5f; x1 = -5.5f/16; z1 = 5.5f/16; }
|
|
} else {
|
|
if( mirror ) { rec.U1 = 0.5f; x1 = 5.5f/16; z1 = -5.5f/16; }
|
|
else { rec.U2 = 0.5f; x2 = 5.5f/16; z2 = -5.5f/16; }
|
|
}
|
|
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x1, 0, z1, rec.U2, rec.V2, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x1, 1, z1, rec.U2, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x2, 1, z2, rec.U1, rec.V1, col );
|
|
cache.vertices[index++] = new VertexP3fT2fC4b( x2, 0, z2, rec.U1, rec.V2, col );
|
|
}
|
|
}
|
|
} |