Skip to content

Instantly share code, notes, and snippets.

@alexmalyutindev
Last active December 19, 2024 08:05
Show Gist options
  • Select an option

  • Save alexmalyutindev/8410463d7bf2b484fc74f2d18209dec6 to your computer and use it in GitHub Desktop.

Select an option

Save alexmalyutindev/8410463d7bf2b484fc74f2d18209dec6 to your computer and use it in GitHub Desktop.
Terrain Chunk Mesh Generator
// MIT License
//
// Copyright (c) 2024 Alexander Malyutin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Terrain Chunk Mesh Generator
// ._____._____.
// |\ | /|\ | /|
// |_\|/_|_\|/_|
// | /|\ | /|\ |
// |/_|_\|/_|_\|
// |\ | /|\ | /|
// |_\|/_|_\|/_|
// | /|\ | /|\ |
// |/_|_\|/_|_\|
public static class TerrainChunkGenerator
{
public static int GetChunkDimension(int sections)
{
return sections * 2 + 1;
}
public static Vector3[] GenChunkVertices(int dimension)
{
var vertices = new Vector3[dimension * dimension];
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
vertices[col + row * dimension] = new Vector3(
(float)col / (dimension - 1),
0.0f,
(float)row / (dimension - 1)
);
}
}
return vertices;
}
public static Vector3[] GenChunkNormals(int dimension)
{
var normals = new Vector3[dimension * dimension];
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
normals[col + row * dimension] = new Vector3(0.0f, 1.0f, 0.0f);
}
}
return normals;
}
public static Vector2[] GenChunkTexcoords(int dimension)
{
var texcoords = new Vector2[dimension * dimension];
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
texcoords[col + row * dimension] = new Vector2(
(float)col / (dimension - 1),
(float)row / (dimension - 1)
);
}
}
return texcoords;
}
public static int[] GenChunkIndexes(int dimension)
{
int[] indexes = new int[(dimension - 1) * (dimension - 1) * 2 * 3];
var indexesSpan = new Span<int>(indexes, 0, indexes.Length);
for (int row = 0; row < dimension - 1; row++)
{
for (int col = 0; col < dimension - 1; col++)
{
var temp = indexesSpan.Slice((col + row * (dimension - 1)) * 6, 6);
GetQuadIndexes(col + row * dimension + 1, dimension, (col + row * dimension) % 2 == 0, ref temp);
}
}
return indexes;
}
public static void GetQuadIndexes(int bottomLeftVertexIndex, int chunkDimension, bool antiDiagonal, ref Span<int> arr)
{
if (antiDiagonal)
{
// .__.
// | /|
// |/_|
int k = 0;
arr[k++] = bottomLeftVertexIndex;
arr[k++] = bottomLeftVertexIndex + chunkDimension;
arr[k++] = bottomLeftVertexIndex + chunkDimension + 1;
arr[k++] = bottomLeftVertexIndex;
arr[k++] = bottomLeftVertexIndex + chunkDimension + 1;
arr[k++] = bottomLeftVertexIndex + 1;
}
else
{
// .__.
// |\ |
// |_\|
int k = 0;
arr[k++] = bottomLeftVertexIndex;
arr[k++] = bottomLeftVertexIndex + chunkDimension;
arr[k++] = bottomLeftVertexIndex + 1;
arr[k++] = bottomLeftVertexIndex + 1;
arr[k++] = bottomLeftVertexIndex + chunkDimension;
arr[k++] = bottomLeftVertexIndex + chunkDimension + 1;
}
}
}
public struct Vector2
{
public float x, y;
public Vector2(float x, float y) : this()
{
this.x = x;
this.y = y;
}
public string ToObjTexcoord() => $"vt {x:F} {y:F}";
}
public struct Vector3
{
public float x, y, z;
public Vector3(float x, float y, float z) : this()
{
this.x = x;
this.y = y;
this.z = z;
}
public string ToObjVertex() => $"v {x:F} {y:F} {z:F}";
public string ToObjNormal() => $"vn {x:F} {y:F} {z:F}";
}
int sections = 2;
int dimension = TerrainChunkGenerator.GetChunkDimension(sections);
var vertices = TerrainChunkGenerator.GenChunkVertices(dimension);
var normals = TerrainChunkGenerator.GenChunkNormals(dimension);
var texcoords = TerrainChunkGenerator.GenChunkTexcoords(dimension);
var indexes = TerrainChunkGenerator.GenChunkIndexes(dimension);
using (var file = File.Create("./test.obj"))
{
using var writer = new StreamWriter(file);
writer.WriteLine("o terrain-chunk");
writer.WriteLine("# Vertices");
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
writer.WriteLine(vertices[col + row * dimension].ToObjVertex());
}
}
writer.WriteLine("# Normals");
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
writer.WriteLine(normals[col + row * dimension].ToObjNormal());
}
}
writer.WriteLine("# Texcoords");
for (int row = 0; row < dimension; row++)
{
for (int col = 0; col < dimension; col++)
{
writer.WriteLine(texcoords[col + row * dimension].ToObjTexcoord());
}
}
writer.WriteLine("# Indexes");
writer.WriteLine("g terrain-chunk");
for (int i = 0; i < indexes.Length; i += 3)
{
writer.WriteLine("f " + string.Join(' ', indexes[i..(i + 3)].Select(index => $"{index}/{index}/{index}")));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment