using System.Collections; using System.Collections.Generic; using Godot; using System; namespace Rokojori { public class MeshGeometry { public List vertices = new List(); public List indices = new List(); public List normals; public List uvs; public List uv2s; public List colors; public int numTriangles => indices.Count / 3; public static MeshGeometry From( MeshInstance3D meshInstance3D ) { return From( (ArrayMesh)meshInstance3D.Mesh, meshInstance3D.GlobalTransform ); } public static MeshGeometry From( ArrayMesh mesh, Transform3D? trsf = null ) { var mg = new MeshGeometry(); var arrays = mesh.SurfaceGetArrays( 0 ); var vertices = arrays[ (int) Mesh.ArrayType.Vertex ]; if ( Variant.Type.Nil != vertices.VariantType ) { mg.vertices = new List( vertices.AsVector3Array() ); } var normals = arrays[ (int) Mesh.ArrayType.Normal ]; if ( Variant.Type.Nil != normals.VariantType ) { mg.normals = new List( normals.AsVector3Array() ); } var uvs = arrays[ (int) Mesh.ArrayType.TexUV ]; if ( Variant.Type.Nil != uvs.VariantType ) { mg.uvs = new List( uvs.AsVector2Array() ); } var uv2s = arrays[ (int) Mesh.ArrayType.TexUV2 ]; if ( Variant.Type.Nil != uv2s.VariantType ) { mg.uv2s = new List( uv2s.AsVector2Array() ); } var colors = arrays[ (int) Mesh.ArrayType.Color ]; if ( Variant.Type.Nil != colors.VariantType ) { mg.colors = new List( colors.AsColorArray() ); } var indices = arrays[ (int) Mesh.ArrayType.Index ]; if ( Variant.Type.Nil != indices.VariantType ) { mg.indices = new List( indices.AsInt32Array() ); } if ( trsf != null ) { mg.ApplyTransform( (Transform3D)trsf ); } return mg; } public void ApplyTransform( Transform3D trsf ) { for ( int i = 0; i < vertices.Count; i++ ) { vertices[ i ] = trsf * vertices[ i ]; normals[ i ] = trsf.Basis.GetRotationQuaternion() * normals[ i ]; } } public void BlendNormals( Vector3 direction, float amount ) { if ( amount <= 0 ) { return; } if ( amount >= 1 ) { for ( int i = 0; i < normals.Count; i++ ) { normals[ i ] = direction; } return; } for ( int i = 0; i < normals.Count; i++ ) { normals[ i ] = Math3D.BlendNormals( normals[ i ], direction, amount ); } } public void Offset( Vector3 offset ) { for ( int i = 0; i < vertices.Count; i++ ) { vertices[ i ] = vertices[ i ] + offset; } } public void ForEachTriangle( Action callback ) { var index = 0; for ( int i = 0; i < indices.Count; i += 3 ) { var a = indices[ i + 0 ]; var b = indices[ i + 1 ]; var c = indices[ i + 2 ]; callback( index, a, b, c ); index ++; } } static int GetUVIndex( int u, int v, int segments ) { return u * ( segments + 1 ) + v; } public static MeshGeometry CreateFromUVFunction( Func uv, int uSegments, int vSegments ) { var mg = new MeshGeometry(); for ( int i = 0; i <= uSegments; i++ ) { var uI = (float)i / uSegments; for ( int j = 0; j <= vSegments; j++ ) { var vJ = (float)j / vSegments; var uvIJ = new Vector2( uI, vJ ); var pose = uv( uvIJ ); mg.vertices.Add( pose.position ); mg.normals.Add( pose.up ); mg.uvs.Add( uvIJ ); } } for ( int i = 0; i < uSegments; i++ ) { var u0 = i; var u1 = i + 1; for ( int j = 0; j < vSegments; j++ ) { var v0 = j; var v1 = j + 1; var a = GetUVIndex( u0, v0, vSegments ); var b = GetUVIndex( u1, v0, vSegments ); var c = GetUVIndex( u1, v1, vSegments ); var d = GetUVIndex( u0, v1, vSegments ); mg.AddTriangle( a, b, d ); mg.AddTriangle( b, c, d ); // mg.AddTriangle( d, b, a ); // mg.AddTriangle( d, c, b ); } } return mg; } public MeshGeometry UniqueTriangles() { var mg = new MeshGeometry(); mg.Initialize( true, true, colors != null, uv2s != null ); if ( colors != null ) { mg.colors = new List(); } if ( uv2s != null ) { mg.uv2s = new List(); } ForEachTriangle( ( index, a, b, c )=> { var vA = vertices[ a ]; var vB = vertices[ b ]; var vC = vertices[ c ]; var uvA = uvs[ a ]; var uvB = uvs[ b ]; var uvC = uvs[ c ]; var nA = normals[ a ]; var nB = normals[ b ]; var nC = normals[ c ]; mg.AddTriangle( vA, vB, vC, nA, nB, nC, uvA, uvB, uvC ); if ( colors != null ) { Lists.Add( mg.colors, colors[ a ], colors[ b ], colors[ c ] ); } if ( uv2s != null ) { Lists.Add( mg.uv2s, uv2s[ a ], uv2s[ b ], uv2s[ c ] ); } } ); return mg; } public void SetTriangleUVs( int triangleIndex, Vector2 uv ) { var i = triangleIndex * 3; var a = indices[ i + 0 ]; var b = indices[ i + 1 ]; var c = indices[ i + 2 ]; uvs[ a ] = uv; uvs[ b ] = uv; uvs[ c ] = uv; } public void SetTriangleNormal( int triangleIndex, Vector3 normal ) { var i = triangleIndex * 3; var a = indices[ i + 0 ]; var b = indices[ i + 1 ]; var c = indices[ i + 2 ]; normals[ a ] = normal; normals[ b ] = normal; normals[ c ] = normal; } public void SetTriangleColor( int triangleIndex, Color color ) { var i = triangleIndex * 3; var a = indices[ i + 0 ]; var b = indices[ i + 1 ]; var c = indices[ i + 2 ]; colors[ a ] = color; colors[ b ] = color; colors[ c ] = color; } public MeshGeometry( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false ) { Initialize( normals, uvs, colors, uvs2 ); } public void Initialize( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false ) { this.normals = normals ? new List() : null; this.uvs = uvs ? new List() : null; this.uv2s = uvs2 ? new List() : null; this.colors = colors ? new List() : null; } public Vector3 GetRawNormal( int triangleIndex ) { var va = vertices[ indices[ triangleIndex * 3 ] ]; var vb = vertices[ indices[ triangleIndex * 3 + 1] ]; var vc = vertices[ indices[ triangleIndex * 3 + 2 ] ]; return Math3D.ComputeNormal( va, vb, vc ); } public void ComputeNormals() { var dl = new DictionaryList(); ForEachTriangle( ( t, va, vb, vc)=> { var normal = GetRawNormal( t ); dl.Add( va, normal ); dl.Add( vb, normal ); dl.Add( vc, normal ); } ); foreach ( var e in dl ) { var normalIndex = e.Key; var normal = Math3D.Average( e.Value ); normals[ normalIndex ] = normal; } } public MeshGeometry Clone() { var mg = new MeshGeometry(); mg.vertices = Lists.Clone( vertices ); mg.indices = Lists.Clone( indices ); mg.normals = Lists.Clone( normals ); mg.uvs = Lists.Clone( uvs ); mg.uv2s = Lists.Clone( uv2s ); mg.colors = Lists.Clone( colors ); return mg; } public void FlipNormalDirection() { for ( int i = 0; i < normals.Count; i++ ) { normals[ i ] = -normals[ i ]; } for ( int i =0 ; i < indices.Count; i += 3 ) { var b = indices[ i ]; indices[ i ] = indices[ i + 2 ]; indices[ i + 2 ] = b; } } public void Add( MeshGeometry mg ) { var mappedIndices = new Dictionary(); for ( int i = 0; i < mg.indices.Count; i++ ) { var mgIndex = mg.indices[ i ]; if ( ! mappedIndices.ContainsKey( mgIndex ) ) { var newIndex = vertices.Count; if ( mgIndex >= mg.vertices.Count || mgIndex < 0 ) { RJLog.Log( "Out of range:", i, ">>", mgIndex, mg.vertices.Count, mg.indices ); } var v = mg.vertices[ mgIndex ]; vertices.Add( v ); if ( normals != null && mg.normals != null) { normals.Add( mg.normals[ mgIndex ] ); } if ( uvs != null && mg.uvs != null) { uvs.Add( mg.uvs[ mgIndex ] ); } if ( colors != null && mg.colors != null) { colors.Add( mg.colors[ mgIndex ] ); } if ( uv2s != null && mg.uv2s != null) { uv2s.Add( mg.uv2s[ mgIndex ] ); } mappedIndices[ mgIndex ] = newIndex; } indices.Add( mappedIndices[ mgIndex ] ); } } public void AddTriangle( int a, int b, int c, bool flip = false ) { if ( flip ) { Lists.Add( indices, c, b, a ); } else { Lists.Add( indices, a, b, c ); } } public void AddTriangle( Vector3 va, Vector3 vb, Vector3 vc, Vector3 na, Vector3 nb, Vector3 nc, Vector2 uva, Vector2 uvb, Vector2 uvc ) { var index = vertices.Count; Lists.Add( vertices, va, vb, vc ); Lists.Add( normals, na, nb, nc ); Lists.Add( uvs, uva, uvb, uvc ); Lists.Add( indices, index, index + 1, index + 2 ); } public void AddTriangle( Vector3 va, Vector3 vb, Vector3 vc, Vector2 uva, Vector2 uvb, Vector2 uvc ) { var n = Vector3.Up; AddTriangle( va, vb, vc, n, n, n, uva, uvb, uvc ); } public void AddTriangle( Vector3 va, Vector3 vb, Vector3 vc ) { var n = Vector3.Up; var uv = Vector2.Zero; AddTriangle( va, vb, vc, n, n, n, uv, uv, uv ); } public void AddQuad( Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd, Vector3 na, Vector3 nb, Vector3 nc, Vector3 nd, Vector2 uva, Vector2 uvb, Vector2 uvc, Vector2 uvd ) { AddTriangle( va, vb, vc, na, nb, nc, uva, uvb, uvc ); AddTriangle( vc, vd, va, nc, nd, na, uvc, uvd, uva ); } public void AddQuad( Quaternion rotation, float size, Rect2 rectangle ) { AddQuad( rotation, size, rectangle.Position, rectangle.End ); } public void AddQuad( Quaternion rotation, float size, Vector2 uv00, Vector2 uv11 ) { var l = size * 0.5f; var normal = Vector3.Forward * rotation; var points = new List { new Vector3( -l, -l, 0 ), new Vector3( l, -l, 0 ), new Vector3( -l, l, 0 ), new Vector3( l, l, 0 ) }; for ( int i = 0; i < points.Count; i++ ) { points[ i ] = points[ i ] * rotation; } var uv10 = new Vector2( uv11.X, uv00.Y ); var uv01 = new Vector2( uv00.X, uv11.Y ); AddQuad( points[ 0 ], points[ 1 ], points[ 2 ], points[ 3 ], normal, normal, normal, normal, uv00, uv10, uv11, uv10 ); } public void AddQuad( int lt, int rt, int lb, int rb, bool flip = false ) { if ( flip ) { AddQuad( rt, lt, rb, lb, false ); } else { Lists.Add( indices, lb, rt, lt, rt, lb, rb ); } } public void AddQuad( Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd, Vector2 uva, Vector2 uvb, Vector2 uvc, Vector2 uvd ) { AddTriangle( va, vb, vc, uva, uvb, uvc ); AddTriangle( vc, vd, va, uvc, uvd, uva ); } public void AddQuad( Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd ) { AddQuad( va, vb, vc, vd, new Vector2( 0, 0 ), new Vector2( 0, 1 ), new Vector2( 1, 1 ), new Vector2( 1, 0 ) ); } public void AddQuad( Vector3 offset, float size ) { var center = new Vector3( 0.5f, 0.5f, 0 ); /* [-0.5, -0.5] [0.5, -0.5 ] [-0.5, 0.5] [0.5, 0.5 ] */ AddQuad( new Vector3( -0.5f, -0.5f, 0 ) * size + offset , new Vector3( -0.5f, 0.5f, 0 ) * size + offset , new Vector3( 0.5f, 0.5f, 0 ) * size + offset , new Vector3( 0.5f, -0.5f, 0 ) * size + offset ); } public void AddConvex2( Convex2 convex ) { var points = convex.points; var tris = points.Count - 2; for ( int i = 0; i < tris; i++ ) { var p0 = Math3D.XYasXZ( points[ 0 ] ); var p1 = Math3D.XYasXZ( points[ i + 1 ] ); var p2 = Math3D.XYasXZ( points[ i + 2 ] ); AddTriangle( p0, p1, p2 ); } } public ArrayMesh GenerateMesh( Mesh.PrimitiveType type = Mesh.PrimitiveType.Triangles, ArrayMesh arrayMesh = null ) { if ( arrayMesh == null ) { arrayMesh = new ArrayMesh(); } var surfaceArray = new Godot.Collections.Array(); surfaceArray.Resize( (int) Mesh.ArrayType.Max ); if ( vertices != null && vertices.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.Vertex ] = vertices.ToArray(); } if ( normals != null && normals.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.Normal ] = normals.ToArray(); } if ( uvs != null && uvs.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.TexUV ] = uvs.ToArray(); } if ( uv2s != null && uv2s.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.TexUV2 ] = uv2s.ToArray(); } if ( colors != null && colors.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.Color ] = colors.ToArray(); } if ( indices != null && indices.Count != 0 ) { surfaceArray[ (int) Mesh.ArrayType.Index ] = indices.ToArray(); } arrayMesh.AddSurfaceFromArrays( Mesh.PrimitiveType.Triangles, surfaceArray ); return arrayMesh; } } }