348 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace StudioKafka.MeshSyncPro.Runtime
{
public static class PenetrationFixEngine
{
private static Dictionary<int, Mesh> _originalMeshCache = new Dictionary<int, Mesh>();
private static Dictionary<int, SkinnedMeshRenderer> _rendererCache = new Dictionary<int, SkinnedMeshRenderer>();
#region APIUndo対応版
public static void FixPenetration(
SkinnedMeshRenderer targetRenderer,
int[] penetratingIndices,
float pushDistance,
int smoothingIterations)
{
if (targetRenderer == null || targetRenderer.sharedMesh == null || penetratingIndices == null || penetratingIndices.Length == 0)
{
Debug.LogWarning("[PenetrationFixEngine] Basic fix に対する入力が無効です。");
return;
}
Debug.Log($"[PenetrationFixEngine] Basic Fix 開始: {targetRenderer.name}, 対象頂点数: {penetratingIndices.Length}");
var meshInstanceId = RegisterMeshForUndo(targetRenderer, "Fix Mesh Penetration (Basic)");
Mesh mesh = targetRenderer.sharedMesh;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
if (normals.Length != vertices.Length)
{
mesh.RecalculateNormals();
normals = mesh.normals;
}
foreach (int index in penetratingIndices)
{
if (index >= 0 && index < vertices.Length && index < normals.Length)
{
Vector3 normal = normals[index];
if (normal.sqrMagnitude > 0.000001f)
{
vertices[index] -= normal.normalized * pushDistance;
}
}
}
if (smoothingIterations > 0)
{
ApplySmoothing(ref vertices, mesh.triangles, penetratingIndices, smoothingIterations);
}
ApplyMeshChanges(mesh, vertices);
Debug.Log($"[PenetrationFixEngine] Basic Fix 完了: {penetratingIndices.Length} 個の頂点を修正しました。");
}
public static void FixPenetrationAdvanced(
SkinnedMeshRenderer targetRenderer,
int[] penetratingIndices,
float pushDistance,
float influenceRadiusSteps,
int smoothingIterations,
float smoothingFactor)
{
if (targetRenderer == null || targetRenderer.sharedMesh == null || penetratingIndices == null || penetratingIndices.Length == 0)
{
Debug.LogWarning("[PenetrationFixEngine] Advanced fix に対する入力が無効です。");
return;
}
Debug.Log($"[PenetrationFixEngine] Advanced Fix 開始: {targetRenderer.name}, 対象頂点数: {penetratingIndices.Length}");
var meshInstanceId = RegisterMeshForUndo(targetRenderer, "Fix Mesh Penetration (Advanced)");
Mesh mesh = targetRenderer.sharedMesh;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
if (normals.Length != vertices.Length)
{
mesh.RecalculateNormals();
normals = mesh.normals;
}
var adjacencyMap = BuildVertexAdjacencyMap(mesh.triangles, vertices.Length);
var influencedVertices = new Dictionary<int, float>();
foreach (int penetratingIndex in penetratingIndices)
{
Queue<(int index, int step)> queue = new Queue<(int, int)>();
queue.Enqueue((penetratingIndex, 0));
HashSet<int> visited = new HashSet<int> { penetratingIndex };
influencedVertices[penetratingIndex] = 1.0f;
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (current.step >= (int)influenceRadiusSteps) continue;
if (adjacencyMap.TryGetValue(current.index, out var neighbors))
{
foreach (int neighborIndex in neighbors)
{
if (visited.Add(neighborIndex))
{
float influence = (influenceRadiusSteps <= 0) ? 1.0f :
(1.0f - (float)(current.step + 1) / (influenceRadiusSteps + 1.0f));
if (!influencedVertices.ContainsKey(neighborIndex) || influence > influencedVertices[neighborIndex])
{
influencedVertices[neighborIndex] = influence;
}
queue.Enqueue((neighborIndex, current.step + 1));
}
}
}
}
}
foreach (var pair in influencedVertices)
{
int index = pair.Key;
float influence = pair.Value;
if (index >= 0 && index < vertices.Length && index < normals.Length)
{
Vector3 normal = normals[index];
if (normal.sqrMagnitude > 0.000001f)
{
vertices[index] -= normal.normalized * (pushDistance * influence);
}
}
}
if (smoothingIterations > 0)
{
ApplyAdvancedSmoothing(ref vertices, adjacencyMap, influencedVertices.Keys.ToArray(), smoothingIterations, smoothingFactor);
}
ApplyMeshChanges(mesh, vertices);
Debug.Log($"[PenetrationFixEngine] Advanced Fix 完了。影響を受けた頂点数: {influencedVertices.Count}");
}
#endregion
#region Undo対応メッシュ管理システム
private static int RegisterMeshForUndo(SkinnedMeshRenderer renderer, string undoName)
{
var originalMesh = renderer.sharedMesh;
var meshInstanceId = originalMesh.GetInstanceID();
if (_originalMeshCache.ContainsKey(meshInstanceId))
{
return meshInstanceId;
}
#if UNITY_EDITOR
var meshBackup = Object.Instantiate(originalMesh);
meshBackup.name = originalMesh.name + "_UndoBackup";
_originalMeshCache[meshInstanceId] = meshBackup;
_rendererCache[meshInstanceId] = renderer;
var workingMesh = Object.Instantiate(originalMesh);
workingMesh.name = originalMesh.name + "_Working";
Undo.RecordObject(renderer, undoName);
renderer.sharedMesh = workingMesh;
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
Undo.undoRedoPerformed += OnUndoRedoPerformed;
#endif
return meshInstanceId;
}
private static void OnUndoRedoPerformed()
{
#if UNITY_EDITOR
var keysToRemove = new List<int>();
foreach (var kvp in _rendererCache)
{
var meshInstanceId = kvp.Key;
var renderer = kvp.Value;
if (renderer == null)
{
keysToRemove.Add(meshInstanceId);
continue;
}
if (_originalMeshCache.TryGetValue(meshInstanceId, out var originalMesh))
{
if (renderer.sharedMesh != null && renderer.sharedMesh.name.Contains("_Working"))
{
Object.DestroyImmediate(renderer.sharedMesh);
}
renderer.sharedMesh = originalMesh;
keysToRemove.Add(meshInstanceId);
Debug.Log($"[PenetrationFixEngine] Undo実行: {renderer.name} のメッシュを元の状態に復元しました");
}
}
foreach (var key in keysToRemove)
{
if (_originalMeshCache.ContainsKey(key))
{
_originalMeshCache.Remove(key);
}
_rendererCache.Remove(key);
}
SceneView.RepaintAll();
#endif
}
private static void ApplyMeshChanges(Mesh mesh, Vector3[] vertices)
{
mesh.vertices = vertices;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
#if UNITY_EDITOR
EditorUtility.SetDirty(mesh);
#endif
}
#endregion
#region
private static void ApplySmoothing(ref Vector3[] vertices, int[] triangles, int[] targetIndices, int iterations)
{
var adjacencyMap = BuildVertexAdjacencyMap(triangles, vertices.Length);
var targetVertexSet = new HashSet<int>(targetIndices);
for (int iter = 0; iter < iterations; iter++)
{
Vector3[] tempVertices = (Vector3[])vertices.Clone();
foreach (int index in targetVertexSet)
{
if (adjacencyMap.TryGetValue(index, out var neighbors) && neighbors.Count > 0)
{
Vector3 averagePos = vertices[index];
foreach (int neighborIdx in neighbors) averagePos += vertices[neighborIdx];
tempVertices[index] = averagePos / (neighbors.Count + 1);
}
}
vertices = tempVertices;
}
}
private static void ApplyAdvancedSmoothing(ref Vector3[] vertices, Dictionary<int, List<int>> adjacencyMap,
int[] targetIndices, int iterations, float smoothingFactor)
{
var smoothingTargetIndices = new HashSet<int>(targetIndices);
for (int iter = 0; iter < iterations; iter++)
{
Vector3[] tempVertices = (Vector3[])vertices.Clone();
foreach (int index in smoothingTargetIndices)
{
if (adjacencyMap.TryGetValue(index, out var neighbors) && neighbors.Count > 0)
{
Vector3 averagePos = Vector3.zero;
foreach (int neighborIdx in neighbors) averagePos += vertices[neighborIdx];
averagePos /= neighbors.Count;
tempVertices[index] = Vector3.Lerp(vertices[index], averagePos, smoothingFactor);
}
}
vertices = tempVertices;
}
}
private static Dictionary<int, List<int>> BuildVertexAdjacencyMap(int[] triangles, int vertexCount)
{
var map = new Dictionary<int, List<int>>();
for (int i = 0; i < vertexCount; i++)
map[i] = new List<int>();
for (int i = 0; i < triangles.Length; i += 3)
{
int v0 = triangles[i], v1 = triangles[i + 1], v2 = triangles[i + 2];
if (v0 < vertexCount && v1 < vertexCount && v2 < vertexCount)
{
if (!map[v0].Contains(v1)) map[v0].Add(v1);
if (!map[v0].Contains(v2)) map[v0].Add(v2);
if (!map[v1].Contains(v0)) map[v1].Add(v0);
if (!map[v1].Contains(v2)) map[v1].Add(v2);
if (!map[v2].Contains(v0)) map[v2].Add(v0);
if (!map[v2].Contains(v1)) map[v2].Add(v1);
}
}
return map;
}
public static void ClearCache()
{
#if UNITY_EDITOR
foreach (var kvp in _rendererCache)
{
var renderer = kvp.Value;
if (renderer != null && renderer.sharedMesh != null && renderer.sharedMesh.name.Contains("_Working"))
{
Object.DestroyImmediate(renderer.sharedMesh);
}
}
foreach (var backupMesh in _originalMeshCache.Values)
{
if (backupMesh != null)
{
Object.DestroyImmediate(backupMesh);
}
}
_originalMeshCache.Clear();
_rendererCache.Clear();
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
#endif
Debug.Log("[PenetrationFixEngine] キャッシュとUndoシステムをクリアしました");
}
#endregion
}
}