fix GltfMeshUtility.WriteAssets

This commit is contained in:
ousttrue
2023-12-01 17:06:31 +09:00
parent f8f27d0619
commit f84a6ccb73
13 changed files with 300 additions and 130 deletions

View File

@@ -3,7 +3,7 @@ using UnityEditor;
using UniGLTF.M17N;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System;
namespace UniGLTF.MeshUtility
@@ -179,54 +179,50 @@ namespace UniGLTF.MeshUtility
{
/// [prefab]
///
/// * backup するのではなく 変更した copy を作成する。元は変えない
/// * copy 先の統合前の renderer を disable で残さず destroy する
/// * 実行すると mesh, blendshape, blendShape を新規に作成する
/// * 新しいヒエラルキーを prefab に保存してから削除して終了する
// 出力フォルダを決める
var folder = "Assets";
var prefab = _exportTarget.GetPrefab();
if (prefab != null)
/// * prefab から instance を作る
/// * instance に対して 焼き付け, 統合, 分離 を実行する
/// * instance のヒエラルキーが改変され、mesh 等のアセットは改変版が作成される(元は変わらない)
/// * instance を asset に保存してから prefab を削除して終了する
///
UnityPath assetFolder = default;
try
{
folder = AssetDatabase.GetAssetPath(prefab);
// Debug.Log(folder);
assetFolder = PrefabContext.GetOutFolder(_exportTarget);
}
// 新規で作成されるアセットはすべてこのフォルダの中に作る。上書きチェックはしない
var assetFolder = EditorUtility.SaveFolderPanel("select asset save folder", Path.GetDirectoryName(folder), "VrmIntegrated");
var unityPath = UniGLTF.UnityPath.FromFullpath(assetFolder);
if (!unityPath.IsUnderWritableFolder)
catch (Exception)
{
EditorUtility.DisplayDialog("asset folder", "Target folder must be in the Assets or writable Packages folder", "cancel");
return;
}
assetFolder = unityPath.Value;
var copy = GameObject.Instantiate(_exportTarget);
if (PrefabUtility.IsOutermostPrefabInstanceRoot(copy))
using (var context = new PrefabContext(_exportTarget, assetFolder))
{
PrefabUtility.UnpackPrefabInstance(copy, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
try
{
var (results, created) = MeshUtility.Process(_exportTarget, context.Instance);
WriteAssets(context.Instance, context.AssetFolder, results);
}
catch (Exception ex)
{
#if DEBUG
Debug.LogException(ex, context.Instance);
context.Keep = true;
#endif
}
}
var (results, created) = MeshUtility.Process(_exportTarget, copy);
WriteAssets(copy, assetFolder, results);
// destroy scene
UnityEngine.Object.DestroyImmediate(copy);
}
else
{
Undo.RegisterFullObjectHierarchyUndo(_exportTarget, "MeshUtility");
using (var context = new UndoContext("MeshUtility", _exportTarget))
{
var (results, created) = MeshUtility.Process(_exportTarget, null);
if (_exportTarget.GetPrefabType() == UnityExtensions.PrefabType.PrefabInstance)
{
PrefabUtility.UnpackPrefabInstance(_exportTarget, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
var (results, created) = MeshUtility.Process(_exportTarget, null);
foreach (var go in created)
{
Undo.RegisterCreatedObjectUndo(go, "MeshUtility");
foreach (var go in created)
{
// 処理後の mesh をアタッチした Renderer.gameobject
Undo.RegisterCreatedObjectUndo(go, "MeshUtility");
}
}
}
@@ -235,68 +231,36 @@ namespace UniGLTF.MeshUtility
}
}
void WriteAssets(GameObject copy, string assetFolder, List<MeshIntegrationResult> results)
/// <summary>
/// Write Mesh & Prefab
/// </summary>
protected virtual void WriteAssets(GameObject copy, string assetFolder, List<MeshIntegrationResult> results)
{
//
// write mesh asset
foreach (var result in results)
{
var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath);
}
// 統合した結果をヒエラルキーに追加する
foreach (var result in results)
{
if (result.Integrated.IntegratedRenderer != null)
if (result.Integrated != null)
{
result.Integrated.IntegratedRenderer.transform.SetParent(copy.transform, false);
var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath);
}
if (result.IntegratedNoBlendShape != null)
{
var childAssetPath = $"{assetFolder}/{result.IntegratedNoBlendShape.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
AssetDatabase.CreateAsset(result.IntegratedNoBlendShape.IntegratedRenderer.sharedMesh, childAssetPath);
}
}
// 統合した結果を反映した BlendShapeClip を作成して置き換える
// var clips = VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(results, copy, assetFolder);
// 用が済んだ 統合前 の renderer を削除する
foreach (var result in results)
{
foreach (var renderer in result.SourceMeshRenderers)
{
GameObject.DestroyImmediate(renderer);
}
foreach (var renderer in result.SourceSkinnedMeshRenderers)
{
GameObject.DestroyImmediate(renderer);
}
}
// reset firstperson
// var firstperson = copy.GetComponent<VRMFirstPerson>();
// if (firstperson != null)
// {
// firstperson.Reset();
// }
// prefab
var prefabPath = $"{assetFolder}/VrmIntegrated.prefab";
var prefabPath = $"{assetFolder}/Integrated.prefab";
Debug.Log(prefabPath);
PrefabUtility.SaveAsPrefabAsset(copy, prefabPath, out bool success);
if (!success)
{
throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {prefabPath}");
}
// var prefabReference = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
// foreach (var clip in clips)
// {
// var so = new SerializedObject(clip);
// so.Update();
// // clip.Prefab = copy;
// var prop = so.FindProperty("m_prefab");
// prop.objectReferenceValue = prefabReference;
// so.ApplyModifiedProperties();
// }
}
protected bool ToggleIsModified(string label, ref bool value)

