DanMachi:

-compatibility improvements
Core:
-simplified object and keyframe serialization
-complicated assetbundle loading
This commit is contained in:
2024-05-10 09:56:39 +02:00
parent c922584d38
commit c05f989384
192 changed files with 79685 additions and 25820 deletions

View File

@@ -1,64 +1,77 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
public class DanMachiCharacterContainer : ObjectContainer, IAnimated
{
protected override System.Type _serializeType => typeof(DanMachiCharacterSerializable);
protected override System.Type _keyframeType => typeof(DanMachiCharacterKeyframeData);
public CharacterAsset Data;
public string AnimationSet;
public string Animation;
public GameObject Body;
public MorphPanel MorphPanel;
public CharacterPanel CharacterPanel;
public List<AnimationClip> Animations;
private Animator _animator;
private AnimatorOverrideController _animatorController;
private Dictionary<HumanBodyBones, Transform> _humanBones = new Dictionary<HumanBodyBones, Transform>();
public List<Transform> Bones = new List<Transform>();
public override void Select()
public void Init()
{
if(MorphPanel != null)
_animator = GetComponentInChildren<Animator>();
if(_animator != null)
{
MorphPanel.gameObject.SetActive(true);
_animator.applyRootMotion = false;
_animator.runtimeAnimatorController = _animatorController = Instantiate(SharedResources.Instance.GenericAnimatorController);
}
else
{
Error.Log(Color.red, $"{name} has no animator");
}
CreateMorphPanel();
CreateCharacterPanel();
SetBones();
}
public override void Deselect()
public void Rebuild(CharacterAsset model)
{
if (MorphPanel != null)
{
MorphPanel.gameObject.SetActive(false);
}
DestroyImmediate(Body);
Build(model);
}
public void Build(CharacterAsset model)
{
Data = model;
var characterAsset = AssetBundle.LoadFromFile(model.FilePath);
var animationSets = DanMachiAssetLibrary.Instance.Animations.Where(a => a.Category == model.Category && a.Id == model.Id);
var assetPath = Path.Combine(DanMachiAssetLibrary.Instance.LocalFilesPath, model.FilePath);
var characterAsset = AssetLibrary.LoadFromFile(assetPath);
var assetNames = characterAsset.GetAllAssetNames();
var characterModel = assetNames.FirstOrDefault(a => Path.GetFileName(a).StartsWith("CP_") && a.EndsWith(".prefab"));
foreach (var go in characterAsset.LoadAllAssets<GameObject>())
if(!string.IsNullOrEmpty(characterModel))
{
Instantiate(go, transform);
Body = Instantiate(characterAsset.LoadAsset(characterModel), transform) as GameObject;
}
characterAsset.Unload(false);
else
{
Body = new GameObject();
Body.transform.SetParent(transform);
foreach (var go in characterAsset.LoadAllAssets<GameObject>())
{
Instantiate(go, Body.transform);
}
}
LoadedAssets = new List<AssetBundleEntry>() { new AssetBundleEntry(model.AssetName, assetPath, characterAsset) };
AssetLibrary.UnloadAsset(assetPath);
SetDefaultMaterials(transform);
Init();
var defaultAnimSet = animationSets.FirstOrDefault(a => a.Costume == "common");
LoadAnimationSet(defaultAnimSet);
}
public void Init()
{
_animator = GetComponentInChildren<Animator>();
_animator.applyRootMotion = false;
_animator.runtimeAnimatorController = _animatorController = Instantiate(SharedResources.Instance.GenericAnimatorController);
CreateMorphPanel();
CreateCharacterPanel();
SetBones();
}
public void LoadAnimationSet(CharacterAsset animationSet)
@@ -67,7 +80,7 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
{
AnimationSet = animationSet.Costume;
Animation = "";
var animationAsset = AssetBundle.LoadFromFile(animationSet.FilePath);
var animationAsset = AssetBundle.LoadFromFile(Path.Combine(DanMachiAssetLibrary.Instance.LocalFilesPath, animationSet.FilePath));
Animations = animationAsset.LoadAllAssets<AnimationClip>().ToList();
animationAsset.Unload(false);
}
@@ -81,6 +94,12 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
public void PlayAnimation(AnimationClip anim)
{
if(_animator == null)
{
Error.Log(Color.red, $"{name} has no animator");
return;
}
if(anim == null)
{
Animation = "";
@@ -110,8 +129,20 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
public void SetBones()
{
_humanBones.Clear();
Bones.Clear();
if (_animator == null) return;
var humanBones = new Dictionary<HumanBodyBones, Transform>();
var allBones = _animator.transform.GetComponentsInChildren<Transform>().ToDictionary(b=>b.name, b=>b);
var allBones = new Dictionary<string, Transform>();
foreach(var bone in _animator.transform.GetComponentsInChildren<Transform>())
{
if (!allBones.ContainsKey(bone.name))
{
allBones[bone.name] = bone;
}
}
humanBones.Add(HumanBodyBones.Hips, TryGet(allBones,"Hips"));
humanBones.Add(HumanBodyBones.Spine, TryGet(allBones,"Spine"));
humanBones.Add(HumanBodyBones.RightUpperLeg, TryGet(allBones,"RightUpLeg"));
@@ -186,6 +217,7 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
}
_humanBones = humanBones;
Bones = allBones.Values.ToList();
}
public T TryGet<T>(Dictionary<string, T> dict, string key)
@@ -199,11 +231,6 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
public void CreateMorphPanel()
{
if(MorphPanel != null)
{
Destroy(MorphPanel.gameObject);
}
var morphRenderers = new Dictionary<SkinnedMeshRenderer, List<MorphHelper>>();
foreach (var rend in gameObject.GetComponentsInChildren<SkinnedMeshRenderer>())
{
@@ -213,29 +240,59 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
}
}
if (morphRenderers.Count == 0) return;
if (morphRenderers.Count == 0)
{
if(MorphPanel != null)
{
Destroy(MorphPanel.gameObject);
MorphPanel = null;
InstantiatedObjects.Remove(MorphPanel.gameObject);
}
return;
}
MorphPanel = MorphPanel.Create(morphRenderers.Select(mr=>mr.Key.sharedMesh.name).ToList());
MorphPanel.name = $"Chr_{Data.Id} Morphs";
if(MorphPanel != null)
{
MorphPanel.Init(morphRenderers.Select(mr => mr.Key.sharedMesh.name).ToList());
}
else
{
MorphPanel = MorphPanel.Create(morphRenderers.Select(mr => mr.Key.sharedMesh.name).ToList());
MorphPanel.name = $"Chr_{Data.Id} Morphs";
InstantiatedObjects.Add(MorphPanel.gameObject);
DanMachiInterface.Instance.SelectedObjectToolbar.AttachToThis(MorphPanel.GetComponent<UIElementDragger>());
}
foreach(var rend in morphRenderers)
foreach (var rend in morphRenderers)
{
var mesh = rend.Key.sharedMesh;
for(int i = 0; i < mesh.blendShapeCount; i++)
{
MorphPanel.CreateMorph(mesh.name, this, rend.Key, i, mesh.GetBlendShapeName(i).Replace("blendShape1.",""));
var panel = MorphPanel.CreateMorph(mesh.name, this, rend.Key, i, mesh.GetBlendShapeName(i).Replace("blendShape1.",""));
var helper = panel.MorphHelper;
if (morphRenderers.ContainsKey(rend.Key))
morphRenderers[rend.Key].Add(helper);
else
morphRenderers.Add(rend.Key, new List<MorphHelper>() { helper });
}
}
Morphs = morphRenderers;
DanMachiModelViewerInterface.Instance.SelectedObjectToolbar.AttachToThis(MorphPanel.GetComponent<UIElementDragger>());
}
public void CreateCharacterPanel()
{
CharacterPanel = CharacterPanel.Create(this);
CharacterPanel.name = $"Chr_{Data.Id} Settings";
DanMachiModelViewerInterface.Instance.SelectedObjectToolbar.AttachToThis(CharacterPanel.GetComponent<UIElementDragger>());
if(CharacterPanel != null)
{
CharacterPanel.Init(this);
}
else
{
CharacterPanel = CharacterPanel.Create(this);
CharacterPanel.name = $"Chr_{Data.Id} Settings";
InstantiatedObjects.Add(CharacterPanel.gameObject);
DanMachiInterface.Instance.SelectedObjectToolbar.AttachToThis(CharacterPanel.GetComponent<UIElementDragger>());
}
}
public override Vector3 GetCenter()
@@ -247,19 +304,45 @@ public class DanMachiCharacterContainer : DanMachiObjectContainer, IAnimated
return base.GetCenter();
}
protected override void OnDestroy()
{
if (_applicationQuitting) return;
if(MorphPanel != null)
{
Destroy(MorphPanel.gameObject);
}
Destroy(CharacterPanel.gameObject);
base.OnDestroy();
}
public Animator GetAnimator()
{
return _animator;
}
public override void PastePose(KeyframeData frame, PoseLoadOptions pasteParams)
{
PlayAnimation("");
if (pasteParams.Root)
{
transform.SetTransform(frame.Root);
}
var frameData = frame as DanMachiCharacterKeyframeData;
if (frameData == null) return;
foreach (var bone in frameData.Bones.Bones)
{
var targetBone = Bones.FirstOrDefault(b => b.name == bone.Name);
if (targetBone == null) continue;
bone.Transform.ApplyTo(targetBone.transform);
}
foreach(var morph in frameData.Morphs)
{
var renderer = Morphs.Keys.FirstOrDefault(k => k.name == morph.Key);
if (renderer == null) continue;
foreach(var value in morph.Value)
{
var helper = Morphs[renderer].FirstOrDefault(h => h.Name == value.Name);
if (helper == null) continue;
helper.Value = value.Value;
helper.UpdateMorph(value.Strength);
}
}
SetKeyframe();
}
}

