0

Trying to generate a sphere made of cubes like so: from google

This was easy, but any sphere of significant sizes has obvious performance issues if each cube is a separate gameobject, especially if the sphere is solid. After some digging, the answer appears to be to create a single mesh from only the exposed edges. Okay, makes sense. Found several videos, this one was the best I could find: link

I was able to customize the script for my needs, but I keep getting what appears to be a 2x2x2 cube regardless of the diameter input. I should be getting a hollow sphere made of a single mesh.

I suspect the issue is that I am not attaching the script to the correct type of gameobject, am missing a needed component on the gameobject, or because I skipped the UV portion.

Can anyone offer some advice?

Code:

public class PlanetGeneration : MonoBehaviour
{
    private int planetDia = 30;
    private int planetRadius;

    //array of bools to keep track if each space is solid(true) or void(false)
    private bool[,,] currentPlanet;

    void Start()
    {
        currentPlanet = new bool[planetDia, planetDia, planetDia];

        planetRadius = planetDia / 2 + 1;

        GeneratePlanet();
        GenerateMesh(); 
    }

    //define each block as void or solid
    public void GeneratePlanet()
    {
        Vector3 planetCenter = new(planetRadius, planetRadius, planetRadius);
        Vector3 currentPoint;

        for (int x = 0; x < planetDia; x++)
        {
            for (int y = 0; y < planetDia; y++)
            {
                for (int z = 0; z < planetDia; z++)
                {
                    //set the current position
                    currentPoint = new Vector3(x, y, z);

                    //check if this point is within the desired planet size
                    if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
                    {
                        //make it a solid block
                        currentPlanet[x, y, z] = true;
                    }
                    //make it void
                    else currentPlanet[x, y, z] = false;
                }
            }
        }
    }

    //generate mesh
    public void GenerateMesh()
    {
        List<Vector3> Vertices = new List<Vector3>();
        List<int> Triangles = new List<int>();

        for (int x = 0; x < planetDia; x++)
        {
            for (int y = 0; y < planetDia; y++)
            {
                for (int z = 0; z < planetDia; z++)
                {
                    //the 8 corners of a cube
                    Vector3[] VertexPos = new Vector3[8]
                    {
                        new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
                        new Vector3(1, 1, 1), new Vector3(1, 1, -1),
                        new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
                        new Vector3(1, -1, 1), new Vector3(1, -1, -1),
                    };

                    //first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction
                    int[,] Faces = new int[6, 7]{
                        {0, 1, 2, 3, 0, 1, 0},     //top
                        {7, 6, 5, 4, 0, -1, 0},   //bottom
                        {2, 1, 5, 6, 0, 0, 1},     //right
                        {0, 3, 7, 4, 0, 0, -1},   //left
                        {3, 2, 6, 7, 1, 0, 0},    //front
                        {1, 0, 4, 5, -1, 0, 0}    //back
                    };


                    //if this block is solid
                    if (currentPlanet[x, y, z] == true)
                    {
                        //for each face of the block
                        for (int i = 0; i < 6; i++)
                        {
                            //check if it is on the edge of the overall array
                            //This part is dumb and needs to be removed. Keeping for now to prevent the array out of bounds error.
                            if(x + Faces[i, 4] == -1 || y + Faces[i, 5] == -1 || z + Faces[i, 6] == -1 ||
                                x + Faces[i, 4] == planetDia || y + Faces[i, 5] == planetDia || z + Faces[i, 6] == planetDia)
                            {
                                //do nothing
                            }

                            //check if the block next to it is void
                            else if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
                            {
                                AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count);
                            }
                        }
                    }

                    void AddQuad(int ai, int bi, int ci, int di, int i)
                    {
                        Vector3 a = VertexPos[ai];
                        Vector3 b = VertexPos[bi];
                        Vector3 c = VertexPos[ci];
                        Vector3 d = VertexPos[di];

                        Vertices.AddRange(new List<Vector3>() { a, b, c, d });
                        Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 });
                    }
                }
            }
        }

        this.gameObject.GetComponent<MeshFilter>().mesh = new Mesh()
        {
            vertices = Vertices.ToArray(),
            triangles = Triangles.ToArray(),
        };

    }
}
plasmas222
  • 25
  • 5

