Last active
October 12, 2025 23:49
-
-
Save ChrisPritchard/2f907fdf743d7362824fbad5da7bbca0 to your computer and use it in GitHub Desktop.
Godot node script to generate cubes using two different approaches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| 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; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment