// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using System.Text;
using Unity.Collections;
using Unity.Mathematics;
namespace MagicaCloth2
{
///
/// MagicaClothで利用する仮想メッシュ構造
/// ・メッシュの親子関係により頂点が連動する
/// ・トライアングルだけでなくライン構造も認める
/// ・スレッドで利用できるようにする
/// ・頂点数は最大65535に制限、ただし一部を除きインデックスはintで扱う
///
public partial class VirtualMesh : IDisposable, IValid
{
public string name = string.Empty;
public ResultCode result = new ResultCode();
public bool isManaged; // PreBuild DeserializeManager管理
///
/// メッシュタイプ
///
public enum MeshType
{
///
/// 通常のメッシュ
/// 座標計算:Transformからスキニング、法線接線計算:Transformからスキニング
///
NormalMesh = 0,
///
/// Transform連動メッシュ
/// 座標計算:Transform直値、法線接線計算:Transform直値
///
NormalBoneMesh = 1,
///
/// プロキシメッシュ
/// 法線接線の計算を接続するトライアングルから求める
/// 座標計算:Transformからスキニング、法線接線計算:接続トライアングル平均化 or スキニング直値
///
ProxyMesh = 2,
///
/// Transform連動プロキシメッシュ
/// シミュレーション前にTransformの復元を行い、シミュレーション後にTransformを書き込む
/// 座標計算:Transform直値、法線接線計算:接続トライアングル平均化 or Transform直値
///
ProxyBoneMesh = 3,
///
/// マッピングメッシュ(MeshClothのみ)
/// メッシュがプロキシメッシュにマッピングされた状態
/// 座標計算:接続ProxyMesh頂点からスキニング、法線接線計算:接続ProxyMeshからスキニング
///
Mapping = 4,
}
public MeshType meshType = MeshType.NormalMesh;
///
/// Transformと直接連動するかどうか(=BoneCloth)
///
public bool isBoneCloth = false;
//=========================================================================================
// メッシュ情報(基本)
//=========================================================================================
///
/// 現在の頂点が指す元の頂点インデックス
///
public ExSimpleNativeArray referenceIndices = new ExSimpleNativeArray();
///
/// 頂点属性
///
public ExSimpleNativeArray attributes = new ExSimpleNativeArray();
public ExSimpleNativeArray localPositions = new ExSimpleNativeArray();
public ExSimpleNativeArray localNormals = new ExSimpleNativeArray();
public ExSimpleNativeArray localTangents = new ExSimpleNativeArray();
///
/// VirtualMeshのUVはTangent計算用でありテクスチャマッピング用ではないので注意!
///
public ExSimpleNativeArray uv = new ExSimpleNativeArray();
public ExSimpleNativeArray boneWeights = new ExSimpleNativeArray();
public ExSimpleNativeArray triangles = new ExSimpleNativeArray();
public ExSimpleNativeArray lines = new ExSimpleNativeArray();
///
/// メッシュの基準トランスフォーム
///
public int centerTransformIndex = -1;
///
/// このメッシュの基準マトリックスと回転
///
public float4x4 initLocalToWorld;
public float4x4 initWorldToLocal;
public quaternion initRotation;
public quaternion initInverseRotation;
public float3 initScale;
///
/// 計算用スケール(X軸のみで判定)
///
public float InitCalcScale => initScale.x;
///
/// スキニングルートボーン
///
public int skinRootIndex = -1;
///
/// スキニングボーン
///
public ExSimpleNativeArray skinBoneTransformIndices = new ExSimpleNativeArray();
public ExSimpleNativeArray skinBoneBindPoses = new ExSimpleNativeArray();
///
/// このメッシュで利用するTransformの情報
///
public TransformData transformData;
///
/// バウンディングボックス
///
public NativeReference boundingBox;
///
/// 頂点の平均接続距離
///
public NativeReference averageVertexDistance;
///
/// 頂点の最大接続距離
///
public NativeReference maxVertexDistance;
public bool IsSuccess => result.IsSuccess();
public bool IsError => result.IsError();
public bool IsProcess => result.IsProcess();
public int VertexCount => localPositions.Count;
public int TriangleCount => triangles.Count;
public int LineCount => lines.Count;
public int SkinBoneCount => skinBoneTransformIndices.Count;
public int TransformCount => transformData?.Count ?? 0;
public bool IsProxy => meshType == MeshType.ProxyMesh || meshType == MeshType.ProxyBoneMesh;
public bool IsMapping => meshType == MeshType.Mapping;
//=========================================================================================
// 結合/リダクション
//=========================================================================================
///
/// 結合された頂点範囲(結合もとメッシュが保持する)
///
public DataChunk mergeChunk;
///
/// 元の頂点からの結合とリダクション後の頂点へのインデックス
///
public NativeArray joinIndices;
//=========================================================================================
// プロキシメッシュ
//=========================================================================================
///
/// 頂点ごとの接続トライアングルインデックスと法線接線フリップフラグ(最大7つ)
/// これは法線を再計算するために用いられるもので7つあれば十分であると判断したもの。
/// そのため正確なトライアングル接続を表していない。
/// データは12-20bitのuintでパックされている
/// 12(hi) = 法線接線のフリップフラグ(法線:0x1,接線:0x2)。ONの場合フリップ。
/// 20(low) = トライアングルインデックス。
///
public NativeArray> vertexToTriangles;
///
/// 頂点ごとの接続頂点インデックス
/// vertexToVertexIndexArrayは頂点ごとのデータ開始インデックス(22bit)とカウンタ(10bit)を1つのuintに結合したもの
/// todo:ここはMultiHashMapのままで良いかもしれない
///
public NativeArray vertexToVertexIndexArray;
public NativeArray vertexToVertexDataArray;
///
/// エッジリスト
///
public NativeArray edges;
///
/// エッジ固有フラグ
///
public const byte EdgeFlag_Cut = 0x1; // 切り口エッジ
public NativeArray edgeFlags;
///
/// エッジごとの接続トライアングルインデックス
///
public NativeParallelMultiHashMap edgeToTriangles;
///
/// 頂点ごとのバインドポーズ
/// 頂点バインドにはスケール値は不要
///
public NativeArray vertexBindPosePositions;
public NativeArray vertexBindPoseRotations;
///
/// 頂点ごとの計算姿勢からTransformへの変換回転(BoneClothのみ)
///
public NativeArray vertexToTransformRotations;
///
/// 各頂点のレベル(起点は0から)
///
//public NativeArray vertexLevels;
///
/// 各頂点の深さ(0.0-1.0)
///
public NativeArray vertexDepths;
///
/// 各頂点のルートインデックス(-1=なし)
///
public NativeArray vertexRootIndices;
///
/// 各頂点の親頂点インデックス(-1=なし)
///
public NativeArray vertexParentIndices;
///
/// 各頂点の子頂点インデックスリスト
///
public NativeArray vertexChildIndexArray;
public NativeArray vertexChildDataArray;
///
/// 各頂点の親からの基準ローカル座標
///
public NativeArray vertexLocalPositions;
///
/// 各頂点の親からの基準ローカル回転
///
public NativeArray vertexLocalRotations;
///
/// 法線調整用回転
///
public NativeArray normalAdjustmentRotations;
#if false
///
/// 角度計算用基準法線の計算方法
///
public enum NormalCalcMode
{
Auto = 0,
X_Axis = 1,
Y_Axis = 2,
Z_Axis = 3,
Point_Outside = 4,
Point_Inside = 5,
//Line_Outside = 6,
//Line_Inside = 7,
}
//public NormalCalcMode normalCalcMode = NormalCalcMode.Auto;
///
/// 各頂点の角度計算用基準ローカル回転
/// -AngleLimitConstraintで使用する
/// pitch/yaw個別制限はv1.0では実装しないので一旦停止させる
///
//public NativeArray vertexAngleCalcLocalRotations;
#endif
///
/// 最大レベル値
///
//public NativeReference vertexMaxLevel;
///
/// ベースラインごとのフラグ
///
public const byte BaseLineFlag_IncludeLine = 0x01; // ラインを含む
public NativeArray baseLineFlags;
///
/// ベースラインごとのデータ開始インデックス
///
public NativeArray baseLineStartDataIndices;
///
/// ベースラインごとのデータ数
///
public NativeArray baseLineDataCounts;
///
/// ベースラインデータ(頂点インデックス)
///
public NativeArray baseLineData;
///
/// カスタムスキニングボーンの登録トランスフォームインデックス
///
public int[] customSkinningBoneIndices;
///
/// センター計算用の固定頂点リスト
///
public ushort[] centerFixedList;
///
/// プロキシメッシュ構築時のセンター位置(ローカル)
/// 固定頂点が無い場合は(0,0,0)
///
public NativeReference localCenterPosition;
public int BaseLineCount => baseLineStartDataIndices.IsCreated ? baseLineStartDataIndices.Length : 0;
public int EdgeCount => edges.IsCreated ? edges.Length : 0;
public int CustomSkinningBoneCount => customSkinningBoneIndices?.Length ?? 0;
public int CenterFixedPointCount => centerFixedList?.Length ?? 0;
public int NormalAdjustmentRotationCount => normalAdjustmentRotations.IsCreated ? normalAdjustmentRotations.Length : 0;
//=========================================================================================
// マッピングメッシュ
//=========================================================================================
///
/// 接続しているプロキシメッシュ
///
public VirtualMesh mappingProxyMesh;
///
/// マッピングメッシュのセンタートランスフォーム情報
/// 毎フレーム更新される
/// Meshの場合はレンダラーのトランスフォームとなる
///
public float3 centerWorldPosition;
public quaternion centerWorldRotation;
public float3 centerWorldScale;
///
/// 初期状態でのマッピングメッシュへの変換マトリックスと変換回転
/// この姿勢は初期化時に固定される
///
public float4x4 toProxyMatrix;
public quaternion toProxyRotation;
///
/// マネージャへの登録ID
///
public int mappingId;
//=========================================================================================
public VirtualMesh() { }
public VirtualMesh(bool initialize)
{
if (initialize)
{
transformData = new TransformData(100);
// 最小限のデータ
averageVertexDistance = new NativeReference(0.0f, Allocator.Persistent);
maxVertexDistance = new NativeReference(0.0f, Allocator.Persistent);
// 作業中にしておく
result.SetProcess();
}
}
public VirtualMesh(string name) : this(true)
{
this.name = name;
}
public void Dispose()
{
// PreBuild DeserializeManager管理中は破棄させない
if (isManaged)
return;
result.Clear();
referenceIndices.Dispose();
attributes.Dispose();
localPositions.Dispose();
localNormals.Dispose();
localTangents.Dispose();
uv.Dispose();
boneWeights.Dispose();
triangles.Dispose();
lines.Dispose();
skinBoneTransformIndices.Dispose();
skinBoneBindPoses.Dispose();
if (joinIndices.IsCreated)
joinIndices.Dispose();
if (vertexToTriangles.IsCreated)
vertexToTriangles.Dispose();
if (vertexToVertexIndexArray.IsCreated)
vertexToVertexIndexArray.Dispose();
if (vertexToVertexDataArray.IsCreated)
vertexToVertexDataArray.Dispose();
if (edges.IsCreated)
edges.Dispose();
if (edgeFlags.IsCreated)
edgeFlags.Dispose();
if (edgeToTriangles.IsCreated)
edgeToTriangles.Dispose();
if (vertexBindPosePositions.IsCreated)
vertexBindPosePositions.Dispose();
if (vertexBindPoseRotations.IsCreated)
vertexBindPoseRotations.Dispose();
if (vertexToTransformRotations.IsCreated)
vertexToTransformRotations.Dispose();
if (vertexRootIndices.IsCreated)
vertexRootIndices.Dispose();
if (vertexParentIndices.IsCreated)
vertexParentIndices.Dispose();
if (vertexChildIndexArray.IsCreated)
vertexChildIndexArray.Dispose();
if (vertexChildDataArray.IsCreated)
vertexChildDataArray.Dispose();
if (baseLineFlags.IsCreated)
baseLineFlags.Dispose();
if (baseLineStartDataIndices.IsCreated)
baseLineStartDataIndices.Dispose();
if (baseLineDataCounts.IsCreated)
baseLineDataCounts.Dispose();
if (baseLineData.IsCreated)
baseLineData.Dispose();
if (localCenterPosition.IsCreated)
localCenterPosition.Dispose();
if (vertexLocalPositions.IsCreated)
vertexLocalPositions.Dispose();
if (vertexLocalRotations.IsCreated)
vertexLocalRotations.Dispose();
if (normalAdjustmentRotations.IsCreated)
normalAdjustmentRotations.Dispose();
//if (vertexAngleCalcLocalRotations.IsCreated)
// vertexAngleCalcLocalRotations.Dispose();
//if (vertexMaxLevel.IsCreated)
// vertexMaxLevel.Dispose();
//if (vertexLevels.IsCreated)
// vertexLevels.Dispose();
if (vertexDepths.IsCreated)
vertexDepths.Dispose();
if (boundingBox.IsCreated)
boundingBox.Dispose();
if (averageVertexDistance.IsCreated)
averageVertexDistance.Dispose();
if (maxVertexDistance.IsCreated)
maxVertexDistance.Dispose();
transformData?.Dispose();
}
public void SetName(string newName)
{
name = newName;
}
///
/// 最低限のデータ検証
///
///
public bool IsValid()
{
if (transformData == null)
return false;
// レンダラーが存在する場合はその存在を確認する
// ただしPreBuildではtransformListは空なのでスキップする
if (centerTransformIndex >= 0 && transformData.IsEmpty == false && transformData.GetTransformFromIndex(centerTransformIndex) == null)
return false;
return true;
}
//=========================================================================================
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"===== {name} =====");
sb.AppendLine($"Result:{result.GetResultString()}");
sb.AppendLine($"Type:{meshType}");
sb.AppendLine($"Vertex:{VertexCount}");
sb.AppendLine($"Line:{LineCount}");
sb.AppendLine($"Triangle:{TriangleCount}");
sb.AppendLine($"Edge:{EdgeCount}");
sb.AppendLine($"SkinBone:{SkinBoneCount}");
sb.AppendLine($"Transform:{TransformCount}");
if (averageVertexDistance.IsCreated)
sb.AppendLine($"avgDist:{averageVertexDistance.Value}");
if (maxVertexDistance.IsCreated)
sb.AppendLine($"maxDist:{maxVertexDistance.Value}");
if (boundingBox.IsCreated)
sb.AppendLine($"AABB:{boundingBox.Value}");
sb.AppendLine();
sb.AppendLine($"<<< Proxy >>>");
sb.AppendLine($"BaseLine:{BaseLineCount}");
sb.AppendLine($"EdgeCount:{EdgeCount}");
int edgeToTrianglesCnt = edgeToTriangles.IsCreated ? edgeToTriangles.Count() : 0;
sb.AppendLine($"edgeToTriangles:{edgeToTrianglesCnt}");
sb.AppendLine($"CustomSkinningBoneCount:{CustomSkinningBoneCount}");
sb.AppendLine($"CenterFixedPointCount:{CenterFixedPointCount}");
sb.AppendLine($"NormalAdjustmentRotationCount:{NormalAdjustmentRotationCount}");
sb.AppendLine();
sb.AppendLine($"<<< Mapping >>>");
sb.AppendLine($"centerWorldPosition:{centerWorldPosition}");
sb.AppendLine();
//sb.AppendLine($"<<< TransformData >>>");
sb.AppendLine(transformData?.ToString() ?? "(none)");
sb.AppendLine();
return sb.ToString();
}
}
}