View File

@@ -0,0 +1,29 @@

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
public class DanMachiCharacterKeyframeData : KeyframeData
{
public BoneList Bones = new BoneList();
public Dictionary<string, List<MorphHelper>> Morphs = new Dictionary<string, List<MorphHelper>>();
public DanMachiCharacterKeyframeData() { }
public DanMachiCharacterKeyframeData(DanMachiCharacterContainer container)
{
this.Root = new SerializableTransform(container.transform);
this.Bones = new BoneList() { Name = "", Bones = container.Bones.Select(b => new SerializableBone(b)).ToArray() };
this.Morphs = container.Morphs.ToDictionary(m => m.Key.name, m => m.Value);
}
public override KeyframeData Clone()
{
var keyframe = new DanMachiCharacterKeyframeData();
keyframe.Root = new SerializableTransform(this.Root);
keyframe.Bones = new BoneList() { Name = Bones.Name, Bones = Bones.Bones.Select(b=> new SerializableBone(b)).ToArray() };
keyframe.Morphs = Morphs.ToDictionary(m => m.Key, m => m.Value.Select(v=>new MorphHelper(v.ParentName, v.Name, v.Strength, v.Value)).ToList());
return keyframe;
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6ce5c34b04160214b935e3dcefed9ffc
guid: d096ac3a0685dd54a836bc35192a0522
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,12 @@

public class DanMachiCharacterSerializable : ObjectContainerSerializable
{
public string AssetName;
public DanMachiCharacterSerializable() { }
public DanMachiCharacterSerializable(DanMachiCharacterContainer container) : base(container)
{
AssetName = container.Data.AssetName;
}
}

View File

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

View File

@@ -1,42 +0,0 @@
public class DanMachiObjectContainer : ObjectContainer
{
protected static DanMachiModelViewerMain Main => DanMachiModelViewerMain.Instance;
protected override void Start()
{
if (DoNotSave) return;
if(Frames.Count == 0)
{
SetKeyframe();
}
DanMachiModelViewerMain.RegisterObject(this);
var selector = UITimelineObjectEntry.Create(this);
InstantiatedObjects.Add(selector.gameObject);
ModelViewerMain.GetInstance<DanMachiModelViewerMain>().SelectObject(this);
}
public override void Select()
{
}
public override void Deselect()
{
}
protected override void OnDestroy()
{
if(_applicationQuitting) return;
if (Main.SelectedObject == this)
{
Main.SelectObject(null);
}
ModelViewerMain.UnregisterObject(this);
base.OnDestroy();
}
}