1
\$\begingroup\$

Edit: I almost forgot to mention - when I place NEW blocks they render, but nothing triggers the originals to render (I have a button assigned to recalculate/redraw for every block surrounding (and including) the currently moused-over one).

I've got a voxel engine that I've been working on for some time, and while developing core features I wasn't too concerned with performance yet. Everything runs pretty quick, but it was eating memory, so one of the obvious things I could do was change my block from a class to a struct (post-changes code below). The most annoying thing was the lack of null comparisons, as I'd used those all over, so I created an "Air" type and used that instead. However, when I was all done making the changes, all blocks were invisible - mousing over them still shows the name/health of the block, I can still jump on them and watch my coordinates change, but they do not render. If I literally just change the single word "struct" to "class" with no other modifications the blocks render properly again. My guess is something in my block struct isn't valid for use with a struct even though it doesn't generate syntax errors? I'm really not sure where the issue lies here, as I've never used structs for mass data storage like this before (in-memory or on-disk sequential mapping, on the other hand, a lot). If you need to see other code, let me know, but I'd suspect the issue is here or else is a fundamental/conceptual thing that seeing the code won't help find..

[DataContract, StructLayout(LayoutKind.Sequential), Serializable]
public struct Block : ICollidable3, ISerializable
{
    private static ulong _nextID = 0;
    [DataMember]
    public ulong ID;

    public bool IsAir
    {
        get
        {
            return Type == BlockType.Get("Air");
        }
    }

    public static Block AirBlock = new Block(Vec3.Zero, BlockType.Get("Air"));

    [DataMember]
    private Vec3 _position;
    public Vec3 Position 
    {
        get
        {
            return _position;
        }
        set
        {
            _position = value;
            BoundingBox = new Microsoft.Xna.Framework.BoundingBox(_position - HalfSize, _position + HalfSize);
        }
    }
    public float Size
    {
        get
        {
            return HalfSize * 2;
        }
        set
        {
            HalfSize = value / 2;
        }
    }
    [DataMember]
    public float HalfSize;
    [DataMember]
    public float Yaw;
    [DataMember]
    public float Pitch;
    [DataMember]
    public float Roll;

    [DataMember]
    private BoundingBox _boundingBox;
    public BoundingBox BoundingBox
    {
        get
        {
            return _boundingBox;
        }
        set 
        {
            _boundingBox = value;
        }
    }

    public static Plane NegXPlane = new Plane(-1, 0, 0, 0);
    public static Plane PosYPlane = new Plane(0, -1, 0, 0);
    public static Plane PosZPlane = new Plane(0, 0, -1, 0);
    public static Plane PosXPlane = new Plane(1, 0, 0, 1);
    public static Plane NegYPlane = new Plane(0, 1, 0, 1);
    public static Plane NegZPlane = new Plane(0, 0, 1, 1);

    #region Vertices
    static float ao = .65f;

    public LitTextureVertex[] NegXVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.NegX)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, -Vec3.UnitX, Type.TexSideX, NegXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitX, Type.TexSideY, NegXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitX, Type.TexSideW, NegXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitX, Type.TexSideY, NegXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitX, Type.TexSideZ, NegXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitX, Type.TexSideW, NegXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitX, Type.TexSideZ, NegXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, -Vec3.UnitX, Type.TexSideX, NegXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitX, Type.TexSideY, NegXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitX, Type.TexSideZ, NegXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitX, Type.TexSideW, NegXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, -Vec3.UnitX, Type.TexSideX, NegXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public LitTextureVertex[] NegYVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.NegY)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitY, Type.TexBottomX, NegYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitY, Type.TexBottomY, NegYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitY, Type.TexBottomW, NegYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitY, Type.TexBottomY, NegYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, -Vec3.UnitY, Type.TexBottomZ, NegYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitY, Type.TexBottomW, NegYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, -Vec3.UnitY, Type.TexBottomZ, NegYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitY, Type.TexBottomX, NegYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitY, Type.TexBottomY, NegYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, -Vec3.UnitY, Type.TexBottomZ, NegYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, -Vec3.UnitY, Type.TexBottomW, NegYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitY, Type.TexBottomX, NegYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public LitTextureVertex[] NegZVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.NegZ)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitZ, Type.TexSideX, NegZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, -Vec3.UnitZ, Type.TexSideY, NegZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitZ, Type.TexSideW, NegZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, -Vec3.UnitZ, Type.TexSideY, NegZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitZ, Type.TexSideZ, NegZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitZ, Type.TexSideW, NegZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitZ, Type.TexSideZ, NegZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitZ, Type.TexSideX, NegZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, -Vec3.UnitZ, Type.TexSideY, NegZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, -Vec3.UnitZ, Type.TexSideZ, NegZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNN, -Vec3.UnitZ, Type.TexSideW, NegZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, -Vec3.UnitZ, Type.TexSideX, NegZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public LitTextureVertex[] PosZVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.PosZ)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitZ, Type.TexSideX, PosZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitZ, Type.TexSideY, PosZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitZ, Type.TexSideW, PosZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitZ, Type.TexSideY, PosZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, Vec3.UnitZ, Type.TexSideZ, PosZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitZ, Type.TexSideW, PosZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, Vec3.UnitZ, Type.TexSideZ, PosZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitZ, Type.TexSideX, PosZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitZ, Type.TexSideY, PosZOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NNP, Vec3.UnitZ, Type.TexSideZ, PosZOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitZ, Type.TexSideW, PosZOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitZ, Type.TexSideX, PosZOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public LitTextureVertex[] PosXVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.PosX)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitX, Type.TexSideX, PosXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitX, Type.TexSideY, PosXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, Vec3.UnitX, Type.TexSideW, PosXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitX, Type.TexSideY, PosXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitX, Type.TexSideZ, PosXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, Vec3.UnitX, Type.TexSideW, PosXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitX, Type.TexSideZ, PosXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitX, Type.TexSideX, PosXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitX, Type.TexSideY, PosXOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNP, Vec3.UnitX, Type.TexSideZ, PosXOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PNN, Vec3.UnitX, Type.TexSideW, PosXOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitX, Type.TexSideX, PosXOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public LitTextureVertex[] PosYVertices
    {
        get
        {
            if (Flips.HasFlag(Faces.PosY)) return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitY, Type.TexTopX, PosYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitY, Type.TexTopY, PosYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, Vec3.UnitY, Type.TexTopW, PosYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitY, Type.TexTopY, PosYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitY, Type.TexTopZ, PosYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, Vec3.UnitY, Type.TexTopW, PosYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
            };
            else return new LitTextureVertex[6]
            {
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitY, Type.TexTopZ, PosYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitY, Type.TexTopX, PosYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPP, Vec3.UnitY, Type.TexTopY, PosYOcclusion.HasFlag(FaceCorners.Y) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.PPN, Vec3.UnitY, Type.TexTopZ, PosYOcclusion.HasFlag(FaceCorners.Z) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPN, Vec3.UnitY, Type.TexTopW, PosYOcclusion.HasFlag(FaceCorners.W) ? ao : 0, Type.Material.Reflectivity),
                new LitTextureVertex(Position + HalfSize * Vec3.NPP, Vec3.UnitY, Type.TexTopX, PosYOcclusion.HasFlag(FaceCorners.X) ? ao : 0, Type.Material.Reflectivity),
            };
        }
    }

    public List<LitTextureVertex> GetVertices()
    {
        List<LitTextureVertex> vertices = new List<LitTextureVertex>();
        if (Facing.HasFlag(Faces.PosX)) vertices.AddRange(PosXVertices);
        if (Facing.HasFlag(Faces.PosY)) vertices.AddRange(PosYVertices);
        if (Facing.HasFlag(Faces.PosZ)) vertices.AddRange(PosZVertices);
        if (Facing.HasFlag(Faces.NegX)) vertices.AddRange(NegXVertices);
        if (Facing.HasFlag(Faces.NegY)) vertices.AddRange(NegYVertices);
        if (Facing.HasFlag(Faces.NegZ)) vertices.AddRange(NegZVertices);
        return vertices;
    }

    #endregion

    [DataMember]
    public bool IsVisible;

    public Block(SerializationInfo info, StreamingContext ctxt)
    {
        _position = (Vec3)info.GetValue("Position", typeof(Vec3));
        ID = info.GetUInt64("ID");
        HalfSize = info.GetSingle("HalfSize");
        Yaw = info.GetSingle("Yaw");
        Pitch = info.GetSingle("Pitch");
        Roll = info.GetSingle("Roll");
        Box bbox = (Box)info.GetValue("BoundingBox", typeof(Box));
        _boundingBox = new BoundingBox(bbox.Min, bbox.Max);
        Flips = (Faces)info.GetValue("Flips", typeof(Faces));
        NegXOcclusion = (FaceCorners)info.GetValue("NegXOcclusion", typeof(FaceCorners));
        PosXOcclusion = (FaceCorners)info.GetValue("PosXOcclusion", typeof(FaceCorners));
        NegYOcclusion = (FaceCorners)info.GetValue("NegYOcclusion", typeof(FaceCorners));
        PosYOcclusion = (FaceCorners)info.GetValue("PosYOcclusion", typeof(FaceCorners));
        NegZOcclusion = (FaceCorners)info.GetValue("NegZOcclusion", typeof(FaceCorners));
        PosZOcclusion = (FaceCorners)info.GetValue("PosZOcclusion", typeof(FaceCorners));
        IsVisible = info.GetBoolean("IsVisible");
        Facing = (Faces)info.GetValue("Facing", typeof(Faces));
        _type = (BlockType)info.GetValue("Type", typeof(BlockType));
        Health = info.GetSingle("Health");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
    {
        info.AddValue("Position", _position);
        info.AddValue("ID", ID);
        info.AddValue("HalfSize", HalfSize);
        info.AddValue("Yaw", Yaw);
        info.AddValue("Pitch", Pitch);
        info.AddValue("Roll", Roll);
        info.AddValue("BoundingBox", Box.FromMinMax(_boundingBox.Min, _boundingBox.Max));
        info.AddValue("Flips", Flips);
        info.AddValue("NegXOcclusion", NegXOcclusion);
        info.AddValue("PosXOcclusion", PosXOcclusion);
        info.AddValue("NegYOcclusion", NegYOcclusion);
        info.AddValue("PosYOcclusion", PosYOcclusion);
        info.AddValue("NegZOcclusion", NegZOcclusion);
        info.AddValue("PosZOcclusion", PosZOcclusion);
        info.AddValue("IsVisible", IsVisible);
        info.AddValue("Facing", Facing);
        info.AddValue("Type", _type);
        info.AddValue("Health", Health);
    }

    public Block(Vec3 position, BlockType type, float size = 1, float yaw = 0, float pitch = 0, float roll = 0)
    {
        ID = _nextID++;
        HalfSize = size / 2;
        Yaw = yaw;
        Pitch = pitch;
        Roll = roll;
        _type = type;
        _position = position;
        IsVisible = true; 
        _boundingBox = new Microsoft.Xna.Framework.BoundingBox(_position - HalfSize, _position + HalfSize);
        Facing = Faces.None;
        Flips = Faces.None;
        PosXOcclusion = FaceCorners.None;
        PosYOcclusion = FaceCorners.None;
        PosZOcclusion = FaceCorners.None;
        NegXOcclusion = FaceCorners.None;
        NegYOcclusion = FaceCorners.None;
        NegZOcclusion = FaceCorners.None;
        Health = 100;
    }

    [Flags]
    public enum Faces : byte
    {
        None = 0,
        PosX = 1,
        PosY = 2,
        PosZ = 4,
        NegX = 8,
        NegY = 16,
        NegZ = 32,
        All = 127,
    };

    [Flags]
    public enum FaceCorners : byte
    {
        None = 0,
        X = 1,
        Y = 2,
        Z = 4,
        W = 8,
        All = 127,
    }

    [DataMember]
    public FaceCorners PosXOcclusion;
    [DataMember]
    public FaceCorners PosYOcclusion;
    [DataMember]
    public FaceCorners PosZOcclusion;
    [DataMember]
    public FaceCorners NegXOcclusion;
    [DataMember]
    public FaceCorners NegYOcclusion;
    [DataMember]
    public FaceCorners NegZOcclusion;

    [DataMember]
    private BlockType _type;
    public BlockType Type
    {
        get
        {
            return (_type == null ? BlockType.Get("Air") : _type);
    }
        set
        {
            _type = value;
        }
    }

    [DataMember]
    public Faces Facing;
    [DataMember]
    public Faces Flips;
    [DataMember]
    public float Health;
}

When a chunk builds its model, this happens for opaque, transparent, and liquid blocks (opaque shown here for brevity):

        IEnumerable<Block> blocks = Blocks.AsParallel().Cast<Block>().Where(b => b.IsAir == false && b.IsVisible);
        IEnumerable<Block> opaqueBlocks = blocks.AsParallel().Cast<Block>().Where(b => !b.Type.Transparency);
        //(Transparent, then Liquid block lists would be here)
        List<LitTextureVertex> vertices = new List<LitTextureVertex>();
        foreach (Block b in opaqueBlocks) vertices.AddRange(b.GetVertices());
        if (vertices.Count > 0)
        {
            lock (VertexBuffer)
            {
                if (vertices.Count > VertexBuffer.VertexCount) VertexBuffer = new DynamicVertexBuffer(Globals.GraphicsDevice, LitTextureVertex.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly);
                VertexBuffer.SetData<LitTextureVertex>(vertices.ToArray(), 0, vertices.Count, SetDataOptions.Discard);
            }
        }
        vertices.Clear();
        //(Transparent, then Liquid blocks with a similar process)
        Dirty = false;
\$\endgroup\$
6
  • \$\begingroup\$ Added important tidbit to the top of the original post. \$\endgroup\$
    – Yushatak
    Jan 16, 2015 at 16:11
  • \$\begingroup\$ How are you actually drawing the blocks? Also, voxel engines typically try to store as little data in the blocks themselves as possible because they are the most abundant things in the world. That could potentially be a reason why you were having memory problems earlier. \$\endgroup\$ Jan 17, 2015 at 19:23
  • \$\begingroup\$ Well as you can see in the class, a model is generated (with appropriate face-culls and quad-flips taken into consideration) when a function is called, and this function is called for every visible block in a chunk when the chunk is visually rebuilt. This is then cached and not rebuilt until necessary (when things change). I'll edit the original post with a sample (opaque blocks only to avoid length, but the same happens for transparent and liquid blocks). \$\endgroup\$
    – Yushatak
    Jan 21, 2015 at 13:41
  • \$\begingroup\$ New development - seems that the "Health" values reset whenever you click when the blocks are structs. I see the number for the health being displayed on the screen flicker a bit when I click, indicating that at least for a moment it does decrement, but it stays at 100%. \$\endgroup\$
    – Yushatak
    Jan 21, 2015 at 13:56
  • \$\begingroup\$ On a hunch I displayed the "Facing" value (none vs. anything else) and it seems that the struct blocks are invisible due to face culling, or more appropriately a lack of the faces ever being enabled (they start disabled). The blocks placed by the player show up because they are assigned Facing values when placed based on surrounding blocks. Why are my structs behaving so weirdly? The same culling/etc. functions should be working fine that work with the class version, and the Health value should certainly be able to change.. \$\endgroup\$
    – Yushatak
    Jan 21, 2015 at 13:59

2 Answers 2

1
\$\begingroup\$

Changes when it was structs were not propagating because things were passed by value rather than reference. This meant that the face culling wasn't applying, and all blocks had all faces culled unless they were freshly-placed.

I opted to stick with classes for now, but that was the issue.

\$\endgroup\$
0
\$\begingroup\$

Try change default Facing value from none to something else. If this value is established for all the blocks you get it just does not change where it should. Perhaps you pass a block as a parameter and change its value Facing inside the method.

\$\endgroup\$
5
  • \$\begingroup\$ Do I need to pass structs around by ref or something? \$\endgroup\$
    – Yushatak
    Jan 21, 2015 at 16:20
  • 1
    \$\begingroup\$ Yes, structures are passed by value by default in C# \$\endgroup\$ Jan 21, 2015 at 16:22
  • \$\begingroup\$ You can, if problem in that. Or you can return facing, if it's possible. I'm not big specialist in all this staff with blocking\unblocking parameters of non-reference types but as i remember it's not good for performance. \$\endgroup\$
    – user52551
    Jan 21, 2015 at 16:26
  • \$\begingroup\$ I wonder why you have decided to use the structure instead of a class? Personally, I abandoned structures in the first place, because it is possible for a class to get a reference to the block and change it without overwriting the entire struct, and secondly, the unit is not subject to the requirements for the structure. Do not take criticism, I wonder whether it will give a boost in performance. \$\endgroup\$
    – user52551
    Jan 21, 2015 at 16:41
  • \$\begingroup\$ I prefer classes functionally, but from what I've read a struct has a much lower memory overhead and access time - a class goes through another pointer (incurring up to 8 bytes waste on a 64-bit system per instance) whereas a struct is more direct. 8 bytes may sound insignificant, but in a block engine that means tens of thousands times that 8 byte waste and it adds up. Perhaps I've been mislead, though, as if I have to pass by ref then it sounds like the benefit might not be there. It's been a few weeks since I read up on this, though, so perhaps I'm getting confused. \$\endgroup\$
    – Yushatak
    Jan 21, 2015 at 17:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .