2016-11-27 14:47:09 +11:00

290 lines
10 KiB
C#

// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using ClassicalSharp.Entities;
using ClassicalSharp.GraphicsAPI;
using ClassicalSharp.Physics;
using ClassicalSharp.Renderers;
using ClassicalSharp.Textures;
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.Draw[block] == DrawType.Gas) {
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.Draw[block] == DrawType.Sprite)
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.Draw[block] == DrawType.Gas) return;
lastTexId = -1;
atlas = game.TerrainAtlas1D;
bool sprite = game.BlockInfo.Draw[block] == DrawType.Sprite;
DrawParts(sprite);
if (index == 0) return;
IGraphicsApi gfx = game.Graphics;
gfx.BindTexture(lastTexId);
TransformVertices();
if (sprite) gfx.FaceCulling = true;
UpdateVB();
if (sprite) gfx.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);
}
}
}