0
\$\begingroup\$

I need to create 2 meshes procedurally and I don't know very well what order to give the vertices to show them correctly, is there any app/utility to help me establish those points?

Images:

Sapling:

#1

I need to cross the two quads, but they are parallel. I need to show this:

...

Button:

#2

I need to scale the cube correctly in order to show this:

...

Doing this on paper is complicated, so I ask for some utility to help me mark the positions of the vertices and the names of the faces to take care of the references I need to use.

This is my code:

MeshData.cs

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

namespace UnityCraft.Voxel3D
{
    [Serializable]
    public class MeshData
    {
        [SerializeField]
        protected List<Vector3> vertices = new List<Vector3>();

        [SerializeField]
        protected List<List<int>> triangles = new List<List<int>>();

        [SerializeField]
        protected List<Vector2> uv = new List<Vector2>();

        public virtual int SubMeshCount
        {
            get => triangles.Count;
            set
            {
                if (value > triangles.Count)
                {
                    int difference = value - triangles.Count;
                    for (int i = 0; i < difference; i++)
                    {
                        triangles.Add(new List<int>());
                    }
                }
                else if (triangles.Count > value)
                {
                    int difference = triangles.Count - value;
                    for (int i = 0; i < difference; i++)
                    {
                        triangles.RemoveAt(triangles.Count - i);
                    }
                }
            }
        }

        public virtual List<Vector3> Vertices => vertices;

        public virtual List<List<int>> Triangles => triangles;

        public virtual List<Vector2> UV => uv;

        public MeshData() : this(1)
        {
        }

        public MeshData(int subMeshCount)
        {
            SubMeshCount = subMeshCount;
        }

        public virtual void AddVertex(Vector3 vertex)
        {
            vertices.Add(vertex);
        }

        public virtual void AddTriangle(int subMesh, int triangle)
        {
            if (subMesh >= triangles.Count)
            {
                return;
            }
            triangles[subMesh].Add(triangle);
        }

        public virtual void AddUV(Vector2 uv)
        {
            this.uv.Add(uv);
        }

        public virtual void AddCube(Vector3 position, Rect[] uvs)
        {
            AddCube(position, 0, uvs);
        }

        public virtual void AddCube(Vector3 position, int subMesh, Rect[] uvs)
        {
            AddQuad(position, subMesh, VoxelDirection.Forward, uvs[0]);
            AddQuad(position, subMesh, VoxelDirection.Back, uvs[1]);
            AddQuad(position, subMesh, VoxelDirection.Right, uvs[2]);
            AddQuad(position, subMesh, VoxelDirection.Left, uvs[3]);
            AddQuad(position, subMesh, VoxelDirection.Up, uvs[4]);
            AddQuad(position, subMesh, VoxelDirection.Down, uvs[5]);
        }

        public virtual void AddScaledCube(Vector3 position, int subMesh, Rect[] uvs, Vector3 scale)
        {
            AddQuad(position, subMesh, VoxelDirection.Forward, uvs[0], scale);
            AddQuad(position, subMesh, VoxelDirection.Back, uvs[1], scale);
            AddQuad(position, subMesh, VoxelDirection.Right, uvs[2], scale);
            AddQuad(position, subMesh, VoxelDirection.Left, uvs[3], scale);
            AddQuad(position, subMesh, VoxelDirection.Up, uvs[4], scale);
            AddQuad(position, subMesh, VoxelDirection.Down, uvs[5], scale);
        }

        public virtual void AddScaledCube(Vector3 position, int subMesh, Rect[] uvs, Vector3[] scales)
        {
            AddQuad(position, subMesh, VoxelDirection.Forward, uvs[0], scales[0]);
            AddQuad(position, subMesh, VoxelDirection.Back, uvs[1], scales[1]);
            AddQuad(position, subMesh, VoxelDirection.Right, uvs[2], scales[2]);
            AddQuad(position, subMesh, VoxelDirection.Left, uvs[3], scales[3]);
            AddQuad(position, subMesh, VoxelDirection.Up, uvs[4], scales[4]);
            AddQuad(position, subMesh, VoxelDirection.Down, uvs[5], scales[5]);
        }

        public virtual void AddCross(Vector3 position, int subMesh, params Rect[] uvs)
        {
            AddQuad(position, VoxelDirection.Diagonal1, uvs[0]);
            AddQuad(position, VoxelDirection.Diagonal2, uvs[1]);
        }

        public virtual void AddQuad(Vector3 position, VoxelDirection direction, Rect uv)
        {
            AddQuad(position, 0, direction, uv);
        }