View File

@@ -0,0 +1,63 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace UniGLTF.MeshUtility
{
// Instantiate
class PrefabContext : IDisposable
{
public readonly GameObject Instance;
readonly UnityPath _assetFolder;
public bool Keep = false;
public string AssetFolder => _assetFolder.Value;
public PrefabContext(GameObject prefab, UnityPath assetFolder)
{
this._assetFolder = assetFolder;
this.Instance = GameObject.Instantiate(prefab);
if (PrefabUtility.IsOutermostPrefabInstanceRoot(this.Instance))
{
// どういう条件でここに来るかはよくわからない
PrefabUtility.UnpackPrefabInstance(this.Instance, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
}
// - Instance を Asset に書き出す
// - Instance を削除する
public void Dispose()
{
if (Keep)
{
// for debug
return;
}
UnityEngine.Object.DestroyImmediate(Instance);
}
public static UnityPath GetOutFolder(GameObject _exportTarget)
{
// 出力フォルダを決める
var folder = "Assets";
var prefab = _exportTarget.GetPrefab();
if (prefab != null)
{
folder = AssetDatabase.GetAssetPath(prefab);
// Debug.Log(folder);
}
// 新規で作成されるアセットはすべてこのフォルダの中に作る。上書きチェックはしない
var assetFolder = EditorUtility.SaveFolderPanel("select asset save folder", Path.GetDirectoryName(folder), "Integrated");
var unityPath = UniGLTF.UnityPath.FromFullpath(assetFolder);
if (!unityPath.IsUnderWritableFolder)
{
throw new Exception("not in asset folder");
}
return unityPath;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 948458ced6bcc904781eed04ebe7cd01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System;
using UnityEditor;
using UnityEngine;
namespace UniGLTF.MeshUtility
{
// Instantiate
class UndoContext : IDisposable
{
public UndoContext(string undoName, GameObject go)
{
Undo.RegisterFullObjectHierarchyUndo(go, undoName);
if (go.GetPrefabType() == UnityExtensions.PrefabType.PrefabInstance)
{
PrefabUtility.UnpackPrefabInstance(go, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
}
public void Dispose()
{
// 特に何もしない
// Undo すると元に戻ってしまう
// TODO: あれば一時オブジェクトの破棄
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5019966789c0e094a8f58fc2734c9a35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -45,17 +45,11 @@ namespace UniGLTF.MeshUtility
/// <summary>
/// 回転とスケールを除去したヒエラルキーのコピーを作成する(MeshをBakeする)
/// 各レンダラー(SkinnedMeshRenderer と MeshRenderer)にアタッチされた sharedMesh に対して
/// 回転とスケールを除去し、BlendShape の現状を焼き付けた版を作成する(まだ、アタッチしない)
/// </summary>
/// <param name="go">対象のヒエラルキーのルート</param>
/// <param name="bakeCurrentBlendShape">BlendShapeを0クリアするか否か。false の場合 BlendShape の現状を Bake する</param>
/// <param name="createAvatar">Avatarを作る関数</param>
/// <returns></returns>
public static Dictionary<Transform, MeshAttachInfo> NormalizeHierarchyFreezeMesh(GameObject go)
{
//
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
//
var result = new Dictionary<Transform, MeshAttachInfo>();
foreach (var src in go.transform.Traverse())
{

View File

@@ -174,33 +174,40 @@ namespace UniGLTF.MeshUtility
}
}
public virtual (List<MeshIntegrationResult>, List<GameObject>) Process(GameObject go, GameObject instance)
public virtual (List<MeshIntegrationResult>, List<GameObject>) Process(GameObject _go, GameObject _instance)
{
var target = _instance ?? _go;
if (FreezeBlendShape || FreezeRotation || FreezeScaling)
{
// MeshをBakeする
var newMesh = BoneNormalizer.NormalizeHierarchyFreezeMesh(go);
var newMesh = BoneNormalizer.NormalizeHierarchyFreezeMesh(target);
// - ヒエラルキーから回転・拡縮を除去する
// - BakeされたMeshで置き換える
// - bindPoses を再計算する
BoneNormalizer.Replace(go, newMesh, FreezeRotation, FreezeScaling);
BoneNormalizer.Replace(target, newMesh, FreezeRotation, FreezeScaling);
}
var copy = CopyInstantiate(go, instance);
// prefab が instantiate されていた場合に
// Mesh統合設定を instantiate に置き換える
var groupCopy = CopyInstantiate(_go, _instance);
var newList = new List<GameObject>();
var empty = GetOrCreateEmpty(instance ?? go, "mesh");
var empty = GetOrCreateEmpty(target, "mesh");
var results = new List<MeshIntegrationResult>();
foreach (var group in copy)
foreach (var group in groupCopy)
{
var (result, newGo) = Integrate(empty, group);
results.Add(result);
newList.AddRange(newGo);
if (TryIntegrate(empty, group, out var resultAndAdded))
{
var (result, newGo) = resultAndAdded;
results.Add(result);
newList.AddRange(newGo);
}
}
// // 用が済んだ 統合前 の renderer を削除する
foreach (var result in results)
{
foreach (var r in result.SourceMeshRenderers)
@@ -212,22 +219,37 @@ namespace UniGLTF.MeshUtility
RemoveComponent(r);
}
}
// foreach (var result in results)
// {
// foreach (var renderer in result.SourceMeshRenderers)
// {
// GameObject.DestroyImmediate(renderer);
// }
// foreach (var renderer in result.SourceSkinnedMeshRenderers)
// {
// GameObject.DestroyImmediate(renderer);
// }
// }
MeshIntegrationGroups.Clear();
return (results, newList);
}
protected virtual (MeshIntegrationResult, GameObject[]) Integrate(GameObject empty,
MeshIntegrationGroup group)
protected virtual bool TryIntegrate(GameObject empty,
MeshIntegrationGroup group, out (MeshIntegrationResult, GameObject[]) resultAndAdded)
{
var result = MeshIntegrator.Integrate(group, SplitByBlendShape
if (MeshIntegrator.TryIntegrate(group, SplitByBlendShape
? MeshIntegrator.BlendShapeOperation.Split
: MeshIntegrator.BlendShapeOperation.Use);
: MeshIntegrator.BlendShapeOperation.Use, out var result))
{
var newGo = result.AddIntegratedRendererTo(empty).ToArray();
resultAndAdded = (result, newGo);
return true;
}
var newGo = result.AddIntegratedRendererTo(empty).ToArray();
return (result, newGo);
resultAndAdded = default;
return false;
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using UnityEngine;
@@ -11,23 +12,46 @@ namespace UniGLTF.MeshUtility
public Transform RootBone;
public void ReplaceMesh(GameObject dst)
{
if (dst == null)
{
throw new ArgumentNullException();
}
if (Bones != null)
{
// recalc bindposes
Mesh.bindposes = Bones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();
var dstRenderer = dst.GetComponent<SkinnedMeshRenderer>();
dstRenderer.sharedMesh = Mesh;
dstRenderer.sharedMaterials = Materials;
dstRenderer.bones = Bones;
dstRenderer.rootBone = RootBone;
if (dst.GetComponent<SkinnedMeshRenderer>() is SkinnedMeshRenderer dstRenderer)
{
dstRenderer.sharedMesh = Mesh;
dstRenderer.sharedMaterials = Materials;
dstRenderer.bones = Bones;
dstRenderer.rootBone = RootBone;
}
else
{
Debug.LogError($"SkinnedMeshRenderer not found", dst);
}
}
else
{
var dstFilter = dst.GetComponent<MeshFilter>();
dstFilter.sharedMesh = Mesh;
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
dstRenderer.sharedMaterials = Materials;
if (dst.GetComponent<MeshFilter>() is MeshFilter dstFilter)
{
dstFilter.sharedMesh = Mesh;
if (dst.gameObject.GetComponent<MeshRenderer>() is MeshRenderer dstRenderer)
{
dstRenderer.sharedMaterials = Materials;
}
else
{
Debug.LogError($"MeshRenderer not found", dst);
}
}
else
{
Debug.LogError($"MeshFilter not found", dst);
}
}
}
}

View File

@@ -48,16 +48,24 @@ namespace UniGLTF.MeshUtility
public IEnumerable<GameObject> AddIntegratedRendererTo(GameObject parent)
{
int count = 0;
if (Integrated != null)
{
Integrated.AddIntegratedRendererTo(parent, Bones);
++count;
yield return Integrated.IntegratedRenderer.gameObject;
}
if (IntegratedNoBlendShape != null)
{
IntegratedNoBlendShape.AddIntegratedRendererTo(parent, Bones);
++count;
yield return IntegratedNoBlendShape.IntegratedRenderer.gameObject;
}
if (count == 0)
{
throw new NotImplementedException();
}
}
}
}

View File

@@ -275,7 +275,8 @@ namespace UniGLTF.MeshUtility
return found;
}
public static MeshIntegrationResult Integrate(MeshIntegrationGroup group, BlendShapeOperation op)
public static bool TryIntegrate(MeshIntegrationGroup group, BlendShapeOperation op,
out MeshIntegrationResult result)
{
var integrator = new MeshUtility.MeshIntegrator();
foreach (var x in group.Renderers)
@@ -289,7 +290,15 @@ namespace UniGLTF.MeshUtility
integrator.Push(mr);
}
}
return integrator.Integrate(group.Name, op);
result = integrator.Integrate(group.Name, op);
if (result.Integrated != null || result.IntegratedNoBlendShape != null)
{
return true;
}
else
{
return false;
}
}
delegate bool TriangleFilter(int i0, int i1, int i2);
@@ -351,7 +360,7 @@ namespace UniGLTF.MeshUtility
return mesh;
}
public MeshIntegrationResult Integrate(string name, BlendShapeOperation op)
MeshIntegrationResult Integrate(string name, BlendShapeOperation op)
{
if (_Bones.Count != _BindPoses.Count)
{

View File

@@ -3,6 +3,8 @@ using UnityEditor;
using UnityEngine;
using UniGLTF.M17N;
using UniGLTF;
using System.Collections.Generic;
using UniGLTF.MeshUtility;
namespace VRM
@@ -48,6 +50,32 @@ namespace VRM
return firstPerson || mod;
}
protected override void WriteAssets(GameObject copy, string assetFolder, List<MeshIntegrationResult> results)
{
base.WriteAssets(copy, assetFolder, results);
// 統合した結果を反映した BlendShapeClip を作成して置き換える
// var clips = VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(results, copy, assetFolder);
// reset firstperson
// var firstperson = copy.GetComponent<VRMFirstPerson>();
// if (firstperson != null)
// {
// firstperson.Reset();
// }
// var prefabReference = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
// foreach (var clip in clips)
// {
// var so = new SerializedObject(clip);
// so.Update();
// // clip.Prefab = copy;
// var prop = so.FindProperty("m_prefab");
// prop.objectReferenceValue = prefabReference;
// so.ApplyModifiedProperties();
// }
}
protected override void DialogMessage()
{
EditorGUILayout.HelpBox(Message.MESH_UTILITY.Msg(), MessageType.Info);

View File

@@ -38,13 +38,19 @@ namespace VRM
return copy;
}
protected override
(UniGLTF.MeshUtility.MeshIntegrationResult, GameObject[]) Integrate(
protected override bool
TryIntegrate(
GameObject empty,
UniGLTF.MeshUtility.MeshIntegrationGroup group)
UniGLTF.MeshUtility.MeshIntegrationGroup group,
out (UniGLTF.MeshUtility.MeshIntegrationResult, GameObject[]) resultAndAdded)
{
var (result, newList) = base.Integrate(empty, group);
if (!base.TryIntegrate(empty, group, out resultAndAdded))
{
resultAndAdded = default;
return false;
}
var (result, newGo) = resultAndAdded;
if (_generateFirstPerson && group.Name == nameof(FirstPersonFlag.Auto))
{
// Mesh 統合の後処理
@@ -62,8 +68,7 @@ namespace VRM
_ProcessFirstPerson(_vrmInstance.FirstPersonBone, result.IntegratedNoBlendShape.IntegratedRenderer);
}
}
return (result, newList);
return true;
}
private void _ProcessFirstPerson(Transform firstPersonBone, SkinnedMeshRenderer smr)

View File

@@ -39,11 +39,16 @@ namespace UniVRM10
}
protected override
(UniGLTF.MeshUtility.MeshIntegrationResult, GameObject[]) Integrate(
bool TryIntegrate(
GameObject empty,
UniGLTF.MeshUtility.MeshIntegrationGroup group)
UniGLTF.MeshUtility.MeshIntegrationGroup group,
out (UniGLTF.MeshUtility.MeshIntegrationResult, GameObject[]) resultAndAdded)
{
var (result, newList) = base.Integrate(empty, group);
if (!base.TryIntegrate(empty, group, out resultAndAdded))
{
return false;
}
var (result, newList) = resultAndAdded;
if (_generateFirstPerson && group.Name == nameof(UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto))
{
@@ -62,8 +67,7 @@ namespace UniVRM10
_ProcessFirstPerson(_vrmInstance.Humanoid.Head, result.IntegratedNoBlendShape.IntegratedRenderer);
}
}
return (result, newList);
return true;
}
private void _ProcessFirstPerson(Transform firstPersonBone, SkinnedMeshRenderer smr)