Skip to content

Instantly share code, notes, and snippets.

@ChrisPritchard
Last active October 12, 2025 23:49
Show Gist options
  • Select an option

  • Save ChrisPritchard/2f907fdf743d7362824fbad5da7bbca0 to your computer and use it in GitHub Desktop.

Select an option

Save ChrisPritchard/2f907fdf743d7362824fbad5da7bbca0 to your computer and use it in GitHub Desktop.

Revisions

  1. ChrisPritchard revised this gist Oct 12, 2025. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion CubeTest.cs
    Original file line number Diff line number Diff line change
    @@ -108,7 +108,6 @@ private static void CreateBlockData(Vector3 position, float size, List<Vector3>
    normals.Add(normal);
    }

    // Add indices for 2 triangles (0,1,2 and 2,3,0)
    indices.Add(k);
    indices.Add(k + 1);
    indices.Add(k + 2);
  2. ChrisPritchard created this gist Oct 12, 2025.
    190 changes: 190 additions & 0 deletions CubeTest.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,190 @@
    using System.Collections.Generic;
    using Godot;

    public partial class CubeTest : Node3D
    {
    private MeshInstance3D meshInstance;

    private static readonly Color[] colour_set = [Colors.Red, Colors.Green, Colors.Blue, Colors.Yellow];

    const int tower_width = 20;
    const int tower_height = 200;
    const int tower_depth = 20;
    const float block_size = 1;

    private int rerender_on_process = 0;

    public override void _Ready()
    {
    meshInstance = new MeshInstance3D();
    var material = new StandardMaterial3D()
    {
    VertexColorUseAsAlbedo = true,
    ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded
    };
    meshInstance.MaterialOverride = material;
    AddChild(meshInstance);
    meshInstance.Mesh = DrawTowerWithSurfaceTool();

    GetNode<OptionButton>("%ProcessOptions").ItemSelected += index => rerender_on_process = (int)index;
    GetNode<Button>("%RenderWithST").Pressed += () => meshInstance.Mesh = DrawTowerWithSurfaceTool();
    GetNode<Button>("%RenderWithArrays").Pressed += () => meshInstance.Mesh = DrawTowerWithArrays();
    }

    public override void _Process(double delta)
    {
    if (rerender_on_process == 1)
    meshInstance.Mesh = DrawTowerWithSurfaceTool();
    else if (rerender_on_process == 2)
    meshInstance.Mesh = DrawTowerWithArrays();
    }

    private static ArrayMesh DrawTowerWithArrays()
    {
    var vertices = new List<Vector3>(tower_width * tower_depth * tower_height * 8);
    var colours = new List<Color>(vertices.Count);
    var normals = new List<Vector3>(vertices.Count);
    var indices = new List<int>(vertices.Count);

    for (var x = 0; x < tower_width; x++)
    for (var y = 0; y < tower_height; y++)
    for (var z = 0; z < tower_depth; z++)
    {
    var blockPosition = new Vector3(x * block_size, y * block_size, z * block_size);
    CreateBlockData(blockPosition, block_size, vertices, colours, normals, indices);
    }

    var arrays = new Godot.Collections.Array();
    arrays.Resize((int)Mesh.ArrayType.Max);

    arrays[(int)Mesh.ArrayType.Vertex] = vertices.ToArray();
    arrays[(int)Mesh.ArrayType.Color] = colours.ToArray();
    arrays[(int)Mesh.ArrayType.Normal] = normals.ToArray();
    arrays[(int)Mesh.ArrayType.Index] = indices.ToArray();

    var arrayMesh = new ArrayMesh();
    arrayMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);

    return arrayMesh;
    }

    private static void CreateBlockData(Vector3 position, float size, List<Vector3> vertices, List<Color> colours, List<Vector3> normals, List<int> indices)
    {
    var halfSize = size / 2.0f;

    var corners = new[] {
    new Vector3(-halfSize, -halfSize, -halfSize) + position,
    new Vector3( halfSize, -halfSize, -halfSize) + position,
    new Vector3( halfSize, -halfSize, halfSize) + position,
    new Vector3(-halfSize, -halfSize, halfSize) + position,
    new Vector3(-halfSize, halfSize, -halfSize) + position,
    new Vector3( halfSize, halfSize, -halfSize) + position,
    new Vector3( halfSize, halfSize, halfSize) + position,
    new Vector3(-halfSize, halfSize, halfSize) + position
    };

    var faces = new (Vector3 normal, int[] corner_indices, bool should_render)[]
    {
    (Vector3.Down, new int[] {3, 2, 1, 0}, position.Y <= 0),
    (Vector3.Up, new int[] {4, 5, 6, 7}, position.Y >= (tower_height - 1) * block_size),
    (Vector3.Forward, new int[] {7, 6, 2, 3}, position.Z >= (tower_depth - 1) * block_size),
    (Vector3.Back, new int[] {0, 1, 5, 4}, position.Z <= 0),
    (Vector3.Left, new int[] {4, 7, 3, 0}, position.X <= 0),
    (Vector3.Right, new int[] {1, 2, 6, 5}, position.X >= (tower_width - 1) * block_size)
    };

    foreach (var (normal, corner_indices, should_render) in faces)
    {
    if (!should_render)
    continue;

    var k = vertices.Count;

    var colour_index = 0;
    foreach (var index in corner_indices)
    {
    vertices.Add(corners[index]);
    colours.Add(colour_set[colour_index++]);
    normals.Add(normal);
    }

    // Add indices for 2 triangles (0,1,2 and 2,3,0)
    indices.Add(k);
    indices.Add(k + 1);
    indices.Add(k + 2);
    indices.Add(k + 2);
    indices.Add(k + 3);
    indices.Add(k);
    }
    }

    private static ArrayMesh DrawTowerWithSurfaceTool()
    {
    var st = new SurfaceTool();
    st.Begin(Mesh.PrimitiveType.Triangles);

    var vertexIndex = 0;

    for (var x = 0; x < tower_width; x++)
    for (var y = 0; y < tower_height; y++)
    for (var z = 0; z < tower_depth; z++)
    {
    var blockPosition = new Vector3(x * block_size, y * block_size, z * block_size);
    CreateBlock(st, ref vertexIndex, blockPosition, block_size);
    }

    return st.Commit();
    }

    private static void CreateBlock(SurfaceTool st, ref int vertexIndex, Vector3 position, float size)
    {
    var halfSize = size / 2.0f;

    var corners = new[] {
    new Vector3(-halfSize, -halfSize, -halfSize) + position,
    new Vector3( halfSize, -halfSize, -halfSize) + position,
    new Vector3( halfSize, -halfSize, halfSize) + position,
    new Vector3(-halfSize, -halfSize, halfSize) + position,
    new Vector3(-halfSize, halfSize, -halfSize) + position,
    new Vector3( halfSize, halfSize, -halfSize) + position,
    new Vector3( halfSize, halfSize, halfSize) + position,
    new Vector3(-halfSize, halfSize, halfSize) + position
    };

    var quads = new (Vector3 normal, Vector3[] corner_vertices, bool should_render)[]
    {
    (Vector3.Down, new [] {corners[3], corners[2], corners[1], corners[0]}, position.Y <= 0),
    (Vector3.Up, new [] {corners[4], corners[5], corners[6], corners[7]}, position.Y >= (tower_height - 1) * block_size),
    (Vector3.Forward, new [] {corners[7], corners[6], corners[2], corners[3]}, position.Z >= (tower_depth - 1) * block_size),
    (Vector3.Back, new [] {corners[0], corners[1], corners[5], corners[4]}, position.Z <= 0),
    (Vector3.Left, new [] {corners[4], corners[7], corners[3], corners[0]}, position.X <= 0),
    (Vector3.Right, new [] {corners[1], corners[2], corners[6], corners[5]}, position.X >= (tower_width - 1) * block_size)
    };

    foreach (var quad in quads)
    DrawQuad(st, ref vertexIndex, quad);
    }

    private static void DrawQuad(SurfaceTool st, ref int k, (Vector3 normal, Vector3[] corner_vertices, bool should_render) quad)
    {
    if (!quad.should_render)
    return;

    var colour_index = 0;
    foreach (var corner in quad.corner_vertices)
    {
    st.SetColor(colour_set[colour_index++]);
    st.SetNormal(quad.normal);
    st.AddVertex(corner);
    }

    st.AddIndex(k);
    st.AddIndex(k + 1);
    st.AddIndex(k + 2);
    st.AddIndex(k + 2);
    st.AddIndex(k + 3);
    st.AddIndex(k);

    k += 4;
    }
    }