327 lines
11 KiB
C#
327 lines
11 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using System.Linq;
|
||
|
||
namespace StudioKafka.MeshSyncPro
|
||
{
|
||
public class MeshSyncProManager : MonoBehaviour
|
||
{
|
||
#region Zone型の定義
|
||
|
||
[Serializable]
|
||
public class Zone
|
||
{
|
||
public Vector3 center;
|
||
public Vector3 size;
|
||
public bool active;
|
||
public Color color;
|
||
|
||
public Zone()
|
||
{
|
||
center = Vector3.zero;
|
||
size = Vector3.one;
|
||
active = true;
|
||
color = Color.green;
|
||
}
|
||
|
||
public Zone(Vector3 zoneCenter, Vector3 zoneSize, bool isActive = true)
|
||
{
|
||
center = zoneCenter;
|
||
size = zoneSize;
|
||
active = isActive;
|
||
color = Color.green;
|
||
}
|
||
|
||
public bool ContainsPoint(Vector3 point)
|
||
{
|
||
Vector3 min = center - size * 0.5f;
|
||
Vector3 max = center + size * 0.5f;
|
||
return point.x >= min.x && point.x <= max.x &&
|
||
point.y >= min.y && point.y <= max.y &&
|
||
point.z >= min.z && point.z <= max.z;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region パブリックプロパティ(UIから設定される)
|
||
|
||
[Header("対象オブジェクト")]
|
||
public List<GameObject> bodyObjects = new List<GameObject>();
|
||
public List<GameObject> clothingObjects = new List<GameObject>();
|
||
|
||
[Header("検出設定")]
|
||
[Range(0.0001f, 0.1f)]
|
||
public float penetrationThreshold = 0.001f;
|
||
|
||
[Range(-1f, 1f)]
|
||
public float dotProductThreshold = 0.8f;
|
||
|
||
[Header("修正設定")]
|
||
[Range(0f, 10f)]
|
||
public float influenceRadiusSteps = 1f;
|
||
|
||
[Range(0, 10)]
|
||
public int smoothingIterations = 2;
|
||
|
||
[Range(0f, 1f)]
|
||
public float smoothingFactor = 0.1f;
|
||
|
||
[Header("検出ゾーン")]
|
||
public Zone[] DetectionZones = new Zone[0];
|
||
|
||
#endregion
|
||
|
||
#region プライベートフィールド
|
||
|
||
private Dictionary<(GameObject, GameObject), int[]> penetratingIndicesResults =
|
||
new Dictionary<(GameObject, GameObject), int[]>();
|
||
private Dictionary<(GameObject, GameObject), Vector3[]> penetratingVerticesResults =
|
||
new Dictionary<(GameObject, GameObject), Vector3[]>();
|
||
private bool isInitialized = false;
|
||
|
||
#endregion
|
||
|
||
#region Unity ライフサイクル
|
||
|
||
private void Awake()
|
||
{
|
||
Initialize();
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
if (DetectionZones == null || DetectionZones.Length == 0)
|
||
{
|
||
CreateDefaultZone();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 初期化
|
||
|
||
private void Initialize()
|
||
{
|
||
if (isInitialized) return;
|
||
|
||
penetratingIndicesResults = new Dictionary<(GameObject, GameObject), int[]>();
|
||
penetratingVerticesResults = new Dictionary<(GameObject, GameObject), Vector3[]>();
|
||
isInitialized = true;
|
||
|
||
Debug.Log("[MeshSyncProManager] マネージャーが初期化されました");
|
||
}
|
||
|
||
private void CreateDefaultZone()
|
||
{
|
||
DetectionZones = new Zone[]
|
||
{
|
||
new Zone(new Vector3(0f, 0.872f, -0.12f), new Vector3(0.2f, 0.07f, 0.2f))
|
||
{
|
||
color = new Color(0f, 1f, 0f, 0.25f)
|
||
}
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region メイン機能(UIから呼び出される)
|
||
|
||
public void ApplyMeshCorrections()
|
||
{
|
||
Debug.Log("[MeshSyncProManager] 貫通検出を開始します");
|
||
ClearResults();
|
||
|
||
if (!ValidateInputs())
|
||
{
|
||
Debug.LogWarning("[MeshSyncProManager] 入力が無効です。検出を中止します");
|
||
return;
|
||
}
|
||
|
||
int totalDetections = 0;
|
||
|
||
foreach (GameObject bodyObj in bodyObjects.Where(obj => obj != null))
|
||
{
|
||
SkinnedMeshRenderer bodyRenderer = bodyObj.GetComponent<SkinnedMeshRenderer>();
|
||
if (bodyRenderer == null) continue;
|
||
|
||
foreach (GameObject clothingObj in clothingObjects.Where(obj => obj != null))
|
||
{
|
||
Mesh clothingMesh = GetMeshFromGameObject(clothingObj);
|
||
if (clothingMesh == null) continue;
|
||
|
||
var detectionResult = Runtime.PenetrationDetectionCore.DetectPenetration(
|
||
bodyRenderer,
|
||
clothingMesh,
|
||
clothingObj.transform,
|
||
penetrationThreshold,
|
||
dotProductThreshold
|
||
);
|
||
|
||
if (detectionResult.IsSuccessful && detectionResult.PenetratingIndices.Length > 0)
|
||
{
|
||
var filteredResult = FilterByZones(detectionResult);
|
||
var key = (bodyObj, clothingObj);
|
||
penetratingIndicesResults[key] = filteredResult.PenetratingIndices;
|
||
penetratingVerticesResults[key] = filteredResult.PenetratingVertices;
|
||
totalDetections += filteredResult.PenetratingIndices.Length;
|
||
|
||
Debug.Log($"[MeshSyncProManager] {bodyObj.name} と {clothingObj.name} 間で {filteredResult.PenetratingIndices.Length} 個の貫通を検出");
|
||
}
|
||
}
|
||
}
|
||
|
||
Debug.Log($"[MeshSyncProManager] 検出完了: 合計 {totalDetections} 個の貫通頂点を検出しました");
|
||
}
|
||
|
||
public void FixDetectedPenetrations(float pushOutDistance)
|
||
{
|
||
Debug.Log($"[MeshSyncProManager] 貫通修正を開始します(押し出し距離: {pushOutDistance:F4})");
|
||
int totalFixed = 0;
|
||
|
||
foreach (var result in penetratingIndicesResults)
|
||
{
|
||
var key = result.Key;
|
||
GameObject bodyObj = key.Item1;
|
||
int[] penetratingIndices = result.Value;
|
||
|
||
if (bodyObj != null && penetratingIndices != null && penetratingIndices.Length > 0)
|
||
{
|
||
SkinnedMeshRenderer bodyRenderer = bodyObj.GetComponent<SkinnedMeshRenderer>();
|
||
if (bodyRenderer != null)
|
||
{
|
||
Runtime.PenetrationFixEngine.FixPenetrationAdvanced(
|
||
bodyRenderer,
|
||
penetratingIndices,
|
||
pushOutDistance,
|
||
influenceRadiusSteps,
|
||
smoothingIterations,
|
||
smoothingFactor
|
||
);
|
||
|
||
totalFixed += penetratingIndices.Length;
|
||
Debug.Log($"[MeshSyncProManager] {bodyObj.name} の {penetratingIndices.Length} 個の頂点を修正しました");
|
||
}
|
||
}
|
||
}
|
||
|
||
ClearResults();
|
||
Debug.Log($"[MeshSyncProManager] 修正完了: 合計 {totalFixed} 個の頂点を修正しました");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 結果取得メソッド(UIから呼び出される)
|
||
|
||
public Dictionary<(GameObject, GameObject), int[]> GetPenetratingIndicesResults()
|
||
{
|
||
return new Dictionary<(GameObject, GameObject), int[]>(penetratingIndicesResults);
|
||
}
|
||
|
||
public Dictionary<(GameObject, GameObject), Vector3[]> GetPenetratingVerticesResults()
|
||
{
|
||
return new Dictionary<(GameObject, GameObject), Vector3[]>(penetratingVerticesResults);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ヘルパーメソッド
|
||
|
||
private bool ValidateInputs()
|
||
{
|
||
if (bodyObjects == null || bodyObjects.Count == 0)
|
||
{
|
||
Debug.LogError("[MeshSyncProManager] ボディオブジェクトが設定されていません");
|
||
return false;
|
||
}
|
||
|
||
if (clothingObjects == null || clothingObjects.Count == 0)
|
||
{
|
||
Debug.LogError("[MeshSyncProManager] 衣装オブジェクトが設定されていません");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private Mesh GetMeshFromGameObject(GameObject obj)
|
||
{
|
||
SkinnedMeshRenderer smr = obj.GetComponent<SkinnedMeshRenderer>();
|
||
if (smr != null && smr.sharedMesh != null)
|
||
{
|
||
return smr.sharedMesh;
|
||
}
|
||
|
||
MeshFilter mf = obj.GetComponent<MeshFilter>();
|
||
if (mf != null && mf.sharedMesh != null)
|
||
{
|
||
return mf.sharedMesh;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private Runtime.PenetrationDetectionCore.DetectionResult FilterByZones(Runtime.PenetrationDetectionCore.DetectionResult originalResult)
|
||
{
|
||
var activeZones = DetectionZones?.Where(z => z != null && z.active).ToArray();
|
||
if (activeZones == null || activeZones.Length == 0)
|
||
{
|
||
return originalResult;
|
||
}
|
||
|
||
var filteredIndices = new List<int>();
|
||
var filteredVertices = new List<Vector3>();
|
||
|
||
for (int i = 0; i < originalResult.PenetratingIndices.Length; i++)
|
||
{
|
||
Vector3 vertex = originalResult.PenetratingVertices[i];
|
||
bool isInAnyZone = activeZones.Any(zone => zone.ContainsPoint(vertex));
|
||
|
||
if (isInAnyZone)
|
||
{
|
||
filteredIndices.Add(originalResult.PenetratingIndices[i]);
|
||
filteredVertices.Add(originalResult.PenetratingVertices[i]);
|
||
}
|
||
}
|
||
|
||
return new Runtime.PenetrationDetectionCore.DetectionResult
|
||
{
|
||
IsSuccessful = filteredIndices.Count > 0,
|
||
PenetratingIndices = filteredIndices.ToArray(),
|
||
PenetratingVertices = filteredVertices.ToArray(),
|
||
RawPenetratingVertices = originalResult.RawPenetratingVertices,
|
||
ErrorMessage = originalResult.ErrorMessage,
|
||
DebugInfo = originalResult.DebugInfo
|
||
};
|
||
}
|
||
|
||
private void ClearResults()
|
||
{
|
||
penetratingIndicesResults.Clear();
|
||
penetratingVerticesResults.Clear();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region デバッグ
|
||
|
||
private void OnDrawGizmos()
|
||
{
|
||
if (DetectionZones != null)
|
||
{
|
||
foreach (var zone in DetectionZones)
|
||
{
|
||
if (zone != null && zone.active)
|
||
{
|
||
Gizmos.color = zone.color;
|
||
Gizmos.DrawWireCube(zone.center, zone.size);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|