rj-action-library/Runtime/Procedural/MeshGeometry.cs

625 lines
14 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class MeshGeometry
{
public List<Vector3> vertices = new List<Vector3>();
public List<int> indices = new List<int>();
public List<Vector3> normals;
public List<Vector2> uvs;
public List<Vector2> uv2s;
public List<Color> 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<Vector3>( vertices.AsVector3Array() );
}
var normals = arrays[ (int) Mesh.ArrayType.Normal ];
if ( Variant.Type.Nil != normals.VariantType )
{
mg.normals = new List<Vector3>( normals.AsVector3Array() );
}
var uvs = arrays[ (int) Mesh.ArrayType.TexUV ];
if ( Variant.Type.Nil != uvs.VariantType )
{
mg.uvs = new List<Vector2>( uvs.AsVector2Array() );
}
var uv2s = arrays[ (int) Mesh.ArrayType.TexUV2 ];
if ( Variant.Type.Nil != uv2s.VariantType )
{
mg.uv2s = new List<Vector2>( uv2s.AsVector2Array() );
}
var colors = arrays[ (int) Mesh.ArrayType.Color ];
if ( Variant.Type.Nil != colors.VariantType )
{
mg.colors = new List<Color>( colors.AsColorArray() );
}
var indices = arrays[ (int) Mesh.ArrayType.Index ];
if ( Variant.Type.Nil != indices.VariantType )
{
mg.indices = new List<int>( 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<int,int,int,int> 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<Vector2,Pose> 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<Color>();
}
if ( uv2s != null )
{
mg.uv2s = new List<Vector2>();
}
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<Vector3>() : null;
this.uvs = uvs ? new List<Vector2>() : null;
this.uv2s = uvs2 ? new List<Vector2>() : null;
this.colors = colors ? new List<Color>() : 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<int,Vector3>();
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<int,int>();
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<Vector3>
{
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;
}
}
}