mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 18:55:06 +08:00
Retain components (e.g. PhysBones) at their original hierarchy path
This commit is contained in:
parent
f7c6a81a4e
commit
625878e698
@ -1,7 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.unity.collab-proxy": "1.10.2",
|
||||
"com.unity.ide.rider": "1.2.1",
|
||||
"com.unity.ide.rider": "3.0.15",
|
||||
"com.unity.ide.visualstudio": "2.0.11",
|
||||
"com.unity.ide.vscode": "1.2.4",
|
||||
"com.unity.test-framework": "1.1.29",
|
||||
@ -10,6 +10,7 @@
|
||||
"com.unity.ugui": "1.0.0",
|
||||
"com.unity.xr.oculus.standalone": "2.38.4",
|
||||
"com.unity.xr.openvr.standalone": "2.0.5",
|
||||
"net.fushizen.modular-avatar.core": "0.0.1",
|
||||
"com.unity.modules.ai": "1.0.0",
|
||||
"com.unity.modules.androidjni": "1.0.0",
|
||||
"com.unity.modules.animation": "1.0.0",
|
||||
@ -40,7 +41,6 @@
|
||||
"com.unity.modules.video": "1.0.0",
|
||||
"com.unity.modules.vr": "1.0.0",
|
||||
"com.unity.modules.wind": "1.0.0",
|
||||
"com.unity.modules.xr": "1.0.0",
|
||||
"net.fushizen.modular-avatar.core": "0.0.1"
|
||||
"com.unity.modules.xr": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +26,19 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
-999999,
|
||||
(Action<VRCAvatarDescriptor>)(av => VRCBuildPipelineCallbacks.OnPreprocessAvatar(av.gameObject))
|
||||
});
|
||||
//EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnPlayModeStateChanged(PlayModeStateChange obj)
|
||||
{
|
||||
if (obj == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
Util.DeleteTemporaryAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +1,171 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using Codice.CM.Common.Merge;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
using VRC.Dynamics;
|
||||
using VRC.SDK3.Dynamics.Contact.Components;
|
||||
using VRC.SDK3.Dynamics.PhysBone.Components;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
using Matrix4x4 = UnityEngine.Matrix4x4;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
public class MergeArmatureHook : IVRCSDKPreprocessAvatarCallback
|
||||
{
|
||||
public int callbackOrder => HookSequence.SEQ_MERGE_ARMATURE;
|
||||
|
||||
|
||||
private Dictionary<Transform, Transform> BoneRemappings = new Dictionary<Transform, Transform>();
|
||||
private List<GameObject> ToDelete = new List<GameObject>();
|
||||
|
||||
public bool OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
var mergeArmatures = avatarGameObject.transform.GetComponentsInChildren<ModularAvatarMergeArmature>(true);
|
||||
|
||||
BoneRemappings.Clear();
|
||||
ToDelete.Clear();
|
||||
|
||||
foreach (var mergeArmature in mergeArmatures)
|
||||
{
|
||||
MergeArmature(mergeArmature);
|
||||
UnityEngine.Object.DestroyImmediate(mergeArmature);
|
||||
}
|
||||
|
||||
foreach (var renderer in avatarGameObject.transform.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
var bones = renderer.bones;
|
||||
for (int i = 0; i < bones.Length; i++) bones[i] = MapBoneReference(bones[i]);
|
||||
renderer.bones = bones;
|
||||
renderer.rootBone = MapBoneReference(renderer.rootBone);
|
||||
renderer.probeAnchor = MapBoneReference(renderer.probeAnchor);
|
||||
}
|
||||
|
||||
foreach (var c in avatarGameObject.transform.GetComponentsInChildren<VRCPhysBone>())
|
||||
{
|
||||
if (c.rootTransform == null) c.rootTransform = c.transform;
|
||||
UpdateBoneReferences(c);
|
||||
}
|
||||
|
||||
foreach (var c in avatarGameObject.transform.GetComponentsInChildren<VRCPhysBoneCollider>())
|
||||
{
|
||||
if (c.rootTransform == null) c.rootTransform = c.transform;
|
||||
UpdateBoneReferences(c);
|
||||
}
|
||||
|
||||
foreach (var c in avatarGameObject.transform.GetComponentsInChildren<ContactBase>())
|
||||
{
|
||||
if (c.rootTransform == null) c.rootTransform = c.transform;
|
||||
UpdateBoneReferences(c);
|
||||
}
|
||||
|
||||
foreach (var bone in ToDelete) UnityEngine.Object.DestroyImmediate(bone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateBoneReferences(Component c)
|
||||
{
|
||||
SerializedObject so = new SerializedObject(c);
|
||||
SerializedProperty iter = so.GetIterator();
|
||||
|
||||
bool enterChildren = true;
|
||||
while (iter.Next(enterChildren))
|
||||
{
|
||||
enterChildren = true;
|
||||
switch (iter.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.String: enterChildren = false;
|
||||
break;
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
if (iter.objectReferenceValue is Transform t)
|
||||
{
|
||||
iter.objectReferenceValue = MapBoneReference(t);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
private Transform MapBoneReference(Transform bone)
|
||||
{
|
||||
if (bone != null && BoneRemappings.TryGetValue(bone, out var newBone))
|
||||
{
|
||||
BoneDatabase.MarkNonRetargetable(newBone);
|
||||
bone = newBone;
|
||||
}
|
||||
return bone;
|
||||
}
|
||||
|
||||
private bool HasAdditionalComponents(GameObject go, out bool needsConstraint)
|
||||
{
|
||||
bool hasComponents = false;
|
||||
needsConstraint = false;
|
||||
|
||||
foreach (Component c in go.GetComponents<Component>())
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case Transform _: break;
|
||||
case ModularAvatarMergeArmature _: break;
|
||||
case VRCPhysBone _: case VRCPhysBoneCollider _: hasComponents = true;
|
||||
break;
|
||||
default:
|
||||
hasComponents = true;
|
||||
needsConstraint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasComponents;
|
||||
}
|
||||
|
||||
private void MergeArmature(ModularAvatarMergeArmature mergeArmature)
|
||||
{
|
||||
// TODO: error reporting framework?
|
||||
if (mergeArmature.mergeTarget == null) return;
|
||||
|
||||
RecursiveMerge(mergeArmature, mergeArmature.gameObject, mergeArmature.mergeTarget.gameObject);
|
||||
RecursiveMerge(mergeArmature, mergeArmature.gameObject, mergeArmature.mergeTarget.gameObject, true);
|
||||
}
|
||||
|
||||
private void RecursiveMerge(ModularAvatarMergeArmature config, GameObject src, GameObject target)
|
||||
/**
|
||||
* (Attempts to) merge the source gameobject into the target gameobject. Returns true if the merged source
|
||||
* object must be retained.
|
||||
*/
|
||||
private bool RecursiveMerge(ModularAvatarMergeArmature config, GameObject src, GameObject newParent, bool zipMerge)
|
||||
{
|
||||
BoneDatabase.AddMergedBone(src.transform);
|
||||
src.transform.SetParent(target.transform, true);
|
||||
GameObject mergedSrcBone = new GameObject(src.name + "@" + GUID.Generate());
|
||||
mergedSrcBone.transform.SetParent(src.transform.parent);
|
||||
mergedSrcBone.transform.localPosition = src.transform.localPosition;
|
||||
mergedSrcBone.transform.localRotation = src.transform.localRotation;
|
||||
mergedSrcBone.transform.localScale = src.transform.localScale;
|
||||
|
||||
if (zipMerge) BoneDatabase.AddMergedBone(mergedSrcBone.transform);
|
||||
mergedSrcBone.transform.SetParent(newParent.transform, true);
|
||||
BoneRemappings[src.transform] = mergedSrcBone.transform;
|
||||
|
||||
bool retain = HasAdditionalComponents(src, out bool needsConstraint);
|
||||
if (needsConstraint)
|
||||
{
|
||||
ParentConstraint constraint = src.AddComponent<ParentConstraint>();
|
||||
constraint.AddSource(new ConstraintSource()
|
||||
{
|
||||
weight = 1,
|
||||
sourceTransform = mergedSrcBone.transform
|
||||
});
|
||||
Matrix4x4 targetToSrc = src.transform.worldToLocalMatrix * newParent.transform.localToWorldMatrix;
|
||||
constraint.translationOffsets = new Vector3[] {targetToSrc.MultiplyPoint(Vector3.zero)};
|
||||
constraint.rotationOffsets = new Vector3[] {targetToSrc.rotation.eulerAngles};
|
||||
constraint.locked = true;
|
||||
constraint.constraintActive = true;
|
||||
}
|
||||
|
||||
List<Transform> children = new List<Transform>();
|
||||
foreach (Transform child in src.transform)
|
||||
{
|
||||
@ -43,17 +175,31 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
var childGameObject = child.gameObject;
|
||||
var childName = childGameObject.name;
|
||||
if (childName.StartsWith(config.prefix) && childName.EndsWith(config.suffix))
|
||||
GameObject childNewParent = mergedSrcBone;
|
||||
bool shouldZip = zipMerge;
|
||||
|
||||
if (shouldZip && childName.StartsWith(config.prefix) && childName.EndsWith(config.suffix))
|
||||
{
|
||||
var targetObjectName = childName.Substring(config.prefix.Length,
|
||||
var targetObjectName = childName.Substring(config.prefix.Length,
|
||||
childName.Length - config.prefix.Length - config.suffix.Length);
|
||||
var targetObject = target.transform.Find(targetObjectName);
|
||||
var targetObject = newParent.transform.Find(targetObjectName);
|
||||
if (targetObject != null)
|
||||
{
|
||||
RecursiveMerge(config, childGameObject, targetObject.gameObject);
|
||||
childNewParent = targetObject.gameObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldZip = false;
|
||||
}
|
||||
}
|
||||
|
||||
var retainChild = RecursiveMerge(config, childGameObject, childNewParent, shouldZip);
|
||||
retain = retain || retainChild;
|
||||
}
|
||||
|
||||
if (!retain) ToDelete.Add(src);
|
||||
|
||||
return retain;
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
|
||||
internal static Transform GetRetargetedBone(Transform bone)
|
||||
{
|
||||
if (!IsRetargetable.ContainsKey(bone)) return null;
|
||||
if (bone == null || !IsRetargetable.ContainsKey(bone)) return null;
|
||||
|
||||
while (bone != null && IsRetargetable.ContainsKey(bone) && IsRetargetable[bone]) bone = bone.parent;
|
||||
|
||||
@ -51,6 +51,13 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
.Select(kvp => new KeyValuePair<Transform, Transform>(kvp.Key, GetRetargetedBone(kvp.Key)))
|
||||
.Where(kvp => kvp.Value != null);
|
||||
}
|
||||
|
||||
public static Transform GetRetargetedBone(Transform bone, bool fallbackToOriginal)
|
||||
{
|
||||
Transform retargeted = GetRetargetedBone(bone);
|
||||
|
||||
return retargeted ? retargeted : (fallbackToOriginal ? bone : null);
|
||||
}
|
||||
}
|
||||
|
||||
internal class RetargetMeshes : IVRCSDKPreprocessAvatarCallback
|
||||
@ -77,19 +84,30 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
}
|
||||
|
||||
// Now remove retargeted bones
|
||||
foreach (var bonePair in BoneDatabase.GetRetargetedBones())
|
||||
if (true)
|
||||
{
|
||||
var sourceBone = bonePair.Key;
|
||||
var destBone = bonePair.Value;
|
||||
|
||||
foreach (Transform child in sourceBone)
|
||||
foreach (var bonePair in BoneDatabase.GetRetargetedBones())
|
||||
{
|
||||
child.SetParent(destBone, true);
|
||||
if (BoneDatabase.GetRetargetedBone(bonePair.Key) == null) continue;
|
||||
|
||||
var sourceBone = bonePair.Key;
|
||||
var destBone = bonePair.Value;
|
||||
|
||||
var children = new List<Transform>();
|
||||
foreach (Transform child in sourceBone)
|
||||
{
|
||||
children.Add(child);
|
||||
}
|
||||
|
||||
foreach (Transform child in children) {
|
||||
child.SetParent(destBone, true);
|
||||
}
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(sourceBone.gameObject);
|
||||
}
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(sourceBone.gameObject);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -171,6 +189,8 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
dst.bindposes = newBindPoses;
|
||||
renderer.bones = newBones;
|
||||
renderer.sharedMesh = dst;
|
||||
renderer.rootBone = BoneDatabase.GetRetargetedBone(renderer.rootBone, true);
|
||||
renderer.probeAnchor = BoneDatabase.GetRetargetedBone(renderer.probeAnchor, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,18 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
internal class CleanupTempAssets : IVRCSDKPostprocessAvatarCallback
|
||||
{
|
||||
public int callbackOrder => 99999;
|
||||
public void OnPostprocessAvatar()
|
||||
{
|
||||
Util.DeleteTemporaryAssets();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Util
|
||||
{
|
||||
internal const string generatedAssetsSubdirectory = "999_Modular_Avatar_Generated";
|
||||
|
@ -13,6 +13,7 @@ namespace net.fushizen.modular_avatar.core
|
||||
public string prefix;
|
||||
public string suffix;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
@ -37,5 +38,6 @@ namespace net.fushizen.modular_avatar.core
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -24,11 +24,11 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.rider": {
|
||||
"version": "1.2.1",
|
||||
"version": "3.0.15",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.test-framework": "1.1.1"
|
||||
"com.unity.ext.nunit": "1.0.6"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
|
@ -35,6 +35,9 @@ GraphicsSettings:
|
||||
- {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_PreloadedShaders: []
|
||||
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
|
||||
type: 0}
|
||||
@ -45,7 +48,7 @@ GraphicsSettings:
|
||||
m_DefaultMobileRenderingPath: 1
|
||||
m_TierSettings: []
|
||||
m_LightmapStripping: 0
|
||||
m_FogStripping: 0
|
||||
m_FogStripping: 1
|
||||
m_InstancingStripping: 0
|
||||
m_LightmapKeepPlain: 1
|
||||
m_LightmapKeepDirCombined: 1
|
||||
|
Loading…
Reference in New Issue
Block a user