Browse Source

Added voxel scale to marching cubes

James Peret 1 year ago
parent
commit
cc88ce76a1

+ 23 - 26
Runtime/ChunkTerrainGenerator.cs

@@ -4,6 +4,7 @@ using UnityEngine;
 using KairoEngine.Core;
 using KairoEngine.Chunks;
 using KairoEngine.NoiseUtilities;
+using KairoEngine.UniverseGenerator;
 using Sirenix.OdinInspector;
 using UniRx;
 
@@ -12,41 +13,44 @@ namespace KairoEngine.TerrainEngine
     [HideMonoScript]
     public class ChunkTerrainGenerator : MonoBehaviour
     {
-        public MarchingCubes3 marchingCubes3;
+        public MarchingCubes4 marchingCubes3;
         public MarchingCubes2 marchingCubes2;
+        public Vector3 voxelSize = new Vector3(1f, 1f, 1f);
         public Vector3Int chunkSize = new Vector3Int(16, 16, 16);
         public Vector3Int chunkLimit = new Vector3Int(2, 1, 2);
         //public float noiseMultiplier = 1.5f;
-        [InlineProperty, HideLabel, FoldoutGroup("Noise Generator")] public NoiseCombinator noiseCombinator = new NoiseCombinator();
+        [InlineEditor(InlineEditorObjectFieldModes.Foldout)] public PlanetTemplate planetTemplate;
 
-        [HideInInspector] public ChunkSystem<BlockBase> chunkSystem;
+        public ChunkSystem<BlockBase> chunkSystem;
 
         [Button("Generate"), ButtonGroup("Buttons")]
         void CreateChunks()
         {
-            if(noiseCombinator == null)
+            if(planetTemplate == null)
             {
-                Debug.LogError("Missing Noise Combinator", this.gameObject);
+                Debug.LogError("Missing planet template", this.gameObject);
                 return;
             }
             DestroyTerrain();
             Debug.Log("Starting chunk generation");
-            chunkSystem = new ChunkSystem<BlockBase>(chunkSize, true);
+            chunkSystem = new ChunkSystem<BlockBase>(chunkSize,voxelSize, true);
             for (int x = 0; x < chunkLimit.x; x++) {
                 for (int z = 0; z < chunkLimit.z; z++) {
                     for (int y = 0; y < chunkLimit.y; y++) {
+                        Vector3 chunkGlobalPos = new Vector3(x * chunkSize.x * voxelSize.x, y * chunkSize.y * voxelSize.y, z * chunkSize.z * voxelSize.z);
                         Vector3Int chunkPos = new Vector3Int(x * chunkSize.x, y * chunkSize.y, z * chunkSize.z);
-                        chunkSystem.CreateChunk(chunkPos, (Chunk<BlockBase> c, Vector3Int pos) => new BlockBase(0, 0));
-                        GenerateChunkTerrain(chunkPos);
+                        chunkSystem.CreateChunk(chunkGlobalPos, chunkPos, (Chunk<BlockBase> c, Vector3Int pos) => new BlockBase(0, 0));
+                        GenerateChunkTerrain(chunkGlobalPos);
                     }
                 }
             }
             for (int x = 0; x < chunkLimit.x; x++) {
                 for (int z = 0; z < chunkLimit.z; z++) {
                     for (int y = 0; y < chunkLimit.y; y++) {
-                        Vector3Int chunkPos = new Vector3Int(x * chunkSize.x, y * chunkSize.y, z * chunkSize.z);
+                        //Vector3Int chunkPos = new Vector3Int(x * chunkSize.x, y * chunkSize.y, z * chunkSize.z);
+                        Vector3 chunkPos = new Vector3(x * chunkSize.x * voxelSize.x, y * chunkSize.y * voxelSize.y, z * chunkSize.z * voxelSize.z);
                         if(marchingCubes3 != null) marchingCubes3.Generate(chunkSystem, chunkPos);
-                        else if(marchingCubes2 != null) marchingCubes2.Generate(chunkSystem, chunkPos);
+                        //else if(marchingCubes2 != null) marchingCubes2.Generate(chunkSystem, chunkPos);
                     }
                 }
             }
@@ -55,21 +59,22 @@ namespace KairoEngine.TerrainEngine
             #endif
         }
 
-        void GenerateChunkTerrain(Vector3Int initialPos)
+        void GenerateChunkTerrain(Vector3 initialPos)
         {
-            float width = (float)(chunkLimit.x * chunkSize.x);
-            float height = (float)(chunkLimit.y * chunkSize.y);
-            float length = (float)(chunkLimit.z * chunkSize.z);
+            float width = (float)(chunkLimit.x * chunkSize.x * voxelSize.x);
+            float height = (float)(chunkLimit.y * chunkSize.y * voxelSize.y);
+            float length = (float)(chunkLimit.z * chunkSize.z * voxelSize.z);
             for (int x = 0; x < chunkSize.x; x++) {
                 for (int z = 0; z < chunkSize.z; z++) {
                     for (int y = 0; y < chunkSize.y; y++) {
                         // Get a terrain height using regular old Perlin noise.
-                        Vector3Int pos = initialPos + new Vector3Int(x, y, z);
+                        Vector3 pos = initialPos + new Vector3(x * voxelSize.x, y * voxelSize.y, z * voxelSize.z);
                         // Old example noise:
                         //float thisHeight = height * Mathf.PerlinNoise((float)pos.x / width * noiseMultiplier + 0.001f, (float)pos.z / length * noiseMultiplier + 0.001f);
-                        float thisHeight = height * noiseCombinator.SamplePoint((float)pos.x / width, (float)pos.z / length);
-                        uint code = CalculateCode(thisHeight, height);
-                        BlockBase block = new BlockBase(code, (uint)Mathf.FloorToInt(thisHeight));
+                        float elevation = planetTemplate.SamplePoint((float)pos.x / width, (float)pos.z / length);
+                        float thisHeight = height * elevation;
+                        uint code = planetTemplate.SamplePointCode((float)pos.x / width, (float)pos.z / length, elevation);
+                        BlockBase block = new BlockBase(code, (uint)Mathf.FloorToInt(thisHeight / voxelSize.y));
                         chunkSystem.SetBlock(pos, block);
                     }
                 }
@@ -102,13 +107,5 @@ namespace KairoEngine.TerrainEngine
                 #endif
             }
         }
-
-        private uint CalculateCode(float height, float maxHeight)
-        {
-            var value = height/maxHeight;
-            if(value > 0.8) return 2;
-            else if(value > 0.4) return 1;
-            else return 0;
-        }
     }
 }

+ 12 - 2
Runtime/MarchingCubes3.cs

@@ -286,11 +286,21 @@ namespace KairoEngine.TerrainEngine
             {
                 default:
                 case 0:
-                    return Color.red;
+                    return Color.white;
                 case 1:
-                    return Color.green;
+                    return Color.red;
                 case 2: 
+                    return Color.green;
+                case 3: 
                     return Color.blue;
+                case 4: 
+                    return Color.black;
+                case 5: 
+                    return Color.yellow;
+                case 6: 
+                    return new Color(1f, 0, 1f); // Purple
+                case 7: 
+                    return new Color(0f, 1f, 1f); // Purple
             } 
         }
 

+ 658 - 0
Runtime/MarchingCubes4.cs

@@ -0,0 +1,658 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Unity.Jobs;
+using Unity.Collections;
+using UnityEngine.Assertions;
+using Sirenix.OdinInspector;
+using KairoEngine.Core;
+using KairoEngine.Chunks;
+
+namespace KairoEngine.TerrainEngine
+{
+    public class MarchingCubes4 : MonoBehaviour
+    {
+        public float terrainSurface = 0.5f;
+        public bool smoothTerrain;
+        public bool flatShaded;
+        public Material material;
+        
+
+        MeshFilter meshFilter;
+        MeshCollider meshCollider;
+
+        GameObject prefab;
+
+        
+        int width = 32;
+        int length = 32;
+        int height = 8;
+
+        Vector3 voxelSize = new Vector3(1f, 1f, 1f);
+
+        float[,,] terrainMap;
+        uint[,,] terrainCodes;
+
+        List<Vector3> vertices = new List<Vector3>();
+        List<int> triangles = new List<int>();
+
+        private ChunkSystem<BlockBase> chunkSystem;
+        private List<JobHandle> jobHandles = new List<JobHandle>();
+        private List<MarchingCubesJob4> jobs = new List<MarchingCubesJob4>();
+
+        private void Update()
+        {
+            UpdateFinishedJobs();
+        }
+
+        public void OnDestroy()
+        {
+            // Make sure we run our jobs to completion before exiting.
+            for (int i = 0; i < jobHandles.Count; i++)
+            {
+                jobHandles[i].Complete();
+            }
+        }
+
+        public void UpdateFinishedJobs()
+        {
+            //jobHandle.Complete();
+            Debug.Log("Updating finished jobs - " + jobHandles.Count);
+            for (int i = 0; i < jobHandles.Count; i++)
+            {
+                
+                if(jobHandles[i].IsCompleted)
+                {
+                    
+                }
+                jobHandles[i].Complete();
+                Debug.Log($"Job {i} is completed");    
+                if(prefab == null) prefab = new GameObject();
+                GameObject target = Instantiate(prefab, jobs[i].initialPosition, transform.rotation, this.transform);
+                // Create vertices and triangle arrays
+                int verticeCount = jobs[i].arrayIndexes[0];
+                int trianglesCount = jobs[i].arrayIndexes[1];
+                Debug.Log($"Completed job has {jobs[i].vertices.Count()}/{verticeCount} vertices and {jobs[i].triangles.Count()}/{trianglesCount} triangles");
+                Vector3[] vertices = jobs[i].vertices.Take(verticeCount).ToArray();
+                int[] triangles = jobs[i].triangles.Take(trianglesCount).ToArray();
+                Color[] vertexColors = jobs[i].vertexColors.Take(verticeCount).ToArray();
+                BuildMesh(target, vertices, triangles, vertexColors);
+                // Remove this
+                jobs[i].blocks.Dispose();
+                jobs[i].codes.Dispose();
+                jobs[i].vertices.Dispose();
+                jobs[i].triangles.Dispose();
+                jobs[i].arrayIndexes.Dispose();
+                jobs[i].triangleTable.Dispose();
+                jobs.RemoveAt(i);
+                jobHandles.RemoveAt(i);
+                i -= 1;
+            }
+        }
+
+        public bool IsGeneratorDone() => jobHandles.Count == 0 ? true : false;
+
+        public void Generate(ChunkSystem<BlockBase> chunkSystem, Vector3 initialPosition = new Vector3()) 
+        {
+            Debug.Log("Generating chunk " + initialPosition);
+            this.chunkSystem = chunkSystem;
+            width = chunkSystem.chunkSize.x;
+            length = chunkSystem.chunkSize.z;
+            height = chunkSystem.chunkSize.y;
+            voxelSize = chunkSystem.voxelSize;
+            //transform.tag = "Terrain";
+            terrainMap = new float[width + 1, height + 1, length + 1];
+            terrainCodes = new uint[width + 1, height + 1, length + 1];
+            PopulateTerrainMap(chunkSystem, initialPosition);
+            ScheduleJob(initialPosition);
+            //CreateMeshData();
+            
+        }
+
+        void PopulateTerrainMap (ChunkSystem<BlockBase> chunkSystem, Vector3 initialPosition = new Vector3()) 
+        {
+            // The data points for terrain are stored at the corners of our "cubes", so the terrainMap needs to be 1 larger
+            // than the width/height of our mesh.
+            for (int x = 0; x < width + 1; x++) {
+                for (int z = 0; z < length + 1; z++) {
+                    for (int y = 0; y < height + 1; y++) {
+                        Vector3 pos = initialPosition + new Vector3(x * voxelSize.x, y * voxelSize.y, z * voxelSize.z);
+                        BlockBase block = chunkSystem.GetBlock(pos);
+                        // Set the value of this point in the terrainMap.
+                        terrainMap[x, y, z] = (float)pos.y - ((float)block.value * voxelSize.y);
+                        terrainCodes[x, y, z] = block.code;
+                    }
+                }
+            }
+        }
+
+        void BuildMesh (GameObject target, Vector3[] vertices, int[] triangles, Color[] vertexColors) 
+        {
+            //Debug.Log($"Building mesh with {vertices.Length} vertices and {triangles.Length} triangles");
+            MeshRenderer meshRederer = target.GetComponent<MeshRenderer>();
+            if(meshRederer == null) meshRederer = target.AddComponent<MeshRenderer>();
+            meshRederer.sharedMaterial = material;
+            MeshFilter meshFilter = target.GetComponent<MeshFilter>();
+            if(meshFilter == null) meshFilter = target.AddComponent<MeshFilter>();
+            MeshCollider meshCollider = target.GetComponent<MeshCollider>();
+            if(meshCollider == null) meshCollider = target.AddComponent<MeshCollider>();
+            Mesh mesh = new Mesh();
+            mesh.vertices = vertices;
+            mesh.triangles = triangles;
+            mesh.colors = vertexColors;
+            mesh.RecalculateNormals();
+            meshFilter.mesh = mesh;
+            meshCollider.sharedMesh = mesh;
+        }
+
+        private void ScheduleJob(Vector3 initialPosition)
+        {
+            JobHandle jobHandle = new JobHandle();
+            var marchingCubesJob = new MarchingCubesJob4
+            {
+                chunkSize = this.chunkSystem.chunkSize,
+                voxelSize = this.voxelSize,
+                terrainSurface = terrainSurface,
+                smoothTerrain = smoothTerrain,
+                flatShaded = flatShaded,
+                blocks = new NativeArray<float>(terrainMap.Length, Allocator.TempJob),
+                codes = new NativeArray<uint>(terrainMap.Length, Allocator.TempJob),
+                vertices = new NativeArray<Vector3>(15000, Allocator.TempJob),
+                triangles = new NativeArray<int>(25000, Allocator.TempJob),
+                vertexColors = new NativeArray<Color>(15000, Allocator.TempJob),
+                arrayIndexes = new NativeArray<int>(2, Allocator.TempJob),
+                triangleTable = new NativeArray<int>(4096, Allocator.TempJob),
+                initialPosition = initialPosition
+            };
+            int blockIndex = 0;
+            for (int x = 0; x < width + 1; x++) {
+                for (int y = 0; y < height + 1; y++) {
+                    for (int z = 0; z < length + 1; z++) {
+                        marchingCubesJob.blocks[blockIndex] = terrainMap[x, y, z];
+                        marchingCubesJob.codes[blockIndex] = terrainCodes[x, y, z];
+                        blockIndex += 1;
+                    }
+                }
+            }
+            jobHandle = marchingCubesJob.Schedule(jobHandle);
+            jobHandles.Add(jobHandle);
+            jobs.Add(marchingCubesJob);
+        }
+    }
+
+    [ExecuteInEditMode]
+    struct MarchingCubesJob4 : IJob
+    {
+        public Vector3Int chunkSize;
+        public Vector3 voxelSize;
+        public float terrainSurface;
+        public bool smoothTerrain;
+        public bool flatShaded;
+        public NativeArray<float> blocks;
+        public NativeArray<uint> codes;
+        public NativeArray<Vector3> vertices;
+        public NativeArray<int> triangles;
+        public NativeArray<Color> vertexColors;
+        public Vector3 initialPosition;
+        public NativeArray<int> arrayIndexes; 
+        public NativeArray<int> triangleTable; 
+
+        private int verticesArrayIndex;
+        private int trianglesArrayIndex;
+
+        public void Execute()
+        {
+            verticesArrayIndex = 0;
+            trianglesArrayIndex = 0;
+            Debug.Log("Executing job");
+            CreateTriangleTable();
+            // Loop through each "cube" in our terrain.
+            for (int x = 0; x < chunkSize.x; x++) {
+                for (int y = 0; y < chunkSize.y; y++) {
+                    for (int z = 0; z < chunkSize.z; z++) {
+                        // Pass the value into our MarchCube function.
+                        MarchCube(new Vector3Int(x, y, z));
+                    }
+                }
+            }
+            arrayIndexes[0] = verticesArrayIndex;
+            arrayIndexes[1] = trianglesArrayIndex;
+        }
+
+        void MarchCube (Vector3Int position) {
+            // Sample terrain values at each corner of the cube.
+            float[] cube = new float[8];
+            for (int i = 0; i < 8; i++) {
+                cube[i] = SampleTerrain(position + CornerTable(i));
+            }
+            Vector3 realPosition = new Vector3(position.x * voxelSize.x, position.y * voxelSize.y, position.z * voxelSize.z);
+            // Get the configuration index of this cube.
+            int configIndex = GetCubeConfiguration(cube);
+            // If the configuration of this cube is 0 or 255 (completely inside the terrain or completely outside of it) we don't need to do anything.
+            if (configIndex == 0 || configIndex == 255) return;
+            // Loop through the triangles. There are never more than 5 triangles to a cube and only three vertices to a triangle.
+            int edgeIndex = 0;
+            for(int i = 0; i < 5; i++) 
+            {
+                for(int p = 0; p < 3; p++) 
+                {
+                    // Get the current indice. We increment triangleIndex through each loop.
+                    int indice = GetFromTriangleTable(configIndex, edgeIndex);
+                    // If the current edgeIndex is -1, there are no more indices and we can exit the function.
+                    if (indice == -1) return;
+                    // Get the vertices for the start and end of this edge.
+                    Vector3 vert1 = position + CornerTable(EdgeIndexes()[indice, 0]);
+                    Vector3 vert2 = position + CornerTable(EdgeIndexes()[indice, 1]);
+                    //vert1 = new Vector3(vert1.x * voxelSize.x, vert1.y * voxelSize.y, vert1.z * voxelSize.z);
+                    //vert2 = new Vector3(vert2.x * voxelSize.x, vert2.y * voxelSize.y, vert2.z * voxelSize.z);
+                    Vector3 vertPosition;
+                    if (smoothTerrain) {
+                        // Get the terrain values at either end of our current edge from the cube array created above.
+                        float vert1Sample = cube[EdgeIndexes()[indice, 0]];
+                        float vert2Sample = cube[EdgeIndexes()[indice, 1]];
+                        // Calculate the difference between the terrain values.
+                        float difference = vert2Sample - vert1Sample;
+                        // If the difference is 0, then the terrain passes through the middle.
+                        if (difference == 0) difference = terrainSurface;
+                        else difference = (terrainSurface - vert1Sample) / difference;
+                        // Calculate the point along the edge that passes through.
+                        vertPosition = vert1 + ((vert2 - vert1) * difference);
+                    } 
+                    else 
+                    {
+                        // Get the midpoint of this edge.
+                        vertPosition = (vert1 + vert2) / 2f;
+                    }
+                    vertPosition = new Vector3(vertPosition.x * voxelSize.x, vertPosition.y * voxelSize.y, vertPosition.z * voxelSize.z);
+                    // Add to our vertices and triangles list and incremement the edgeIndex.
+                    if (flatShaded) 
+                    {
+                        vertices[verticesArrayIndex] = vertPosition;
+                        triangles[trianglesArrayIndex] = verticesArrayIndex;
+                        vertexColors[verticesArrayIndex] = SampleTerrainColor(position);
+                        verticesArrayIndex += 1;
+                        trianglesArrayIndex += 1;
+                    } 
+                    else 
+                    {
+                        triangles[trianglesArrayIndex] = VertForIndice(vertPosition, position);
+                        trianglesArrayIndex += 1;
+                    }
+                    edgeIndex++;
+                }
+            }
+        }
+        
+        float SampleTerrain (Vector3Int point) 
+        {
+            // https://stackoverflow.com/questions/3613429/algorithm-to-convert-a-multi-dimensional-array-to-a-one-dimensional-array
+            int index = (((chunkSize.y + 1) * (chunkSize.z + 1)) * point.x) + ((chunkSize.z + 1) * point.y) + point.z;
+            return blocks[index];
+        }
+
+        Color SampleTerrainColor (Vector3Int point) 
+        {
+            int index = (((chunkSize.y + 1) * (chunkSize.z + 1)) * point.x) + ((chunkSize.z + 1) * point.y) + point.z;
+            uint code = codes[index];
+            switch (code)
+            {
+                default:
+                case 0:
+                    return Color.white;
+                case 1:
+                    return Color.red;
+                case 2: 
+                    return Color.green;
+                case 3: 
+                    return Color.blue;
+                case 4: 
+                    return Color.black;
+                case 5: 
+                    return Color.yellow;
+                case 6: 
+                    return new Color(1f, 0, 1f); // Purple
+                case 7: 
+                    return new Color(0f, 1f, 1f); // Purple
+            } 
+        }
+
+        int GetCubeConfiguration (float[] cube) 
+        {
+            // Starting with a configuration of zero, loop through each point in the cube and check if it is below the terrain surface.
+            int configurationIndex = 0;
+            for (int i = 0; i < 8; i++) {
+                // If it is, use bit-magic to the set the corresponding bit to 1. So if only the 3rd point in the cube was below
+                // the surface, the bit would look like 00100000, which represents the integer value 32.
+                if (cube[i] > terrainSurface)
+                    configurationIndex |= 1 << i;
+            }
+            return configurationIndex;
+        }
+        
+        int VertForIndice (Vector3 vert, Vector3Int point) 
+        {
+            // Loop through all the vertices currently in the vertices list.
+            for (int i = 0; i < verticesArrayIndex; i++) 
+            {
+                // If we find a vert that matches ours, then simply return this index.
+                if (vertices[i] == vert) return i;
+            }
+            // If we didn't find a match, add this vert to the list and return last index.
+            vertices[verticesArrayIndex] = vert;
+            vertexColors[verticesArrayIndex] = SampleTerrainColor(point);
+            verticesArrayIndex += 1;
+            return verticesArrayIndex - 1;
+        }
+
+        // Vector3Int[] CornerTable;
+        // int[,] EdgeIndexes;
+        // private int[,] TriangleTable;
+
+        private Vector3Int CornerTable(int index)
+        {
+            Vector3Int[] CornerTable = new Vector3Int[8] {
+                new Vector3Int(0, 0, 0),
+                new Vector3Int(1, 0, 0),
+                new Vector3Int(1, 1, 0),
+                new Vector3Int(0, 1, 0),
+                new Vector3Int(0, 0, 1),
+                new Vector3Int(1, 0, 1),
+                new Vector3Int(1, 1, 1),
+                new Vector3Int(0, 1, 1)
+            };
+            return CornerTable[index];
+        }
+
+        private int[,] EdgeIndexes()
+        {
+            int[,] EdgeIndexes = new int[12, 2] {
+                {0, 1}, {1, 2}, {3, 2}, {0, 3}, {4, 5}, {5, 6}, {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}
+            };
+            return EdgeIndexes;
+        }
+
+        private void CreateTriangleTable()
+        {
+            int[,] table = TriangleTable();
+            int index = 0;
+            for (int x = 0; x < table.GetLength(0); x++)
+            {
+                for (int y = 0; y < table.GetLength(1); y++)
+                {
+                    triangleTable[index] = table[x, y];
+                    index += 1;
+                }
+            }
+        }
+
+        private int GetFromTriangleTable(int x, int y)
+        {
+            return triangleTable[x * 16 + y];
+        }
+
+        private int[,] TriangleTable()
+        {
+            int[,] TriangleTable = new int[,] 
+            {
+                {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
+                {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
+                {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
+                {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
+                {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
+                {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
+                {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
+                {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
+                {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
+                {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
+                {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
+                {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
+                {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
+                {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
+                {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
+                {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
+                {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
+                {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
+                {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
+                {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
+                {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
+                {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
+                {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
+                {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
+                {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
+                {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
+                {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
+                {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
+                {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
+                {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
+                {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
+                {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
+                {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
+                {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
+                {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
+                {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
+                {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
+                {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
+                {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
+                {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
+                {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
+                {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
+                {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
+                {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
+                {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
+                {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
+                {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
+                {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
+                {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
+                {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
+                {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
+                {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
+                {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
+                {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
+                {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
+                {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
+                {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
+                {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
+                {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
+                {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
+                {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
+                {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
+                {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
+                {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
+                {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
+                {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
+                {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
+                {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
+                {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
+                {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
+                {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
+                {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
+                {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
+                {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
+                {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
+                {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
+                {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
+                {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
+                {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
+                {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
+                {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
+                {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
+                {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
+                {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
+                {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
+                {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
+                {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
+                {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
+                {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
+                {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
+                {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
+                {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
+                {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
+                {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
+                {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
+                {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
+                {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
+                {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
+                {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
+                {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
+                {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
+                {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
+                {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
+                {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
+                {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
+                {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
+                {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
+                {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
+                {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
+                {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
+                {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
+                {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
+                {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
+                {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
+                {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
+                {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
+                {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
+                {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
+                {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
+                {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
+                {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
+                {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
+                {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
+                {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
+                {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
+                {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
+                {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
+                {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
+                {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
+                {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
+                {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
+                {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
+                {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
+                {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
+                {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
+                {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
+                {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
+                {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
+                {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
+                {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
+                {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
+                {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
+                {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
+                {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
+                {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
+                {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
+                {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
+                {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
+                {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
+                {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
+                {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
+                {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
+                {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
+                {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
+                {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
+                {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
+                {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
+                {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
+                {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
+                {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
+                {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
+                {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
+                {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
+                {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
+                {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
+                {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
+                {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
+                {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
+                {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
+                {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
+                {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
+                {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
+                {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
+                {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
+                {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
+                {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
+                {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
+                {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+                {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
+            };
+            return TriangleTable;
+        }
+    }
+}

+ 11 - 0
Runtime/MarchingCubes4.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b936cea7e3b4e0a4aae206a7e21c2099
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
Runtime/TerrainEngine.asmdef

@@ -6,6 +6,7 @@
         "GUID:7e5ae6a38d1532248b4c890eca668b06",
         "GUID:22b3fea425824d6448cb89ed3c76c9a8",
         "GUID:d09ad6089c871be4581e7b220db144be",
+        "GUID:3ffdbd0bbde813141a8a14a897160240",
         "GUID:e0cd26848372d4e5c891c569017e11f1",
         "GUID:560b04d1a97f54a4e82edc0cbbb69285"
     ],