        public virtual void AddQuad(Vector3 position, int subMesh, VoxelDirection direction, Rect uv, Vector3 scale = default)
        {
            if (scale == default)
                scale = Vector3.one;

            switch (direction)
            {
                case VoxelDirection.Forward:
                    vertices.Add((position + Vector3.forward + Vector3.left) * scale.z);
                    vertices.Add((position + Vector3.forward) * scale.z);
                    vertices.Add((position + Vector3.forward + Vector3.left + Vector3.up) * scale.z);
                    vertices.Add((position + Vector3.forward + Vector3.up) * scale.z);
                    break;

                case VoxelDirection.Back:
                    vertices.Add(position * scale.z);
                    vertices.Add((position + Vector3.left) * scale.x);
                    vertices.Add((position + Vector3.up) * scale.x);
                    vertices.Add((position + Vector3.left + Vector3.up) * scale.x);
                    break;

                case VoxelDirection.Right:
                    vertices.Add((position + Vector3.forward) * scale.x);
                    vertices.Add(position * scale.x);
                    vertices.Add((position + Vector3.forward + Vector3.up) * scale.x);
                    vertices.Add((position + Vector3.up) * scale.x);
                    break;

                case VoxelDirection.Left:
                    vertices.Add((position + Vector3.left) * scale.x);
                    vertices.Add((position + Vector3.left + Vector3.forward) * scale.x);
                    vertices.Add((position + Vector3.left + Vector3.up) * scale.x);
                    vertices.Add((position + Vector3.left + Vector3.forward + Vector3.up) * scale.x);
                    break;

                case VoxelDirection.Up:
                    vertices.Add((position + Vector3.up) * scale.y);
                    vertices.Add((position + Vector3.up + Vector3.left) * scale.y);
                    vertices.Add((position + Vector3.up + Vector3.forward) * scale.y);
                    vertices.Add((position + Vector3.up + Vector3.forward + Vector3.left) * scale.y);
                    break;

                case VoxelDirection.Down:
                    vertices.Add((position + Vector3.forward) * scale.y);
                    vertices.Add((position + Vector3.forward + Vector3.left) * scale.y);
                    vertices.Add(position * scale.y);
                    vertices.Add((position + Vector3.left) * scale.y);
                    break;

                case VoxelDirection.Diagonal1:
                    vertices.Add(position);
                    vertices.Add(position + Vector3.up);
                    vertices.Add(position + Vector3.right + Vector3.forward);
                    vertices.Add(position + Vector3.one);
                    break;

                case VoxelDirection.Diagonal2:
                    vertices.Add(position + Vector3.forward);
                    vertices.Add(position + Vector3.forward + Vector3.up);
                    vertices.Add(position + Vector3.left);
                    vertices.Add(position + Vector3.left + Vector3.up);
                    break;
            }

            triangles[subMesh].Add(vertices.Count - 4);
            triangles[subMesh].Add(vertices.Count - 3);
            triangles[subMesh].Add(vertices.Count - 2);
            triangles[subMesh].Add(vertices.Count - 3);
            triangles[subMesh].Add(vertices.Count - 1);
            triangles[subMesh].Add(vertices.Count - 2);

            this.uv.Add(new Vector2(uv.x + uv.width, uv.y));
            this.uv.Add(new Vector2(uv.x, uv.y));
            this.uv.Add(new Vector2(uv.x + uv.width, uv.y + uv.height));
            this.uv.Add(new Vector2(uv.x, uv.y + uv.height));
        }

        public virtual Mesh BuildMesh(string name = "")
        {
            // Debug.Log($"Creating mesh {name}! (Reviewing stacktrace)");

            Mesh mesh = new Mesh
            {
                subMeshCount = triangles.Count,
                vertices = vertices.ToArray(),
                name = name
            };

            for (int i = 0; i < triangles.Count; i++)
            {
                mesh.SetTriangles(triangles[i], i, true);
            }

            mesh.uv = uv.ToArray();
            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
            mesh.RecalculateTangents();

            return mesh;
        }
    }
}

CustomRenderer.cs

using System.Collections;
using System.Collections.Generic;
using BayatGames.Frameworks.Voxel3D;
using UnityEngine;

namespace UnityCraft.Tests
{
    public class CustomRenderers : MonoBehaviour
    {
        private MeshData saplingMeshData;
        private MeshData buttonMeshData;
        private MeshData levelMeshData;

        private static Rect[] NullRects => new[]
        {
            Rect.zero,
            Rect.zero,
            Rect.zero,
            Rect.zero,
            Rect.zero,
            Rect.zero
        };

        // Start is called before the first frame update
        private void Start()
        {
            saplingMeshData = new MeshData();
            buttonMeshData = new MeshData();
            levelMeshData = new MeshData();

            saplingMeshData.AddCross(Vector3.zero, 0, Rect.zero, Rect.zero);

            buttonMeshData.AddScaledCube(Vector3.zero, 0, NullRects, new Vector3(.4f, .2f, .3f));

            saplingMeshData.CreateGameObject("Sapling");
            buttonMeshData.CreateGameObject("Button");
            //levelMeshData.CreateGameObject("Level");
        }

        // Update is called once per frame
        private void Update()
        {
        }
    }

    public static class F
    {
        public static GameObject CreateGameObject(this MeshData data, string name)
        {
            var mesh = data.BuildMesh(name);

            var gameObject = new GameObject(name);

            var meshFilter = gameObject.AddComponent<MeshFilter>();
            meshFilter.mesh = mesh;

            var meshRenderer = gameObject.AddComponent<MeshRenderer>();

            return gameObject;
        }
    }
}
```
\$\endgroup\$
  • \$\begingroup\$ Why do them procedurally? They are very simple shapes, it would be much more efficient to just create them in a regular 3d modelling program and import the vertices. You won't really get anything between doing that and doing it on paper \$\endgroup\$ – Bálint Oct 29 '19 at 8:59
  • \$\begingroup\$ But I need to do them procedurally because they are part of a Voxel Engine. \$\endgroup\$ – z3nth10n Oct 29 '19 at 9:57
  • \$\begingroup\$ If you do them procedurally, you'll end up with a Mesh. If you load them in, you'll also end up with a mesh. There's really no difference, voxel engines don't need to be procedural in every sense. \$\endgroup\$ – Bálint Oct 29 '19 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.