1 Answers1

0

After some more testing, and finding the youtube video authors updated code on github, I made several corrections and got it working. Code below in case anyone wants to reference.

Also of note - make a blank game object, attach the script, add a mesh filter and mesh renderer, set the material in the renderer.

This is a good starting point for anyone looking for this basic idea.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;


public class PlanetGeneration : MonoBehaviour
{
    //must be an odd number
    private int planetDia = 39;
    private int planetRadius;

    //array of bools to keep track if each space is solid(true) or void(false)
    private bool[,,] currentPlanet;

    void Start()
    {
        currentPlanet = new bool[planetDia, planetDia, planetDia];

        planetRadius = planetDia / 2 - 1;

        GeneratePlanet();
        GenerateMesh(); 
    }

    //define each block as void or solid
    public void GeneratePlanet()
    {
        Vector3 planetCenter = new(planetRadius + 1, planetRadius + 1, planetRadius + 1);
        Vector3 currentPoint;

        for (int x = 0; x < planetDia; x++)
        {
            for (int y = 0; y < planetDia; y++)
            {
                for (int z = 0; z < planetDia; z++)
                {
                    //set the current position
                    currentPoint = new Vector3(x, y, z);

                    //check if this point is within the desired planet size
                    if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
                    {
                        //make it a solid block
                        currentPlanet[x, y, z] = true;
                    }
                    //make it void
                    else currentPlanet[x, y, z] = false;
                }
            }
        }
    }

    //generate mesh
    public void GenerateMesh()
    {
        List<int> Triangles = new();
        List<Vector3> Verticies = new();
        List<Vector2> uv = new();


        for (int x = 1; x < planetDia; x++)
        {
            for (int y = 1; y < planetDia; y++)
            {
                for (int z = 1; z < planetDia; z++)
                {
                    //the 8 corners of a cube
                    Vector3[] VertexPos = new Vector3[8]
                    {
                        new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
                        new Vector3(1, 1, 1), new Vector3(1, 1, -1),
                        new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
                        new Vector3(1, -1, 1), new Vector3(1, -1, -1),
                    };

                    //first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction, the last two are for the UV
                    int[,] Faces = new int[6, 9]{
                        {0, 1, 2, 3, 0, 1, 0, 0, 0},     //top
                        {7, 6, 5, 4, 0, -1, 0, 1, 0},   //bottom
                        {2, 1, 5, 6, 0, 0, 1, 1, 1},     //right
                        {0, 3, 7, 4, 0, 0, -1,  1, 1},   //left
                        {3, 2, 6, 7, 1, 0, 0,  1, 1},    //front
                        {1, 0, 4, 5, -1, 0, 0,  1, 1}    //back
                    };


                    //if this block is solid
                    if (currentPlanet[x, y, z] == true)
                    {
                        //for each face of the block
                        for (int i = 0; i < 6; i++)
                        {                        

                            //check if the block next to it is void                           
                            if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
                            {                                
                                AddQuad(i, Verticies.Count);
                            }
                        }
                    }

                    void AddQuad(int facenum, int v)
                    {
                        // Add Mesh
                        for (int i = 0; i < 4; i++) Verticies.Add(new Vector3(x, y, z) + VertexPos[Faces[facenum, i]] / 2f);
                        Triangles.AddRange(new List<int>() { v, v + 1, v + 2, v, v + 2, v + 3 });

                        // Add uvs
                        Vector2 bottomleft = new Vector2(Faces[facenum, 7], Faces[facenum, 8]) / 2f;

                        
                        uv.AddRange(new List<Vector2>() { bottomleft + new Vector2(0, 0.5f), bottomleft + new Vector2(0.5f, 0.5f), bottomleft + new Vector2(0.5f, 0), bottomleft });
                    }
                }
            }
        }

        GetComponent<MeshFilter>().mesh = new Mesh()
        {
            vertices = Verticies.ToArray(),
            triangles = Triangles.ToArray(),
            uv = uv.ToArray()
        };
    }    
}
plasmas222
  • 25
  • 5