mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-10-24 23:17:11 -04:00
260 lines
8.6 KiB
C#
260 lines
8.6 KiB
C#
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
using System;
|
|
using ClassicalSharp.Entities;
|
|
using ClassicalSharp.GraphicsAPI;
|
|
using ClassicalSharp.Physics;
|
|
using ClassicalSharp.Renderers;
|
|
using ClassicalSharp.Textures;
|
|
using OpenTK;
|
|
|
|
#if USE16_BIT
|
|
using BlockID = System.UInt16;
|
|
#else
|
|
using BlockID = System.Byte;
|
|
#endif
|
|
|
|
namespace ClassicalSharp.Model {
|
|
|
|
public class BlockModel : IModel {
|
|
|
|
BlockID block = 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;
|
|
Bobbing = false;
|
|
UsesSkin = false;
|
|
}
|
|
|
|
public override void CreateParts() { }
|
|
|
|
public override float NameYOffset { get { return height + 0.075f; } }
|
|
|
|
public override float GetEyeY(Entity entity) {
|
|
BlockID 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.0f, -0.5f);
|
|
public override AABB PickingBounds {
|
|
get { return new AABB(minBB, maxBB).Offset(offset); }
|
|
}
|
|
|
|
public void CalcState(BlockID 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 float RenderDistance(Entity p) {
|
|
block = Utils.FastByte(p.ModelName);
|
|
CalcState(block);
|
|
return base.RenderDistance(p);
|
|
}
|
|
|
|
int lastTexId = -1;
|
|
protected override void DrawModel(Entity 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;
|
|
Vector3I P = Vector3I.Floor(realP.EyePosition);
|
|
col = game.World.IsValidPos(P) ? game.Lighting.LightCol(P.X, P.Y, P.Z) : game.Lighting.Outside;
|
|
|
|
// Adjust pitch so angle when looking straight down is 0.
|
|
float adjHeadX = realP.HeadX - 90;
|
|
if (adjHeadX < 0) adjHeadX += 360;
|
|
|
|
// Adjust colour so held block is brighter when looking straght up
|
|
float t = Math.Abs(adjHeadX - 180) / 180;
|
|
float colScale = Utils.Lerp(0.9f, 0.7f, t);
|
|
col = FastColour.ScalePacked(col, colScale);
|
|
block = ((FakePlayer)p).Block;
|
|
} else {
|
|
block = Utils.FastByte(p.ModelName);
|
|
}
|
|
|
|
CalcState(block);
|
|
if (!(p is FakePlayer)) NoShade = bright;
|
|
if (bright) col = FastColour.WhitePacked;
|
|
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
|
|
cache.vertices[i] = v;
|
|
}
|
|
}
|
|
|
|
CuboidDrawer drawer = new CuboidDrawer();
|
|
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 {
|
|
drawer.elementsPerAtlas1D = atlas.elementsPerAtlas1D;
|
|
drawer.invVerElementSize = atlas.invElementSize;
|
|
|
|
drawer.minBB = game.BlockInfo.MinBB[block]; drawer.minBB.Y = 1 - drawer.minBB.Y;
|
|
drawer.maxBB = game.BlockInfo.MaxBB[block]; drawer.maxBB.Y = 1 - drawer.maxBB.Y;
|
|
|
|
Vector3 min = game.BlockInfo.RenderMinBB[block];
|
|
Vector3 max = game.BlockInfo.RenderMaxBB[block];
|
|
drawer.x1 = min.X - 0.5f; drawer.y1 = min.Y; drawer.z1 = min.Z - 0.5f;
|
|
drawer.x2 = max.X - 0.5f; drawer.y2 = max.Y; drawer.z2 = max.Z - 0.5f;
|
|
|
|
drawer.Tinted = game.BlockInfo.Tinted[block];
|
|
drawer.TintColour = game.BlockInfo.FogColour[block];
|
|
|
|
drawer.Bottom(1, Colour(FastColour.ShadeYBottom), GetTex(Side.Bottom), cache.vertices, ref index);
|
|
if (SwitchOrder) {
|
|
drawer.Right(1, Colour(FastColour.ShadeX), GetTex(Side.Right), cache.vertices, ref index);
|
|
drawer.Back(1, Colour(FastColour.ShadeZ), GetTex(Side.Back), cache.vertices, ref index);
|
|
drawer.Left(1, Colour(FastColour.ShadeX), GetTex(Side.Left), cache.vertices, ref index);
|
|
drawer.Front(1, Colour(FastColour.ShadeZ), GetTex(Side.Front), cache.vertices, ref index);
|
|
} else {
|
|
drawer.Front(1, Colour(FastColour.ShadeZ), GetTex(Side.Front), cache.vertices, ref index);
|
|
drawer.Right(1, Colour(FastColour.ShadeX), GetTex(Side.Right), cache.vertices, ref index);
|
|
drawer.Back(1, Colour(FastColour.ShadeZ), GetTex(Side.Back), cache.vertices, ref index);
|
|
drawer.Left(1, Colour(FastColour.ShadeX), GetTex(Side.Left), cache.vertices, ref index);
|
|
}
|
|
drawer.Top(1, Colour(1.0f), GetTex(Side.Top), cache.vertices, ref index);
|
|
}
|
|
}
|
|
|
|
int GetTex(int side) {
|
|
int texId = game.BlockInfo.GetTextureLoc(block, side);
|
|
texIndex = texId / atlas.elementsPerAtlas1D;
|
|
|
|
FlushIfNotSame(texIndex);
|
|
return texId;
|
|
}
|
|
|
|
int Colour(float shade) {
|
|
return NoShade ? col : FastColour.ScalePacked(col, shade);
|
|
}
|
|
|
|
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 = this.col;
|
|
|
|
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 = this.col;
|
|
|
|
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);
|
|
}
|
|
}
|
|
} |