본문 바로가기
개발/Unity

유니티 - 절차적 메시로 정점이 24개인 큐브 만들기 (Make 24 Vertices Cube with Procedural Mesh)

by 피로물든딸기 2022. 11. 6.
반응형

Unity 전체 링크

 

참고 - 절차적 메시로 정점이 8개인 큐브 만들기

 

절차적 메시를 이용해서 정점이 24개인 큐브를 만들어보자.

Vector3.forward(0, 0, 1)이므로 아래와 같이 방향을 정의한다.

그리고 이 방향 순서대로 쿼드를 6개 만든다.

    public enum Direction
    {
        FORWARD, // (0, 0, +1)
        RIGHT,   // (+1, 0, 0)
        UP,      // (0, +1, 0)
        BACK,    // (0, 0, -1)
        LEFT,    // (-1, 0, 0)
        DOWN     // (0, -1, 0)
    }

 

위의 방향을 기준으로 정점에 번호를 매기면 아래와 같다.

 

따라서 faceNumber는 아래와 같이 정의해둔다.

    public int[][] faceNumber =
    {
        new int[] {0, 1, 2, 3}, // FORWARD
        new int[] {5, 0, 3, 6}, // RIGHT 
        new int[] {5, 4, 1, 0}, // UP     
        new int[] {4, 5, 6, 7}, // BACK  
        new int[] {1, 4, 7, 2}, // LEFT 
        new int[] {3, 2, 7, 6}, // DOWN    
    };

 

유니티 큐브의 mesh를 보면 0 → 1 → 3 / 1 → 2 → 3 순서대로 삼각형을 그리고 있다. 

 

(x, y, z)의 크기를 변경할 수 있도록 size를 Vector3로 선언한다.

public Vector3 size = new Vector3(1.0f, 1.0f, 1.0f);

 

정점이 24개이므로 vertices는 24개의 배열로 만든다.

그리고 위의 그림에 있는 번호대로 v0 ~ v7을 선언하고 vSet을 만든다.

(x, y, z)의 size를 각각 Vector에 곱하기 위해 아다마르 곱(Vector3.Scale)을 이용하였다.

    vertices = new Vector3[24];
        
    Vector3 v0 = Vector3.Scale(new Vector3(+0.5f, +0.5f, +0.5f), size) + offset;
    Vector3 v1 = Vector3.Scale(new Vector3(-0.5f, +0.5f, +0.5f), size) + offset;
    Vector3 v2 = Vector3.Scale(new Vector3(-0.5f, -0.5f, +0.5f), size) + offset;
    Vector3 v3 = Vector3.Scale(new Vector3(+0.5f, -0.5f, +0.5f), size) + offset;
    Vector3 v4 = Vector3.Scale(new Vector3(-0.5f, +0.5f, -0.5f), size) + offset;
    Vector3 v5 = Vector3.Scale(new Vector3(+0.5f, +0.5f, -0.5f), size) + offset;
    Vector3 v6 = Vector3.Scale(new Vector3(+0.5f, -0.5f, -0.5f), size) + offset;
    Vector3 v7 = Vector3.Scale(new Vector3(-0.5f, -0.5f, -0.5f), size) + offset;

    Vector3[] vSet = new Vector3[] {
        v0, v1, v2, v3, v4, v5, v6, v7
    };

 

삼각형이 총 6개의 면에 2개씩 있으므로 triangles는 36개가 필요하다.

 

vertices에 각 방향에 정의된 정점을 넣어주고, 0 → 1 → 3 / 1 → 2 → 3 순서대로 triangles에 넣어준다.

    triangles = new int[3 * 12];

    int vIdx, tIdx;

    vIdx = tIdx = 0;
    for(int dir = 0; dir < 6; dir++)
    {
        vertices[vIdx++] = vSet[faceNumber[dir][0]];
        vertices[vIdx++] = vSet[faceNumber[dir][1]];
        vertices[vIdx++] = vSet[faceNumber[dir][2]];
        vertices[vIdx++] = vSet[faceNumber[dir][3]];

        triangles[tIdx++] = vIdx - 4 + 0;
        triangles[tIdx++] = vIdx - 4 + 1;
        triangles[tIdx++] = vIdx - 4 + 3;

        triangles[tIdx++] = vIdx - 4 + 1;
        triangles[tIdx++] = vIdx - 4 + 2;
        triangles[tIdx++] = vIdx - 4 + 3;
    }

 

