diff --git a/Assets/UniGLTF/Editor/UniGLTF/ScriptedImporter/GltfScriptedImporterBase.cs b/Assets/UniGLTF/Editor/UniGLTF/ScriptedImporter/GltfScriptedImporterBase.cs index d7588cbcf..83dd81993 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ScriptedImporter/GltfScriptedImporterBase.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ScriptedImporter/GltfScriptedImporterBase.cs @@ -61,7 +61,7 @@ namespace UniGLTF context.AddObjectToAsset(k.Name, o); }); var root = loaded.Root; - GameObject.DestroyImmediate(loaded); + DestroyImmediate(loaded); context.AddObjectToAsset(root.name, root); context.SetMainObject(root); diff --git a/Assets/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs b/Assets/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs index 79f7d94c1..6f4374687 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/RuntimeGltfInstance.cs @@ -9,6 +9,10 @@ namespace UniGLTF /// ImporterContext の Load 結果の GltfModel /// /// Runtime でモデルを Destory したときに関連リソース(Texture, Material...などの UnityEngine.Object)を自動的に Destroy する。 + /// + /// TODO: Editor Importの場合でも一瞬だけこのクラスのインスタンスが生じるが、すぐDestroyImmediateされるため、それらを区別する用途に使うことができる + /// Editorでも利用されている以上、名前・責務が良くないので見直したい。 + /// /// public class RuntimeGltfInstance : MonoBehaviour, IResponsibilityForDestroyObjects { diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index 791397b22..e178df0c5 100644 --- a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -8,7 +9,7 @@ namespace UniVRM10 /// /// ブレンドシェイプを蓄えてまとめて適用するクラス /// - internal sealed class ExpressionMerger + internal sealed class ExpressionMerger : IDisposable { /// /// Key から Expression を得る @@ -24,7 +25,7 @@ namespace UniVRM10 MaterialValueBindingMerger m_materialValueBindingMerger; - public ExpressionMerger(VRM10ObjectExpression expressions, Transform root) + public ExpressionMerger(VRM10ObjectExpression expressions, Transform root, bool isPrefabInstance) { m_clipMap = expressions.Clips.ToDictionary( x => expressions.CreateKey(x.Clip), @@ -33,7 +34,7 @@ namespace UniVRM10 ); m_valueMap = new Dictionary(ExpressionKey.Comparer); m_morphTargetBindingMerger = new MorphTargetBindingMerger(m_clipMap, root); - m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root); + m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root, isPrefabInstance); } /// @@ -74,5 +75,10 @@ namespace UniVRM10 { m_materialValueBindingMerger.RestoreMaterialInitialValues(); } + + public void Dispose() + { + m_materialValueBindingMerger.Dispose(); + } } } diff --git a/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs index 495ccfd90..665b2b611 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using UniGLTF.Extensions.VRMC_vrm; using UnityEngine; using VRM10.MToon10; +using Object = UnityEngine.Object; namespace UniVRM10 { /// /// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ... /// - internal sealed class MaterialValueBindingMerger + internal sealed class MaterialValueBindingMerger : IDisposable { private static readonly string COLOR_PROPERTY = MToon10Prop.BaseColorFactor.ToUnityShaderLabName(); private static readonly string EMISSION_COLOR_PROPERTY = MToon10Prop.EmissiveFactor.ToUnityShaderLabName(); @@ -18,6 +19,8 @@ namespace UniVRM10 private static readonly string SHADE_COLOR_PROPERTY = MToon10Prop.ShadeColorFactor.ToUnityShaderLabName(); private static readonly string MATCAP_COLOR_PROPERTY = MToon10Prop.MatcapColorFactor.ToUnityShaderLabName(); + private readonly HashSet _clonedMaterials = new(); + public static string GetProperty(MaterialColorType bindType) { switch (bindType) @@ -50,16 +53,44 @@ namespace UniVRM10 /// Dictionary m_materialMap = new Dictionary(); - void InitializeMaterialMap(Dictionary clipMap, Transform root) + void InitializeMaterialMap(Dictionary clipMap, Transform root, bool isPrefabInstance) { - Dictionary materialNameMap = new Dictionary(); + var materialNameMap = new Dictionary(); foreach (var renderer in root.GetComponentsInChildren()) { - foreach (var material in renderer.sharedMaterials) + // VFXRendererなど、Materialが設定できないRendererが存在する + if (renderer is not SkinnedMeshRenderer && renderer is not MeshRenderer) continue; + + if (isPrefabInstance) { - if (material != null && !materialNameMap.ContainsKey(material.name)) + // EditorImportされたPrefabのInstanceとして生成されている場合、Materialを複製する + var sharedMaterials = renderer.sharedMaterials; + var materials = renderer.materials; + for (var i = 0; i < materials.Length; i++) { - materialNameMap.Add(material.name, material); + var sharedMaterial = sharedMaterials[i]; + var material = materials[i]; + + if (!sharedMaterial || !material) continue; + + // 複製されたマテリアルはこのクラス内で破棄 + if (sharedMaterial != material) _clonedMaterials.Add(material); + + // 複製前の名前に揃え、それを記録しておく + // なお、Vrm10Runtimeのインスタンスが作られるより先にユーザーによってMaterialが複製されるパターンは想定しない + material.name = sharedMaterial.name; + materialNameMap.TryAdd(sharedMaterial.name, material); + } + } + else + { + // PrefabのInstanceでない(RuntimeImportされている)ならMaterialは複製しない + foreach (var material in renderer.sharedMaterials) + { + if (material) + { + materialNameMap.TryAdd(material.name, material); + } } } } @@ -284,9 +315,18 @@ namespace UniVRM10 } #endregion - public MaterialValueBindingMerger(Dictionary clipMap, Transform root) + public MaterialValueBindingMerger(Dictionary clipMap, Transform root, bool isPrefabInstance) { - InitializeMaterialMap(clipMap, root); + InitializeMaterialMap(clipMap, root, isPrefabInstance); + } + + public void Dispose() + { + foreach (var clonedMaterial in _clonedMaterials) + { + Object.Destroy(clonedMaterial); + } + _clonedMaterials.Clear(); } } } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs index f27be9aea..5b4d46531 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs @@ -141,8 +141,11 @@ namespace UniVRM10 } var initPose = RuntimeGltfInstance.SafeGetInitialPose(transform); + + // NOTE: RuntimeGltfInstanceがないかどうかでPrefabのインスタンスであるか(EditorImportされているか)が判別できる + var isPrefabInstance = !GetComponent(); - return new Vrm10Runtime(this, useControlRig, m_springBoneRuntime, initPose); + return new Vrm10Runtime(this, useControlRig, m_springBoneRuntime, initPose, isPrefabInstance); } public Vrm10Runtime Runtime diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index c6e8661e2..5a800d2e1 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -52,7 +52,7 @@ namespace UniVRM10 IReadOnlyDictionary _initPose; public Vrm10Runtime(Vrm10Instance instance, bool useControlRig, IVrm10SpringBoneRuntime springBoneRuntime, - IReadOnlyDictionary initPose) + IReadOnlyDictionary initPose, bool isPrefabInstance) { if (!Application.isPlaying) { @@ -77,7 +77,7 @@ namespace UniVRM10 } Constraints = instance.GetComponentsInChildren(); LookAt = new Vrm10RuntimeLookAt(instance, instance.Humanoid, ControlRig); - Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable); + Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable, isPrefabInstance); SpringBone = springBoneRuntime; } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs index 1a4f747ce..902654f35 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeExpression.cs @@ -23,9 +23,9 @@ namespace UniVRM10 public float LookAtOverrideRate { get; private set; } public float MouthOverrideRate { get; private set; } - internal Vrm10RuntimeExpression(Vrm10Instance target, ILookAtEyeDirectionApplicable eyeDirectionApplicable) + internal Vrm10RuntimeExpression(Vrm10Instance target, ILookAtEyeDirectionApplicable eyeDirectionApplicable, bool isPrefabInstance) { - _merger = new ExpressionMerger(target.Vrm.Expression, target.transform); + _merger = new ExpressionMerger(target.Vrm.Expression, target.transform, isPrefabInstance); _keys = target.Vrm.Expression.Clips .Select(x => target.Vrm.Expression.CreateKey(x.Clip)) .ToList(); @@ -56,6 +56,9 @@ namespace UniVRM10 _eyeDirectionApplicable?.Restore(); _eyeDirectionApplicable = null; + + _merger?.Dispose(); + _merger = null; } internal void Process(LookAtEyeDirection inputEyeDirection)