// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
using System;
using OpenTK;
#if USE16_BIT
using BlockID = System.UInt16;
#else
using BlockID = System.Byte;
#endif
namespace ClassicalSharp {
/// Stores various properties about the blocks in Minecraft Classic.
public partial class BlockInfo {
public byte[] hidden = new byte[Block.Count * Block.Count];
public byte[] CanStretch = new byte[Block.Count];
internal void UpdateCulling() {
for (int block = 0; block < Block.Count; block++)
CanStretch[block] = 0x3F;
for (int block = 1; block < Block.Count; block++) {
for (int neighbour = 1; neighbour < Block.Count; neighbour++) {
CalcCulling((BlockID)block, (BlockID)neighbour);
}
}
}
internal void UpdateCulling(BlockID block) {
CanStretch[block] = 0x3F;
for (int other = 1; other < Block.Count; other++) {
CalcCulling(block, (BlockID)other);
CalcCulling((BlockID)other, block);
}
}
void CalcCulling(BlockID block, BlockID other) {
Vector3 bMin = MinBB[block], bMax = MaxBB[block];
Vector3 oMin = MinBB[other], oMax = MaxBB[other];
if (IsLiquid(block)) bMax.Y -= 1.5f/16;
if (IsLiquid(other)) oMax.Y -= 1.5f/16;
if (Draw[block] == DrawType.Sprite) {
SetHidden(block, other, Side.Left, true);
SetHidden(block, other, Side.Right, true);
SetHidden(block, other, Side.Front, true);
SetHidden(block, other, Side.Back, true);
SetHidden(block, other, Side.Bottom, oMax.Y == 1);
SetHidden(block, other, Side.Top, bMax.Y == 1);
} else {
SetXStretch(block, bMin.X == 0 && bMax.X == 1);
SetZStretch(block, bMin.Z == 0 && bMax.Z == 1);
bool bothLiquid = IsLiquid(block) && IsLiquid(other);
SetHidden(block, other, Side.Left, oMax.X == 1 && bMin.X == 0);
SetHidden(block, other, Side.Right, oMin.X == 0 && bMax.X == 1);
SetHidden(block, other, Side.Front, oMax.Z == 1 && bMin.Z == 0);
SetHidden(block, other, Side.Back, oMin.Z == 0 && bMax.Z == 1);
SetHidden(block, other, Side.Bottom,
bothLiquid || (oMax.Y == 1 && bMin.Y == 0));
SetHidden(block, other, Side.Top,
bothLiquid || (oMin.Y == 0 && bMax.Y == 1));
}
}
bool IsHidden(BlockID block, BlockID other, int side) {
// Sprite blocks can never hide faces.
if (Draw[block] == DrawType.Sprite) return false;
// NOTE: Water is always culled by lava
if ((block == Block.Water || block == Block.StillWater)
&& (other == Block.Lava || other == Block.StillLava))
return true;
// All blocks (except for say leaves) cull with themselves.
if (block == other) return Draw[block] != DrawType.TransparentThick;
// An opaque neighbour (asides from lava) culls the face.
if (Draw[other] == DrawType.Opaque && !IsLiquid(other)) return true;
if (Draw[block] != DrawType.Translucent || Draw[other] != DrawType.Translucent) return false;
// e.g. for water / ice, don't need to draw water.
CollideType bType = Collide[block], oType = Collide[other];
bool canSkip = (bType == CollideType.Solid && oType == CollideType.Solid)
|| bType != CollideType.Solid;
return canSkip;
}
void SetHidden(BlockID block, BlockID other, int side, bool value) {
value = IsHidden(block, other, side) && FaceOccluded(block, other, side) && value;
int bit = value ? 1 : 0;
hidden[block * Block.Count + other] &= (byte)~(1 << side);
hidden[block * Block.Count + other] |= (byte)(bit << side);
}
/// Returns whether the face at the given face of the block
/// should be drawn with the neighbour 'other' present on the other side of the face.
public bool IsFaceHidden(BlockID block, BlockID other, int tileSide) {
#if USE16_BIT
return (hidden[(block << 12) | other] & (1 << tileSide)) != 0;
#else
return (hidden[(block << 8) | other] & (1 << tileSide)) != 0;
#endif
}
void SetXStretch(BlockID block, bool stretch) {
const byte mask = 0x3C;
CanStretch[block] &= 0xC3; // ~0x3C
CanStretch[block] |= (stretch ? mask : (byte)0);
}
void SetZStretch(BlockID block, bool stretch) {
const byte mask = 0x03;
CanStretch[block] &= 0xFC; // ~0x03
CanStretch[block] |= (stretch ? mask : (byte)0);
}
}
}