큐브는 Mesh Collider보다 Box Collider가 더 적합하므로 BoxCollider를 추가하도록 하자.

    void createProceduralMesh()
    {
        mesh.Clear();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();

        //Destroy(this.GetComponent<MeshCollider>());
        //this.gameObject.AddComponent<MeshCollider>();

        Destroy(this.GetComponent<BoxCollider>());
        this.gameObject.AddComponent<BoxCollider>();
    }

 

이제 빈 오브젝트에 Mesh Renderer, Filter, Default-Material과 스크립트를 추가한다.

 

게임을 실행하면 큐브가 만들어진다. 

 

실제 유니티의 Primitive 큐브와 비교해보자.

그려진 mesh가 모두 일치하는 것을 알 수 있다.

 

전체 코드는 다음과 같다.

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

[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralCube : MonoBehaviour
{
    public Vector3 size = new Vector3(1.0f, 1.0f, 1.0f);
    public Vector3 offset = new Vector3(0, 0, 0);

    Mesh mesh;
    Vector3[] vertices;
    int[] triangles;

    public enum Direction
    {
        FORWARD, // (0, 0, +1)
        RIGHT,   // (+1, 0, 0)
        UP,      // (0, +1, 0)
        BACK,    // (0, 0, -1)
        LEFT,    // (-1, 0, 0)
        DOWN     // (0, -1, 0)
    }

    public int[][] faceNumber =
    {
        new int[] {0, 1, 2, 3}, // FORWARD
        new int[] {5, 0, 3, 6}, // RIGHT 
        new int[] {5, 4, 1, 0}, // UP     
        new int[] {4, 5, 6, 7}, // BACK  
        new int[] {1, 4, 7, 2}, // LEFT 
        new int[] {3, 2, 7, 6}, // DOWN    
    };

    void OnValidate()
    {
        if (mesh == null) return;

        if (size.magnitude > 0 || offset.magnitude > 0)
        {
            setMeshData(size);
            createProceduralMesh();
        }
    }

    void Start()
    {
        mesh = GetComponent<MeshFilter>().mesh;

        setMeshData(size);
        createProceduralMesh();
    }

    void setMeshData(Vector3 size)
    {
        vertices = new Vector3[24];
        
        Vector3 v0 = Vector3.Scale(new Vector3(+0.5f, +0.5f, +0.5f), size) + offset;
        Vector3 v1 = Vector3.Scale(new Vector3(-0.5f, +0.5f, +0.5f), size) + offset;
        Vector3 v2 = Vector3.Scale(new Vector3(-0.5f, -0.5f, +0.5f), size) + offset;
        Vector3 v3 = Vector3.Scale(new Vector3(+0.5f, -0.5f, +0.5f), size) + offset;
        Vector3 v4 = Vector3.Scale(new Vector3(-0.5f, +0.5f, -0.5f), size) + offset;
        Vector3 v5 = Vector3.Scale(new Vector3(+0.5f, +0.5f, -0.5f), size) + offset;
        Vector3 v6 = Vector3.Scale(new Vector3(+0.5f, -0.5f, -0.5f), size) + offset;
        Vector3 v7 = Vector3.Scale(new Vector3(-0.5f, -0.5f, -0.5f), size) + offset;

        Vector3[] vSet = new Vector3[] {
            v0, v1, v2, v3, v4, v5, v6, v7
        };

        triangles = new int[3 * 12];

        int vIdx, tIdx;

        vIdx = tIdx = 0;
        for(int dir = 0; dir < 6; dir++)
        {
            vertices[vIdx++] = vSet[faceNumber[dir][0]];
            vertices[vIdx++] = vSet[faceNumber[dir][1]];
            vertices[vIdx++] = vSet[faceNumber[dir][2]];
            vertices[vIdx++] = vSet[faceNumber[dir][3]];

            triangles[tIdx++] = vIdx - 4 + 0;
            triangles[tIdx++] = vIdx - 4 + 1;
            triangles[tIdx++] = vIdx - 4 + 3;

            triangles[tIdx++] = vIdx - 4 + 1;
            triangles[tIdx++] = vIdx - 4 + 2;
            triangles[tIdx++] = vIdx - 4 + 3;
        }
    }

    void createProceduralMesh()
    {
        mesh.Clear();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();

        //Destroy(this.GetComponent<MeshCollider>());
        //this.gameObject.AddComponent<MeshCollider>();

        Destroy(this.GetComponent<BoxCollider>());
        this.gameObject.AddComponent<BoxCollider>();
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글