Skip to content

Instantly share code, notes, and snippets.

@promontis
Last active May 29, 2019 16:10
Show Gist options
  • Save promontis/ca686732cfec04b4d9e38542dafd9468 to your computer and use it in GitHub Desktop.
Save promontis/ca686732cfec04b4d9e38542dafd9468 to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SharpGLTF.Geometry;
using SharpGLTF.Geometry.VertexTypes;
using SharpGLTF.Materials;
using VERTEX = SharpGLTF.Geometry.VertexBuilder<
SharpGLTF.Geometry.VertexTypes.VertexPositionNormal,
SharpGLTF.Geometry.VertexTypes.VertexTexture1,
SharpGLTF.Geometry.VertexTypes.VertexEmpty>;
namespace Stylister.Modeling3D.Core.Geometry
{
public class PolygonMeshBuilder
{
private readonly List<Vector2> _points = new List<Vector2>();
private readonly List<Vector2> _outlinePoints = new List<Vector2>();
private readonly List<List<Vector2>> _holes = new List<List<Vector2>>();
private readonly List<double> _epoints = new List<double>();
private readonly List<int> _eholes = new List<int>();
public PolygonMeshBuilder(List<Vector2> counters)
{
var points = counters;
AddToEPoints(points);
_points.AddRange(points);
_outlinePoints.AddRange(points);
}
private void AddToEPoints(List<Vector2> points)
{
_epoints.AddRange(points.SelectMany(point => new List<double> { point.X, point.Y }));
}
private PolygonMeshBuilder AddHole(List<Vector2> hole)
{
_points.AddRange(hole);
_holes.Add(hole);
_eholes.Add(_epoints.Count / 2);
AddToEPoints(hole);
return this;
}
private VertexData BuildVertexData(float depth)
{
var result = new VertexData();
var normals = new List<Vector3>();
var positions = new List<Vector3>();
var uvs = new List<Vector2>();
var bounds = _points.ComputeBounds();
foreach (var point in _points)
{
normals.Add(new Vector3(0, 1.0f, 0));
positions.Add(new Vector3(point.X, 0, point.Y));
uvs.Add(new Vector2((point.X - bounds.Min.X) / bounds.Width, (point.Y - bounds.Min.Y) / bounds.Height));
}
var res = Earcut.Tessellate(_epoints, _eholes);
var indices = new List<int>(res);
if (depth > 0)
{
var positionsCount = positions.Count / 3;
// Add the elements at the depth
foreach (var point in _points)
{
normals.Add(new Vector3(0, -1.0f, 0));
positions.Add(new Vector3(point.X, -depth, point.Y));
uvs.Add(new Vector2(1 - (point.X - bounds.Min.X) / bounds.Width, 1 - (point.Y - bounds.Min.Y) / bounds.Height));
}
var totalCount = indices.Count;
for (var i = 0; i < totalCount; i += 3)
{
var i0 = indices[i + 0];
var i1 = indices[i + 1];
var i2 = indices[i + 2];
indices.Add(i2 + positionsCount);
indices.Add(i1 + positionsCount);
indices.Add(i0 + positionsCount);
}
// Add the sides
AddSide(positions, normals, uvs, indices, bounds, _outlinePoints, depth, false);
foreach (var hole in _holes)
{
AddSide(positions, normals, uvs, indices, bounds, hole, depth, true);
}
}
result.Indices = indices;
result.Positions = positions;
result.Normals = normals;
result.Uvs = uvs;
return result;
}
private void AddSide(List<Vector3> positions, List<Vector3> normals, List<Vector2> uvs, List<int> indices, Bounds bounds, List<Vector2> points, float depth, bool flip)
{
var startIndex = positions.Count / 3;
var uLength = 0.0f;
for (var i = 0; i < points.Count; i++)
{
var p = points[i];
var p1 = i + 1 > points.Count - 1 ? points[0] : points[i + 1];
positions.Add(new Vector3(p.X, 0, p.Y));
positions.Add(new Vector3(p.X, -depth, p.Y));
positions.Add(new Vector3(p1.X, 0, p1.Y));
positions.Add(new Vector3(p1.X, -depth, p1.Y));
var v1 = new Vector3(p.X, 0, p.Y);
var v2 = new Vector3(p1.X, 0, p1.Y);
var v3 = v2 - v1;
var v4 = new Vector3(0, 1, 0);
var vn = Vector3.Cross(v3, v4);
vn = Vector3.Normalize(vn);
uvs.Add(new Vector2(uLength / bounds.Width, 0));
uvs.Add(new Vector2(uLength / bounds.Width, 1));
uLength += v3.Length();
uvs.Add(new Vector2(uLength / bounds.Width, 0));
uvs.Add(new Vector2(uLength / bounds.Width, 1));
if (!flip)
{
normals.Add(new Vector3(-vn.X, -vn.Y, -vn.Z));
normals.Add(new Vector3(-vn.X, -vn.Y, -vn.Z));
normals.Add(new Vector3(-vn.X, -vn.Y, -vn.Z));
normals.Add(new Vector3(-vn.X, -vn.Y, -vn.Z));
indices.Add(startIndex);
indices.Add(startIndex + 1);
indices.Add(startIndex + 2);
indices.Add(startIndex + 1);
indices.Add(startIndex + 3);
indices.Add(startIndex + 2);
}
else
{
normals.Add(new Vector3(vn.X, vn.Y, vn.Z));
normals.Add(new Vector3(vn.X, vn.Y, vn.Z));
normals.Add(new Vector3(vn.X, vn.Y, vn.Z));
normals.Add(new Vector3(vn.X, vn.Y, vn.Z));
indices.Add(startIndex);
indices.Add(startIndex + 2);
indices.Add(startIndex + 1);
indices.Add(startIndex + 1);
indices.Add(startIndex + 2);
indices.Add(startIndex + 3);
}
startIndex += 4;
}
}
public MeshBuilder<VertexPositionNormal, VertexTexture1, VertexEmpty> Build(float depth = 0)
{
VERTEX CreateVertexFromIndex(VertexData vertexData1, int index)
{
return new VERTEX(
new VertexPositionNormal(vertexData1.Positions[index],vertexData1.Normals[index]),
new VertexTexture1(vertexData1.Uvs[index]),
new VertexEmpty());
}
var result = VERTEX.CreateCompatibleMesh();
var vertexData = BuildVertexData(depth);
var material = new MaterialBuilder("material");
var primitive = result.UsePrimitive(material);
for (var i = 2; i < vertexData.Indices.Count; i += 3)
{
var idx0 = vertexData.Indices[i - 2];
var idx1 = vertexData.Indices[i - 1];
var idx2 = vertexData.Indices[i - 0];
var a = CreateVertexFromIndex(vertexData, idx0);
var b = CreateVertexFromIndex(vertexData, idx1);
var c = CreateVertexFromIndex(vertexData, idx2);
primitive.AddTriangle(a, b, c);
}
return result;
}
}
public class VertexData
{
public List<int> Indices { get; set; }
public List<Vector3> Positions { get; set; }
public List<Vector3> Normals { get; set; }
public List<Vector2> Uvs { get; set; }
}
}
@promontis
Copy link
Author

@vpenades
Cool! I've just updated the code :) Far cleaner indeed!

The indices are correct like this, right?

@vpenades
Copy link

yes, it seems it's correct now... dunno if it's working for you, thought.

I would improve the VertexData code a bit further:

public class VertexData
{
		public List<int> Indices { get; set; }
		public List<Vector3> Positions { get; set; }
		public List<Vector3> Normals { get; set; }
		public List<Vector2> Uvs { get; set; }

              VERTEX GetVertex(int index)
			{
                                index = Indices[ index ];

				return new VERTEX(
					new VertexPositionNormal( Positions[index], Normals[index] ),
					new VertexTexture1( Uvs[index] ),
					new VertexEmpty( ) );
                        }
}


{

for (var i = 2; i < vertexData.Indices.Count; i += 3)
			{
                                var a = vertexData.GetVertex(i-2);
				var b = vertexData.GetVertex(i-1);
				var c = vertexData.GetVertex(i-0);

				primitive.AddTriangle(a, b, c);
                         }
}

you could even move the whole "Build" method inside VertexData class...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment