// Magica Cloth. // Copyright (c) MagicaSoft, 2020-2022. // https://magicasoft.jp using System.Collections.Generic; using UnityEngine; namespace MagicaCloth { /// /// ランタイム処理 /// public class MagicaAvatarRuntime : MagicaAvatarAccess { /// /// このアバターが保持するボーン辞書 /// private Dictionary boneDict = new Dictionary(); /// /// このアバターが保持するボーンの参照数 /// private Dictionary boneReferenceDict = new Dictionary(); /// /// アバターパーツリスト /// private List avatarPartsList = new List(); /// /// このアバターが保持するコライダーリスト /// /// /// private List colliderList = new List(); //========================================================================================= /// /// 初期設定 /// public override void Create() { CreateBoneDict(); CreateColliderList(); } /// /// 破棄 /// public override void Dispose() { } /// /// 有効化 /// public override void Active() { } /// /// 無効化 /// public override void Inactive() { } //========================================================================================= public int AvatarPartsCount { get { return avatarPartsList.Count; } } public MagicaAvatarParts GetAvatarParts(int index) { return avatarPartsList[index]; } //========================================================================================= /// /// すべてのボーンを辞書に登録する /// この時にボーン名に重複があると着せ替えのときに問題を起こす可能性がある /// private void CreateBoneDict() { var tlist = owner.GetComponentsInChildren(); foreach (var t in tlist) { if (boneDict.ContainsKey(t.name)) { // Duplication name! Debug.LogWarning(string.Format("{0} [{1}]", Define.GetErrorMessage(Define.Error.OverlappingTransform), t.name)); } else { boneDict.Add(t.name, t); boneReferenceDict.Add(t, 1); // 参照数1で初期化 } } } /// /// アバターが保持するコライダーをリスト化する /// private void CreateColliderList() { var clist = owner.GetComponentsInChildren(); if (clist != null && clist.Length > 0) { colliderList.AddRange(clist); } } /// /// 現在アバターが保有するコライダー数を取得する /// /// public int GetColliderCount() { if (Application.isPlaying) { return colliderList.Count; } else { return owner.GetComponentsInChildren().Length; } } //========================================================================================= /// /// ゲームオブジェクト名が重複するトランスフォームのリストを返す /// /// public List CheckOverlappingTransform() { var boneHash = new HashSet(); var overlapList = new List(); var tlist = owner.GetComponentsInChildren(); var root = owner.transform; foreach (var t in tlist) { if (t == root) continue; if (boneHash.Contains(t.name)) { overlapList.Add(t); } else { boneHash.Add(t.name); } } return overlapList; } //========================================================================================= /// /// アバターパーツの追加 /// /// public int AddAvatarParts(MagicaAvatarParts parts) { if (parts == null) return 0; //Debug.Log("AddAvatarParts:" + parts.name); // すでに着せ替え済みならば何もしない if (parts.HasParent) return parts.PartsId; // アクティブ化する if (parts.gameObject.activeSelf == false) parts.gameObject.SetActive(true); // 初期化(すでに初期化済みならば何もしない) owner.Init(); // スキンメッシュレンダラーリスト var skinRendererList = parts.GetComponentsInChildren(); //Debug.Log("skinRendererList:" + skinRendererList.Length); // Magicaコンポーネントリスト //var magicaComponentList = parts.GetComponentsInChildren(); var magicaComponentList = parts.GetMagicaComponentList(); //Debug.Log("magicaComponentList:" + magicaComponentList.Length); // パーツを子として追加する var root = owner.transform; var croot = parts.transform; parts.transform.SetParent(root, false); parts.transform.localPosition = Vector3.zero; parts.transform.localRotation = Quaternion.identity; parts.ParentAvatar = owner; avatarPartsList.Add(parts); // 必要なボーンを移植する var partsBoneDict = parts.GetBoneDict(); foreach (var bone in partsBoneDict.Values) { if (bone != croot) AddBone(root, croot, bone); } // すべてのボーン参照数を加算する foreach (var bone in partsBoneDict.Values) { if (bone != croot) { var t = boneDict[bone.name]; boneReferenceDict[t]++; //Debug.Log("reference[" + t.name + "]:" + boneReferenceDict[t]); } } // ボーンの交換情報作成 var boneReplaceDict = new Dictionary(); foreach (var bone in partsBoneDict.Values) { if (bone != croot) { boneReplaceDict.Add(bone, boneDict[bone.name]); } else { boneReplaceDict.Add(bone, root); } } #if false foreach (var kv in avatar.Runtime.boneReplaceDict) { if (kv.Key != kv.Value) { Debug.Log("置換[" + kv.Key.name + "]->[" + kv.Value.name + "]"); } } #endif // スキンメッシュレンダラー置換 foreach (var skinRenderer in skinRendererList) { ReplaceSkinMeshRenderer(skinRenderer, boneReplaceDict); } // Magicaコンポーネント置換 foreach (var comp in magicaComponentList) { ReplaceMagicaComponent(comp, boneReplaceDict); } // Magicaコンポーネントに本体のコライダーを追加する if (colliderList.Count > 0) { foreach (var comp in magicaComponentList) { var cloth = comp as BaseCloth; if (cloth && cloth.TeamData.MergeAvatarCollider) { // 初期化 cloth.Init(); foreach (var col in colliderList) { cloth.AddCollider(col); } } } } // パーツの機能は停止させる parts.gameObject.SetActive(false); // イベント owner.OnAttachParts.Invoke(owner, parts); return parts.PartsId; } /// /// 指定ボーンを親に追加する /// /// /// /// private void AddBone(Transform root, Transform croot, Transform bone) { if (boneDict.ContainsKey(bone.name)) { // すでに登録済み return; } // ボーンを追加する親ボーンを検索する Transform attachBone = root; Transform before = bone; Transform t = bone.parent; while (t && t != croot) { if (boneDict.ContainsKey(t.name)) { attachBone = boneDict[t.name]; break; } before = t; t = t.parent; } // ボーン追加 before.SetParent(attachBone, false); //Debug.Log("Add attach:" + attachBone.name + " before:" + before.name); // before以下を辞書に登録する var blist = before.GetComponentsInChildren(); foreach (var b in blist) { if (boneDict.ContainsKey(b.name)) { // Duplication name! Debug.LogWarning(string.Format("{0} [{1}]", Define.GetErrorMessage(Define.Error.AddOverlappingTransform), b.name)); } else { boneDict.Add(b.name, b); boneReferenceDict.Add(b, 0); // まず参照数0で初期化 } } } /// /// スキンメッシュレンダラーのボーン置換 /// /// private void ReplaceSkinMeshRenderer(SkinnedMeshRenderer skinRenderer, Dictionary boneReplaceDict) { // ルートボーン置換 skinRenderer.rootBone = MeshUtility.GetReplaceBone(skinRenderer.rootBone, boneReplaceDict); // ボーン置換 var bones = skinRenderer.bones; for (int i = 0; i < bones.Length; i++) { bones[i] = MeshUtility.GetReplaceBone(bones[i], boneReplaceDict); } skinRenderer.bones = bones; } /// /// Magicaコンポーネントの置換 /// /// private void ReplaceMagicaComponent(CoreComponent comp, Dictionary boneReplaceDict) { comp.ChangeAvatar(boneReplaceDict); } /// /// アバターパーツの削除 /// /// public void RemoveAvatarParts(MagicaAvatarParts parts) { //Debug.Log("RemoveAvatarParts:" + parts.name); if (parts == null) return; if (avatarPartsList.Contains(parts) == false) return; // 接続を切る parts.ParentAvatar = null; avatarPartsList.Remove(parts); // 参照数を1つ減らし削除するボーンをリスト化する var removeBoneList = new List(); var croot = parts.transform; foreach (var bone in parts.GetBoneDict().Values) { if (bone == null) continue; if (bone != croot) { var t = boneDict[bone.name]; boneReferenceDict[t]--; if (boneReferenceDict[t] == 0) { boneReferenceDict.Remove(t); boneDict.Remove(t.name); removeBoneList.Add(t); } //Debug.Log("reference[" + t.name + "]:" + boneReferenceDict[t]); } } // ボーン削除 foreach (var bone in removeBoneList) { if (bone) { GameObject.Destroy(bone.gameObject); } } #if false foreach (var bone in boneDict.Values) { if (bone) Debug.Log("残 bone:" + bone.name); } foreach (var kv in boneReferenceDict) { if (kv.Key) Debug.Log("残 reference[" + kv.Key.name + "]:" + kv.Value); } #endif // 本体コライダーを削除する if (colliderList.Count > 0) { // Magicaコンポーネントリスト var magicaComponentList = parts.GetMagicaComponentList(); foreach (var comp in magicaComponentList) { var cloth = comp as BaseCloth; if (cloth) { foreach (var col in colliderList) { cloth.RemoveCollider(col); } } } } // パーツ削除 GameObject.Destroy(parts.gameObject); // イベント owner.OnDetachParts.Invoke(owner); } /// /// アバターパーツの削除(パーツID) /// /// public void RemoveAvatarParts(int partsId) { var parts = avatarPartsList.Find((p) => p.PartsId == partsId); RemoveAvatarParts(parts); } } }