Advertisement
JontePonte

Marching cubes duplicate vertices

Sep 14th, 2024
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.68 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. public class MeshGenerator : MonoBehaviour
  6. {
  7.     public Vector3Int chunkDimensions;
  8.  
  9.     public Material material;
  10.  
  11.     public int brushSize;
  12.     public float brushStrength;
  13.  
  14.     [Header("Noise Settings")]
  15.     public float heightMultiplier;
  16.     public float frequency;
  17.     public float lacunarity;
  18.     public float persistance;
  19.     public int octaves;
  20.     public float density;
  21.  
  22.     float[,,][] voxelGrid;
  23.  
  24.     List<Vector3> vertices;
  25.  
  26.     int[] caseToNumpolys;
  27.     int[][] triTable;
  28.  
  29.     Vector3[] cornerOffset;
  30.  
  31.     Camera cam;
  32.  
  33.     private void Start()
  34.     {
  35.         Initialize();
  36.         CreateVoxelGrid();
  37.         GenerateMesh();
  38.     }
  39.  
  40.     private void OnValidate()
  41.     {
  42.         Initialize();
  43.         CreateVoxelGrid();
  44.         GenerateMesh();
  45.     }
  46.  
  47.     private void Update()
  48.     {
  49.         if (Input.GetMouseButton(1))
  50.         {
  51.             Ray ray = cam.ScreenPointToRay(Input.mousePosition);
  52.  
  53.             if (Physics.Raycast(ray, out RaycastHit hit))
  54.             {
  55.                 ModifyTerrain(new Vector3Int((int)hit.point.x, (int)hit.point.y, (int)hit.point.z), true);
  56.             }
  57.         }
  58.  
  59.         if (Input.GetMouseButton(0))
  60.         {
  61.             Ray ray = cam.ScreenPointToRay(Input.mousePosition);
  62.  
  63.             if (Physics.Raycast(ray, out RaycastHit hit))
  64.             {
  65.                 ModifyTerrain(new Vector3Int((int)hit.point.x, (int)hit.point.y, (int)hit.point.z), false);
  66.             }
  67.         }
  68.     }
  69.  
  70.     void Initialize()
  71.     {
  72.         caseToNumpolys = Tables.caseToNumPolys;
  73.         triTable = Tables.triTable;
  74.  
  75.         cam = Camera.main;
  76.  
  77.         cornerOffset = new Vector3[]
  78.         {
  79.             new Vector3(0, 0, 0),
  80.             new Vector3(0, 1, 0),
  81.             new Vector3(1, 1, 0),
  82.             new Vector3(1, 0, 0),
  83.             new Vector3(0, 0, 1),
  84.             new Vector3(0, 1, 1),
  85.             new Vector3(1, 1, 1),
  86.             new Vector3(1, 0, 1),
  87.  
  88.         };
  89.     }
  90.  
  91.     void GenerateMesh()
  92.     {
  93.         vertices = new();
  94.  
  95.         for (int z = 0; z < chunkDimensions.z; z++)
  96.         {
  97.             for (int y = 0; y < chunkDimensions.y; y++)
  98.             {
  99.                 for (int x = 0; x < chunkDimensions.x; x++)
  100.                 {
  101.                     AddVertices(x, y, z);
  102.                 }
  103.             }
  104.         }
  105.  
  106.         int[] indices = new int[vertices.Count];
  107.  
  108.         for (int i = 0; i < vertices.Count; i++)
  109.         {
  110.             indices[i] = i;
  111.         }
  112.  
  113.         Mesh mesh = new Mesh();
  114.         mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  115.  
  116.         mesh.vertices = vertices.ToArray();
  117.         mesh.triangles = indices;
  118.  
  119.         mesh.RecalculateNormals();
  120.  
  121.         if (GameObject.Find("Mesh") == null)
  122.         {
  123.             GameObject obj = new GameObject("Mesh");
  124.             obj.transform.parent = transform;
  125.             obj.AddComponent<MeshFilter>().mesh = mesh;
  126.             obj.AddComponent<MeshRenderer>().material = material;
  127.             obj.AddComponent<MeshCollider>();
  128.         }
  129.  
  130.         else
  131.         {
  132.             var mf = GameObject.Find("Mesh").GetComponent<MeshFilter>();
  133.             var collider = GameObject.Find("Mesh").GetComponent<MeshCollider>();
  134.  
  135.             mf.mesh = mesh;
  136.             collider.sharedMesh = null;
  137.             collider.sharedMesh = mesh;
  138.         }
  139.  
  140.     }
  141.  
  142.     void AddVertices(int x, int y, int z)
  143.     {
  144.         int triCase = CalculateCase(voxelGrid[x, y, z]);
  145.         int[] edges = triTable[triCase];
  146.  
  147.         for (int j = 0, i = 0; j < caseToNumpolys[triCase]; j++, i += 3)
  148.         {
  149.             vertices.AddRange(new List<Vector3>
  150.             {
  151.                 GetEdgePosition(edges[i + 2], x, y, z),
  152.                 GetEdgePosition(edges[i + 1], x, y, z),
  153.                 GetEdgePosition(edges[i], x, y, z)
  154.             });
  155.         }
  156.     }
  157.  
  158.     Vector3 GetEdgePosition(int edge, int x, int y, int z)
  159.     {
  160.         float[] voxelData = voxelGrid[x, y, z];
  161.  
  162.         switch (edge)
  163.         {
  164.             case 0: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[1], 0, 1, 0);
  165.             case 1: return GetInterpolatedEdgePosition(x, y + 1, z, voxelData[1], voxelData[2], 1, 0, 0);
  166.             case 2: return GetInterpolatedEdgePosition(x + 1, y, z, voxelData[3], voxelData[2], 0, 1, 0);
  167.             case 3: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[3], 1, 0, 0);
  168.  
  169.             case 4: return GetInterpolatedEdgePosition(x, y, z + 1, voxelData[4], voxelData[5], 0, 1, 0);
  170.             case 5: return GetInterpolatedEdgePosition(x, y + 1, z + 1, voxelData[5], voxelData[6], 1, 0, 0);
  171.             case 6: return GetInterpolatedEdgePosition(x + 1, y, z + 1, voxelData[7], voxelData[6], 0, 1, 0);
  172.             case 7: return GetInterpolatedEdgePosition(x, y, z + 1, voxelData[4], voxelData[7], 1, 0, 0);
  173.  
  174.             case 8: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[4], 0, 0, 1);
  175.             case 9: return GetInterpolatedEdgePosition(x, y + 1, z, voxelData[1], voxelData[5], 0, 0, 1);
  176.             case 10: return GetInterpolatedEdgePosition(x + 1, y + 1, z, voxelData[2], voxelData[6], 0, 0, 1);
  177.             case 11: return GetInterpolatedEdgePosition(x + 1, y, z, voxelData[3], voxelData[7], 0, 0, 1);
  178.  
  179.             default: throw new ArgumentOutOfRangeException(nameof(edge), "Invalid edge index: " + edge);
  180.         }
  181.     }
  182.  
  183.     // Helper method to calculate interpolated edge positions
  184.     Vector3 GetInterpolatedEdgePosition(int x, int y, int z, float voxelA, float voxelB, int offsetX, int offsetY, int offsetZ)
  185.     {
  186.         float interpolation = Interpolate(voxelA, voxelB);
  187.  
  188.         return new Vector3(x + offsetX * interpolation, y + offsetY * interpolation, z + offsetZ * interpolation);
  189.     }
  190.  
  191.     float Interpolate(float a, float b)
  192.     {
  193.         return (0 - a) / (b - a);
  194.     }
  195.  
  196.     int CalculateCase(float[] voxelData)
  197.     {
  198.         int triCase = 0;
  199.  
  200.         for (int i = 0; i < 8; i++)
  201.         {
  202.             int bit = voxelData[i] < 0 ? 1 : 0;
  203.             triCase |= bit << i;
  204.         }
  205.  
  206.         return triCase;
  207.     }
  208.  
  209.     private void ModifyTerrain(Vector3Int vertex, bool addTerrain)
  210.     {
  211.         for (int zOffset = -brushSize; zOffset <= brushSize; zOffset++)
  212.         {
  213.             for (int yOffset = -brushSize; yOffset <= brushSize; yOffset++)
  214.             {
  215.                 for (int xOffset = -brushSize; xOffset <= brushSize; xOffset++)
  216.                 {
  217.                     bool xOutOfRange = vertex.x + xOffset < 0 || vertex.x + xOffset > chunkDimensions.x - 1;
  218.                     bool yOutOfRange = vertex.y + yOffset < 0 || vertex.y + yOffset > chunkDimensions.y - 1;
  219.                     bool zOutOfRange = vertex.z + zOffset < 0 || vertex.z + zOffset > chunkDimensions.z - 1;
  220.  
  221.                     if (xOutOfRange || yOutOfRange || zOutOfRange)
  222.                     {
  223.                         continue;
  224.                     }
  225.  
  226.                     float[] voxel = voxelGrid[vertex.x + xOffset, vertex.y + yOffset, vertex.z + zOffset];
  227.  
  228.                     for (int i = 0; i < 8; i++)
  229.                     {
  230.                         int x = (int)(vertex.x + xOffset + cornerOffset[i].x);
  231.                         int y = (int)(vertex.y + yOffset + cornerOffset[i].y);
  232.                         int z = (int)(vertex.z + zOffset + cornerOffset[i].z);
  233.  
  234.                         float dist = Vector3.Distance(vertex, new Vector3(x, y, z));
  235.  
  236.                         if (dist < brushSize)
  237.                         {
  238.                             voxel[i] = addTerrain ? voxel[i] + brushStrength : voxel[i] - brushStrength;
  239.                         }
  240.                     }
  241.                 }
  242.             }
  243.         }
  244.  
  245.         GenerateMesh();
  246.  
  247.     }
  248.  
  249.     public void CreateVoxelGrid()
  250.     {
  251.         voxelGrid = new float[chunkDimensions.x, chunkDimensions.y, chunkDimensions.z][];
  252.  
  253.         for (int z = 0; z < chunkDimensions.z; z++)
  254.         {
  255.             for (int x = 0; x < chunkDimensions.x; x++)
  256.             {
  257.                 //The noise value at all 8 corners
  258.                 float[] noise = new float[8];
  259.  
  260.                 for (int i = 0; i < 8; i++)
  261.                 {
  262.                     noise[i] = FractalNoise((int)(x + cornerOffset[i].x), (int)(z + cornerOffset[i].z));
  263.                 }
  264.  
  265.                 for (int y = 0; y < chunkDimensions.y; y++)
  266.                 {
  267.                     CreateVoxel(new Vector3Int(x, y, z), noise);
  268.                 }
  269.             }      
  270.         }
  271.     }
  272.  
  273.     public void CreateVoxel(Vector3Int pos, float[] noise)
  274.     {
  275.         voxelGrid[pos.x, pos.y, pos.z] = new float[8];
  276.  
  277.         for (int i = 0; i < 8; i++)
  278.         {
  279.             Vector3 cornerPos = pos + cornerOffset[i];
  280.             voxelGrid[pos.x, pos.y, pos.z][i] = (-cornerPos.y + noise[i]) * density + Fractal3DNoise(cornerPos);
  281.         }
  282.     }
  283.  
  284.     float FractalNoise(int x, int y)
  285.     {
  286.         float noise = 0;
  287.  
  288.         for (int i = 0; i < octaves; i++)
  289.         {
  290.             float frequency = Mathf.Pow(lacunarity, i) * this.frequency;
  291.             float amplitude = Mathf.Pow(persistance, i);
  292.  
  293.             noise += Mathf.PerlinNoise(x * frequency, y * frequency) * amplitude;
  294.         }
  295.  
  296.         return noise * heightMultiplier;
  297.     }
  298.  
  299.     float Fractal3DNoise(Vector3 coord)
  300.     {
  301.         float noise = 0;
  302.  
  303.         for (int i = 0; i < octaves; i++)
  304.         {
  305.             float frequency = Mathf.Pow(lacunarity, i) * this.frequency;
  306.             float amplitude = Mathf.Pow(persistance, i);
  307.  
  308.             noise += Perlin.Noise(coord.x * frequency, coord.y * frequency, coord.z * frequency) * amplitude;
  309.         }
  310.  
  311.         return noise - density;
  312.     }
  313. }
  314.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement