diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests.meta b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests.meta new file mode 100644 index 00000000..1d14163d --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 24343d20eb4d4a258e26863ccb46cc1d +timeCreated: 1691928598 \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs new file mode 100644 index 00000000..9f0db65c --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs @@ -0,0 +1,44 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.animation; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using VRC.SDK3.Avatars.Components; + +public class ActiveAnimationRetargeterTests : TestBase +{ + [Test] + public void SimpleRetarget() { + var avatar = CreatePrefab("SimpleRetarget.prefab"); + var descriptor = avatar.GetComponent(); + + // initialize context + var buildContext = new BuildContext(descriptor); + var pathMappings = buildContext.PluginBuildContext.ActivateExtensionContext(); + new MergeAnimatorProcessor().OnPreprocessAvatar(avatar, buildContext); // we need this for AnimationDatabase + buildContext.AnimationDatabase.Bootstrap(descriptor); + + // get game objects + var changedChild = avatar.transform.Find("Toggled/Child"); + var newParent = avatar.transform.Find("NewParent"); + + // do retargeting + var retargeter = new ActiveAnimationRetargeter(buildContext, new BoneDatabase(), changedChild); + var created = retargeter.CreateIntermediateObjects(newParent.gameObject); + retargeter.FixupAnimations(); + + // commit + buildContext.AnimationDatabase.Commit(); + + var clip = findFxClip(avatar, layerName: "retarget"); + var curveBindings = AnimationUtility.GetCurveBindings(clip); + + // Intermediate object must be created + Assert.That(created, Is.Not.EqualTo(newParent.gameObject)); + + // The created animation must have m_IsActive of intermediate object + Assert.That(curveBindings, Does.Contain(EditorCurveBinding.FloatCurve( + pathMappings.GetObjectIdentifier(created), typeof(GameObject), "m_IsActive"))); + } +} \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs.meta b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs.meta new file mode 100644 index 00000000..c747dea4 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/ActiveAnimationRetargeterTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 644d89376db24e3fa21645116fc46ba1 +timeCreated: 1691928769 \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim new file mode 100644 index 00000000..d72de040 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim @@ -0,0 +1,116 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: SimpleRetarget + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: + - curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.33333334 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Toggled + classID: 1 + script: {fileID: 0} + m_PPtrCurves: [] + m_SampleRate: 60 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 758104917 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + pptrCurveMapping: [] + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.33333334 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: + - curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.33333334 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Toggled + classID: 1 + script: {fileID: 0} + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim.meta b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim.meta new file mode 100644 index 00000000..c6e0c922 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5858d87cefe184199a2904d294b5f8d3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller new file mode 100644 index 00000000..e28eb598 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller @@ -0,0 +1,103 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1107 &-8599852744395466123 +AnimatorStateMachine: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: [] + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 0} +--- !u!1102 &-5862847857433847372 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: SimpleRetarget + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 5858d87cefe184199a2904d294b5f8d3, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: SimpleRetarget + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: -8599852744395466123} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} + - serializedVersion: 5 + m_Name: retarget + m_StateMachine: {fileID: 898016236898757940} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1107 &898016236898757940 +AnimatorStateMachine: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: retarget + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: -5862847857433847372} + m_Position: {x: 110, y: 200, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: -5862847857433847372} diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller.meta b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller.meta new file mode 100644 index 00000000..6aadc783 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 27059e37876a5458285a0904e07b983f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab new file mode 100644 index 00000000..70ef55ef --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab @@ -0,0 +1,415 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3928259091898741543 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8769549937025373208} + m_Layer: 0 + m_Name: Child + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8769549937025373208 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3928259091898741543} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4913003977449755838} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4892841545226093997 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1404385703395384853} + - component: {fileID: 4034299884250139105} + - component: {fileID: 5057208987422739041} + - component: {fileID: 8116285043581302730} + m_Layer: 0 + m_Name: SimpleRetarget + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1404385703395384853 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4892841545226093997} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4913003977449755838} + - {fileID: 8273154166008081719} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!95 &4034299884250139105 +Animator: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4892841545226093997} + m_Enabled: 1 + m_Avatar: {fileID: 0} + m_Controller: {fileID: 0} + m_CullingMode: 0 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorControllerStateOnDisable: 0 +--- !u!114 &5057208987422739041 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4892841545226093997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3} + m_Name: + m_EditorClassIdentifier: + Name: + ViewPosition: {x: 0, y: 1.6, z: 0.2} + Animations: 0 + ScaleIPD: 1 + lipSync: 0 + lipSyncJawBone: {fileID: 0} + lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1} + lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1} + VisemeSkinnedMesh: {fileID: 0} + MouthOpenBlendShapeName: Facial_Blends.Jaw_Down + VisemeBlendShapes: [] + unityVersion: + portraitCameraPositionOffset: {x: 0, y: 0, z: 0} + portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139} + networkIDs: [] + customExpressions: 0 + expressionsMenu: {fileID: 0} + expressionParameters: {fileID: 0} + enableEyeLook: 0 + customEyeLookSettings: + eyeMovement: + confidence: 0.5 + excitement: 0.5 + leftEye: {fileID: 0} + rightEye: {fileID: 0} + eyesLookingStraight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingUp: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingDown: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingLeft: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingRight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidType: 0 + upperLeftEyelid: {fileID: 0} + upperRightEyelid: {fileID: 0} + lowerLeftEyelid: {fileID: 0} + lowerRightEyelid: {fileID: 0} + eyelidsDefault: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsClosed: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingUp: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingDown: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsSkinnedMesh: {fileID: 0} + eyelidsBlendshapes: + customizeAnimationLayers: 1 + baseAnimationLayers: + - isEnabled: 0 + type: 0 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 4 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 5 + animatorController: {fileID: 9100000, guid: 27059e37876a5458285a0904e07b983f, + type: 2} + mask: {fileID: 0} + isDefault: 0 + specialAnimationLayers: + - isEnabled: 0 + type: 6 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 7 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 8 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + AnimationPreset: {fileID: 0} + animationHashSet: [] + autoFootsteps: 1 + autoLocomotion: 1 + collider_head: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_torso: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} +--- !u!114 &8116285043581302730 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4892841545226093997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3} + m_Name: + m_EditorClassIdentifier: + launchedFromSDKPipeline: 0 + completedSDKPipeline: 0 + blueprintId: + contentType: 0 + assetBundleUnityVersion: + fallbackStatus: 0 +--- !u!1 &5552471151564693013 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8273154166008081719} + m_Layer: 0 + m_Name: NewParent + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8273154166008081719 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5552471151564693013} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1404385703395384853} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8530643608437214789 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4913003977449755838} + m_Layer: 0 + m_Name: Toggled + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4913003977449755838 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8530643608437214789} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8769549937025373208} + m_Father: {fileID: 1404385703395384853} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab.meta b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab.meta new file mode 100644 index 00000000..317fd738 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/ActiveAnimationRetargeterTests/SimpleRetarget.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 43df0f1469a9e41eb9d0e11067919c29 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest.meta b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest.meta new file mode 100644 index 00000000..80edc96a --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: db9b105182f24b07aa2d35a0265d37f0 +timeCreated: 1691932793 \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab new file mode 100644 index 00000000..22123d1e --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab @@ -0,0 +1,409 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &633397236186228200 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6024653815829872387} + - component: {fileID: 6995900281859911826} + m_Layer: 0 + m_Name: NestedFixed + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6024653815829872387 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 633397236186228200} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1793776364590335641} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &6995900281859911826 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 633397236186228200} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0e2d9f1d69e34b92a96e6cc162770fad, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &1352002985630144344 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1630097882623414224} + - component: {fileID: 2950892147035252982} + - component: {fileID: 304181488106869769} + - component: {fileID: 475944055395189127} + m_Layer: 0 + m_Name: Nested + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1630097882623414224 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1352002985630144344} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1793776364590335641} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!95 &2950892147035252982 +Animator: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1352002985630144344} + m_Enabled: 1 + m_Avatar: {fileID: 0} + m_Controller: {fileID: 0} + m_CullingMode: 0 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorControllerStateOnDisable: 0 +--- !u!114 &304181488106869769 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1352002985630144344} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3} + m_Name: + m_EditorClassIdentifier: + Name: + ViewPosition: {x: 0, y: 1.6, z: 0.2} + Animations: 0 + ScaleIPD: 1 + lipSync: 0 + lipSyncJawBone: {fileID: 0} + lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1} + lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1} + VisemeSkinnedMesh: {fileID: 0} + MouthOpenBlendShapeName: Facial_Blends.Jaw_Down + VisemeBlendShapes: [] + unityVersion: + portraitCameraPositionOffset: {x: 0, y: 0, z: 0} + portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139} + networkIDs: [] + customExpressions: 0 + expressionsMenu: {fileID: 0} + expressionParameters: {fileID: 0} + enableEyeLook: 0 + customEyeLookSettings: + eyeMovement: + confidence: 0.5 + excitement: 0.5 + leftEye: {fileID: 0} + rightEye: {fileID: 0} + eyesLookingStraight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingUp: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingDown: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingLeft: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingRight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidType: 0 + upperLeftEyelid: {fileID: 0} + upperRightEyelid: {fileID: 0} + lowerLeftEyelid: {fileID: 0} + lowerRightEyelid: {fileID: 0} + eyelidsDefault: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsClosed: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingUp: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingDown: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsSkinnedMesh: {fileID: 0} + eyelidsBlendshapes: + customizeAnimationLayers: 0 + baseAnimationLayers: + - isEnabled: 0 + type: 0 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 4 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 5 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + specialAnimationLayers: + - isEnabled: 0 + type: 6 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 7 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 8 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + AnimationPreset: {fileID: 0} + animationHashSet: [] + autoFootsteps: 1 + autoLocomotion: 1 + collider_head: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_torso: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} +--- !u!114 &475944055395189127 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1352002985630144344} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3} + m_Name: + m_EditorClassIdentifier: + launchedFromSDKPipeline: 0 + completedSDKPipeline: 0 + blueprintId: + contentType: 0 + assetBundleUnityVersion: + fallbackStatus: 0 +--- !u!1 &1805588812230664739 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1793776364590335641} + - component: {fileID: 9189897604033275492} + m_Layer: 0 + m_Name: FixedObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1793776364590335641 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1805588812230664739} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6024653815829872387} + m_Father: {fileID: 1630097882623414224} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &9189897604033275492 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1805588812230664739} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0e2d9f1d69e34b92a96e6cc162770fad, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab.meta b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab.meta new file mode 100644 index 00000000..f26af330 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Nested.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dd4f845656231418fa729c023ec9e3b1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab new file mode 100644 index 00000000..fadbd8df --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab @@ -0,0 +1,365 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6945812502350586737 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7029556924238088139} + - component: {fileID: 496475613106972470} + m_Layer: 0 + m_Name: FixedObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7029556924238088139 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6945812502350586737} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 8067499159413706370} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &496475613106972470 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6945812502350586737} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0e2d9f1d69e34b92a96e6cc162770fad, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &7757874029049927178 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8067499159413706370} + - component: {fileID: 5879847453762120100} + - component: {fileID: 9030400794333238619} + - component: {fileID: 9218906121517220053} + m_Layer: 0 + m_Name: Simple + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8067499159413706370 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7757874029049927178} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7029556924238088139} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!95 &5879847453762120100 +Animator: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7757874029049927178} + m_Enabled: 1 + m_Avatar: {fileID: 0} + m_Controller: {fileID: 0} + m_CullingMode: 0 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorControllerStateOnDisable: 0 +--- !u!114 &9030400794333238619 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7757874029049927178} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3} + m_Name: + m_EditorClassIdentifier: + Name: + ViewPosition: {x: 0, y: 1.6, z: 0.2} + Animations: 0 + ScaleIPD: 1 + lipSync: 0 + lipSyncJawBone: {fileID: 0} + lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1} + lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1} + VisemeSkinnedMesh: {fileID: 0} + MouthOpenBlendShapeName: Facial_Blends.Jaw_Down + VisemeBlendShapes: [] + unityVersion: + portraitCameraPositionOffset: {x: 0, y: 0, z: 0} + portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139} + networkIDs: [] + customExpressions: 0 + expressionsMenu: {fileID: 0} + expressionParameters: {fileID: 0} + enableEyeLook: 0 + customEyeLookSettings: + eyeMovement: + confidence: 0.5 + excitement: 0.5 + leftEye: {fileID: 0} + rightEye: {fileID: 0} + eyesLookingStraight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingUp: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingDown: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingLeft: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyesLookingRight: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidType: 0 + upperLeftEyelid: {fileID: 0} + upperRightEyelid: {fileID: 0} + lowerLeftEyelid: {fileID: 0} + lowerRightEyelid: {fileID: 0} + eyelidsDefault: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsClosed: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingUp: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsLookingDown: + upper: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + lower: + linked: 1 + left: {x: 0, y: 0, z: 0, w: 0} + right: {x: 0, y: 0, z: 0, w: 0} + eyelidsSkinnedMesh: {fileID: 0} + eyelidsBlendshapes: + customizeAnimationLayers: 0 + baseAnimationLayers: + - isEnabled: 0 + type: 0 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 4 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 5 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + specialAnimationLayers: + - isEnabled: 0 + type: 6 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 7 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + - isEnabled: 0 + type: 8 + animatorController: {fileID: 0} + mask: {fileID: 0} + isDefault: 1 + AnimationPreset: {fileID: 0} + animationHashSet: [] + autoFootsteps: 1 + autoLocomotion: 1 + collider_head: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_torso: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_footL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_handL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleL: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerIndexR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerMiddleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerRingR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} + collider_fingerLittleR: + isMirrored: 1 + state: 0 + transform: {fileID: 0} + radius: 0 + height: 0 + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0, w: 1} +--- !u!114 &9218906121517220053 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7757874029049927178} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3} + m_Name: + m_EditorClassIdentifier: + launchedFromSDKPipeline: 0 + completedSDKPipeline: 0 + blueprintId: + contentType: 0 + assetBundleUnityVersion: + fallbackStatus: 0 diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab.meta b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab.meta new file mode 100644 index 00000000..5e419cf3 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/Simple.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 80c58abfb3541481fa146cfb8d98ede3 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs new file mode 100644 index 00000000..1c54bf1d --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs @@ -0,0 +1,67 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.animation; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEngine.Animations; +using VRC.SDK3.Avatars.Components; + +public class WorldFixedObjectTest : TestBase +{ + [Test] + public void SimpleTest() + { + var avatar = CreatePrefab("Simple.prefab"); + var descriptor = avatar.GetComponent(); + + var fixedObject = avatar.transform.Find("FixedObject"); + + // initialize context + var buildContext = new BuildContext(descriptor); + buildContext.PluginBuildContext.ActivateExtensionContext(); + + new WorldFixedObjectProcessor(descriptor).Process(buildContext); + + var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)"); + var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject"); + + // fixed root is created + Assert.That(fixedRoot, Is.Not.Null); + Assert.That(fixedRoot.GetComponent(), Is.Not.Null); + + // objects are moved to fixed root + Assert.That(movedFixedObject, Is.Not.Null); + Assert.That(movedFixedObject, Is.EqualTo(fixedObject)); + } + + [Test] + public void NestedTest() + { + var avatar = CreatePrefab("Nested.prefab"); + var descriptor = avatar.GetComponent(); + + var fixedObject = avatar.transform.Find("FixedObject"); + var nestedFixed = avatar.transform.Find("FixedObject/NestedFixed"); + + // initialize context + var buildContext = new BuildContext(descriptor); + buildContext.PluginBuildContext.ActivateExtensionContext(); + + new WorldFixedObjectProcessor(descriptor).Process(buildContext); + + var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)"); + var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject"); + var nestedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/NestedFixed"); + + // fixed root is created + Assert.That(fixedRoot, Is.Not.Null); + Assert.That(fixedRoot.GetComponent(), Is.Not.Null); + + // objects are moved to fixed root + Assert.That(movedFixedObject, Is.Not.Null); + Assert.That(movedFixedObject, Is.EqualTo(fixedObject)); + + // objects are moved to fixed root + Assert.That(nestedFixedObject, Is.Not.Null); + Assert.That(nestedFixedObject, Is.EqualTo(nestedFixed)); + } +} diff --git a/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs.meta b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs.meta new file mode 100644 index 00000000..2daa9a5e --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/WorldFixedObjectTest/WorldFixedObjectTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dd65b27d8f9d40b0a33a36f4c5f125db +timeCreated: 1691932797 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Assets.meta b/Packages/nadena.dev.modular-avatar/Assets.meta new file mode 100644 index 00000000..f3ba2ded --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Assets.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 45facc2394ac4e6d9c6a8427227dc931 +timeCreated: 1691906506 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab b/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab new file mode 100644 index 00000000..17457c6e --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab @@ -0,0 +1,32 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4316036803263603590 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8830680306940261232} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8830680306940261232 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4316036803263603590} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab.meta b/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab.meta new file mode 100644 index 00000000..477f8162 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 78828bfbcb4cb4ce3b00de044eb2d927 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs b/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs new file mode 100644 index 00000000..0fbad44e --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using nadena.dev.modular_avatar.animation; +using UnityEditor; +using UnityEngine; +using VRC.SDK3.Avatars.Components; +using Object = UnityEngine.Object; + +namespace nadena.dev.modular_avatar.core.editor +{ + /// + /// The class to retarget m_IsActive animation to moved multiple objects + /// + internal class ActiveAnimationRetargeter + { + private readonly BuildContext _context; + private readonly BoneDatabase _boneDatabase; + private readonly TrackObjectRenamesContext _pathMappings; + private readonly List _intermediateObjs = new List(); + + /// + /// Tracks an object whose Active state is animated, and which leads up to this Merge Animator component. + /// We use this tracking data to create proxy objects within the main armature, which track the same active + /// state. + /// + struct IntermediateObj + { + /// + /// The path of original object + /// + public string OriginalPath; + + /// + /// Name of the intermediate object. Used to name proxy objects. + /// + public string Name; + + /// + /// Whether this object is initially active. + /// + public bool InitiallyActive; + + /// + /// List of created Intermediate Objects + /// + public List Created; + } + + public ActiveAnimationRetargeter( + BuildContext context, + BoneDatabase boneDatabase, + Transform root + ) + { + _context = context; + _boneDatabase = boneDatabase; + _pathMappings = context.PluginBuildContext.Extension(); + + while (root != null && root.GetComponent() == null) + { + var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject); + System.Diagnostics.Debug.Assert(originalPath != null); + + if (context.AnimationDatabase.ClipsForPath(originalPath).Any(clip => + GetActiveBinding(clip.CurrentClip as AnimationClip, originalPath) != null + )) + { + _intermediateObjs.Add(new IntermediateObj + { + OriginalPath = originalPath, + Name = $"{root.gameObject.name}${Guid.NewGuid()}", + InitiallyActive = root.gameObject.activeSelf, + Created = new List(), + }); + } + + root = root.parent; + } + + // currently _intermediateObjs is in child -> parent order. + // we want parent -> child order so reverse entire list + _intermediateObjs.Reverse(); + } + + public IEnumerable AddedGameObjects => _intermediateObjs.SelectMany(x => x.Created); + + public GameObject CreateIntermediateObjects(GameObject sourceBone) + { + for (var i = 0; i < _intermediateObjs.Count; i++) + { + var intermediate = _intermediateObjs[i]; + var preexisting = sourceBone.transform.Find(intermediate.Name); + if (preexisting != null) + { + sourceBone = preexisting.gameObject; + continue; + } + + var switchObj = new GameObject(intermediate.Name); + switchObj.transform.SetParent(sourceBone.transform, false); + switchObj.transform.localPosition = Vector3.zero; + switchObj.transform.localRotation = Quaternion.identity; + switchObj.transform.localScale = Vector3.one; + switchObj.SetActive(intermediate.InitiallyActive); + + if (i == 0) + { + // This new leaf can break parent bone physbones. Add a PB Blocker + // to prevent this becoming an issue. + switchObj.GetOrAddComponent(); + } + + intermediate.Created.Add(switchObj); + + sourceBone = switchObj; + + // Ensure mesh retargeting looks through this + _boneDatabase.AddMergedBone(sourceBone.transform); + _boneDatabase.RetainMergedBone(sourceBone.transform); + _pathMappings.MarkTransformLookthrough(sourceBone); + } + + return sourceBone; + } + + public void FixupAnimations() + { + foreach (var intermediate in _intermediateObjs) + { + var path = intermediate.OriginalPath; + + foreach (var holder in _context.AnimationDatabase.ClipsForPath(path)) + { + if (!Util.IsTemporaryAsset(holder.CurrentClip)) + { + holder.CurrentClip = Object.Instantiate(holder.CurrentClip); + } + + var clip = holder.CurrentClip as AnimationClip; + if (clip == null) continue; + + var curve = GetActiveBinding(clip, path); + if (curve != null) + { + foreach (var mapping in intermediate.Created) + { + clip.SetCurve(_pathMappings.GetObjectIdentifier(mapping), typeof(GameObject), "m_IsActive", + curve); + } + } + } + } + } + + private AnimationCurve GetActiveBinding(AnimationClip clip, string path) + { + return AnimationUtility.GetEditorCurve(clip, + EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive")); + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs.meta new file mode 100644 index 00000000..11dae49b --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/ActiveAnimationRetargeter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ebce0ceba4d48c48992702b5c446936 +timeCreated: 1691915242 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs b/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs index d6e82f63..57908f9d 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs @@ -21,7 +21,10 @@ namespace nadena.dev.modular_avatar.core.editor internal readonly Dictionary ClonedMenus = new Dictionary(); - + + public static implicit operator BuildContext(ndmf.BuildContext ctx) => + ctx.Extension().BuildContext; + /// /// This dictionary overrides the _original contents_ of ModularAvatarMenuInstallers. Notably, this does not /// replace the source menu for the purposes of identifying any other MAMIs that might install to the same diff --git a/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs b/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs new file mode 100644 index 00000000..4b3d64dd --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs @@ -0,0 +1,22 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace nadena.dev.modular_avatar.core.editor +{ + [CustomEditor(typeof(ModularAvatarWorldFixedObject))] + internal class WorldFixedObjectEditor : MAEditorBase + { + protected override void OnInnerInspectorGUI() + { +#if UNITY_ANDROID + EditorGUILayout.HelpBox(Localization.S("worldfixed.quest"), MessageType.Warning); + +#else + EditorGUILayout.HelpBox(Localization.S("worldfixed.normal"), MessageType.Info); +#endif + + Localization.ShowLanguageUI(); + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs.meta new file mode 100644 index 00000000..012f00db --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/Inspector/WorldFixedObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 649616c47b7842a6963efa48a0cace44 +timeCreated: 1691907086 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/Localization/en.json b/Packages/nadena.dev.modular-avatar/Editor/Localization/en.json index a9fa0b84..85ccb2a1 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/Localization/en.json +++ b/Packages/nadena.dev.modular-avatar/Editor/Localization/en.json @@ -51,6 +51,8 @@ "merge_animator.path_mode.tooltip": "How to interpret paths in animations. Using relative mode lets you record animations from an animator on this object.", "merge_animator.match_avatar_write_defaults": "Match Avatar Write Defaults", "merge_animator.match_avatar_write_defaults.tooltip": "Match the write defaults setting used on the avatar's animator. If the avatar's write defaults settings are inconsistent, the settings on the animator will be left alone.", + "worldfixed.quest": "This component is not compatible with the standalone Oculus Quest and will have no effect.", + "worldfixed.normal": "This object will be fixed to world unless you fixed to avatar with constraint.", "fpvisible.normal": "This object will be visible in your first person view.", "fpvisible.NotUnderHead": "This component has no effect when not placed under the head bone.", "fpvisible.quest": "This component is not compatible with the standalone Oculus Quest and will have no effect.", diff --git a/Packages/nadena.dev.modular-avatar/Editor/Localization/ja.json b/Packages/nadena.dev.modular-avatar/Editor/Localization/ja.json index 3282beb1..55898077 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/Localization/ja.json +++ b/Packages/nadena.dev.modular-avatar/Editor/Localization/ja.json @@ -50,6 +50,8 @@ "merge_animator.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます", "merge_animator.match_avatar_write_defaults": "アバターのWrite Defaults設定に合わせる", "merge_animator.match_avatar_write_defaults.tooltip": "アバターの該当アニメーターのWrite Defaults設定に合わせます。アバター側の設定が矛盾する場合は、統合されるアニメーターのWD値がそのまま採用されます。", + "worldfixed.quest": "このコンポーネントはクエスト単体非対応のため無効となっています。", + "worldfixed.normal": "このオブジェクトはConstraint等でアバターに追従させない限りワールドに固定されます。", "fpvisible.normal": "このオブジェクトは一人視点で表示されます。", "fpvisible.quest": "このコンポーネントはクエスト単体非対応のため無効化となっています。", "fpvisible.NotUnderHead": "このコンポーネントはヘッドボーン外では効果がありません。", diff --git a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs index 663de664..6352f77a 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs @@ -31,7 +31,6 @@ using UnityEditor; using UnityEngine; using UnityEngine.Animations; using VRC.Dynamics; -using VRC.SDK3.Avatars.Components; using VRC.SDK3.Dynamics.PhysBone.Components; using Object = UnityEngine.Object; @@ -222,122 +221,20 @@ namespace nadena.dev.modular_avatar.core.editor return hasComponents; } - /// - /// Tracks an object whose Active state is animated, and which leads up to this Merge Animator component. - /// We use this tracking data to create proxy objects within the main armature, which track the same active - /// state. - /// - struct IntermediateObj - { - /// - /// Name of the intermediate object. Used to name proxy objects. - /// - public string name; - - /// - /// The original path of this intermediate object. - /// - public string originPath; - - /// - /// Whether this object is initially active. - /// - public bool active; - } - - private List intermediateObjects = new List(); - - private Dictionary> - activationPathMappings = new Dictionary>(); + private ActiveAnimationRetargeter _activeRetargeter; private void MergeArmature(ModularAvatarMergeArmature mergeArmature, GameObject mergeTargetObject) { // TODO: error reporting? if (mergeTargetObject == null) return; - GatherActiveStatePaths(mergeArmature.transform); + _activeRetargeter = new ActiveAnimationRetargeter(context, BoneDatabase, mergeArmature.transform); RecursiveMerge(mergeArmature, mergeArmature.gameObject, mergeTargetObject, true); - FixupAnimations(); - } + _activeRetargeter.FixupAnimations(); - private AnimationCurve GetActiveBinding(AnimationClip clip, string path) - { - return AnimationUtility.GetEditorCurve(clip, - EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive")); - } - - private void FixupAnimations() - { - foreach (var kvp in activationPathMappings) - { - var path = kvp.Key; - var mappings = kvp.Value; - - foreach (var holder in context.AnimationDatabase.ClipsForPath(path)) - { - if (!Util.IsTemporaryAsset(holder.CurrentClip)) - { - holder.CurrentClip = Object.Instantiate(holder.CurrentClip); - } - - var clip = holder.CurrentClip as AnimationClip; - if (clip == null) continue; - - var curve = GetActiveBinding(clip, path); - if (curve != null) - { - foreach (var mapping in mappings) - { - clip.SetCurve(PathMappings.GetObjectIdentifier(mapping), typeof(GameObject), "m_IsActive", - curve); - } - } - } - } - } - - private void GatherActiveStatePaths(Transform root) - { - intermediateObjects.Clear(); - activationPathMappings.Clear(); - - List rootPath = new List(); - - while (root != null && root.GetComponent() == null) - { - rootPath.Insert(0, new IntermediateObj() - { - name = root.name, - originPath = RuntimeUtil.AvatarRootPath(root.gameObject), - active = root.gameObject.activeSelf - }); - root = root.parent; - } - - var prefix = ""; - - for (int i = 1; i <= rootPath.Count; i++) - { - var srcPrefix = string.Join("/", rootPath.Take(i).Select(p => p.name)); - if (context.AnimationDatabase.ClipsForPath(srcPrefix).Any(clip => - GetActiveBinding(clip.CurrentClip as AnimationClip, srcPrefix) != null - )) - { - var intermediate = rootPath[i - 1].name + "$" + Guid.NewGuid().ToString(); - var originPath = rootPath[i - 1].originPath; - intermediateObjects.Add(new IntermediateObj() - { - name = intermediate, - originPath = originPath, - active = rootPath[i - 1].active - }); - if (prefix.Length > 0) prefix += "/"; - prefix += intermediate; - activationPathMappings[originPath] = new List(); - } - } + thisPassAdded.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform)); } /** @@ -366,49 +263,7 @@ namespace nadena.dev.modular_avatar.core.editor GameObject mergedSrcBone = newParent; if (retain) - { - mergedSrcBone = newParent; - var switchPath = ""; - foreach (var intermediate in intermediateObjects) - { - var preexisting = mergedSrcBone.transform.Find(intermediate.name); - if (preexisting != null) - { - mergedSrcBone = preexisting.gameObject; - continue; - } - - var switchObj = new GameObject(intermediate.name); - switchObj.transform.SetParent(mergedSrcBone.transform, false); - switchObj.transform.localPosition = Vector3.zero; - switchObj.transform.localRotation = Quaternion.identity; - switchObj.transform.localScale = Vector3.one; - switchObj.SetActive(intermediate.active); - - if (switchPath.Length > 0) - { - switchPath += "/"; - } - else - { - // This new leaf can break parent bone physbones. Add a PB Blocker - // to prevent this becoming an issue. - switchObj.GetOrAddComponent(); - } - - switchPath += intermediate.name; - - activationPathMappings[intermediate.originPath].Add(switchObj); - - mergedSrcBone = switchObj; - - // Ensure mesh retargeting looks through this - BoneDatabase.AddMergedBone(mergedSrcBone.transform); - BoneDatabase.RetainMergedBone(mergedSrcBone.transform); - PathMappings.MarkTransformLookthrough(mergedSrcBone); - thisPassAdded.Add(mergedSrcBone.transform); - } - } + mergedSrcBone = _activeRetargeter.CreateIntermediateObjects(newParent); var isPrefabInstance = PrefabUtility.IsPartOfPrefabInstance(src.transform); var isPrefabAsset = PrefabUtility.IsPartOfPrefabAsset(src.transform); diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs index 3be05e17..7d93cccc 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs @@ -41,6 +41,9 @@ namespace nadena.dev.modular_avatar.core.editor.plugin seq.Run(MergeArmaturePluginPass.Instance); seq.Run(BoneProxyPluginPass.Instance); seq.Run(VisibleHeadAccessoryPluginPass.Instance); + seq.Run("World Fixed Object", + ctx => new WorldFixedObjectProcessor(ctx.AvatarDescriptor).Process(ctx) + ); seq.Run(ReplaceObjectPluginPass.Instance); }); seq.Run(BlendshapeSyncAnimationPluginPass.Instance); diff --git a/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs b/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs new file mode 100644 index 00000000..fa5a5617 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs @@ -0,0 +1,93 @@ +using System.Linq; +using nadena.dev.modular_avatar.editor.ErrorReporting; +using UnityEditor; +using UnityEngine; +using UnityEngine.Animations; +using VRC.SDK3.Avatars.Components; + +namespace nadena.dev.modular_avatar.core.editor +{ + internal class WorldFixedObjectProcessor + { + private BuildContext _context; + private VRCAvatarDescriptor _avatar; + private Transform _proxy; + + public WorldFixedObjectProcessor(VRCAvatarDescriptor avatar) + { + _avatar = avatar; + } + + public void Process(BuildContext context) + { + _context = context; + foreach (var target in _avatar.GetComponentsInChildren(true) + .OrderByDescending(x => NestCount(x.transform))) + BuildReport.ReportingObject(target, () => Process(target)); + } + + int NestCount(Transform transform) + { + int count = 0; + while (transform.parent != null) transform = transform.parent; + return count; + } + + void Process(ModularAvatarWorldFixedObject target) + { + var retargeter = new ActiveAnimationRetargeter( + _context, + new BoneDatabase(), + target.transform + ); + + var proxy = CreateProxy(); + + var parent = retargeter.CreateIntermediateObjects(proxy.gameObject); + + var xform = target.transform; + + var pscale = proxy.lossyScale; + var oscale = xform.lossyScale; + xform.localScale = new Vector3(oscale.x / pscale.x, oscale.y / pscale.y, oscale.z / pscale.z); + + target.transform.SetParent(parent.transform, true); + + retargeter.FixupAnimations(); + + Object.DestroyImmediate(target); + } + + private Transform CreateProxy() + { + if (_proxy != null) return _proxy; + + // 78828bfbcb4cb4ce3b00de044eb2d927: Assets/FixedPrefab.prefab + var fixedGameObject = AssetDatabase.LoadAssetAtPath( + AssetDatabase.GUIDToAssetPath("78828bfbcb4cb4ce3b00de044eb2d927")); + + var avatarRoot = _avatar.transform; + GameObject obj = new GameObject("(MA WorldFixedRoot)"); + + obj.transform.SetParent(avatarRoot, false); + obj.transform.localPosition = Vector3.zero; + obj.transform.localRotation = Quaternion.identity; + obj.transform.localScale = Vector3.one; + + var constraint = obj.AddComponent(); + constraint.AddSource(new ConstraintSource() + { + weight = 1.0f, + sourceTransform = fixedGameObject.transform, + }); + constraint.constraintActive = true; + constraint.locked = true; + constraint.rotationOffsets = new[] {Vector3.zero}; + constraint.translationOffsets = new[] {Vector3.zero}; + + _proxy = obj.transform; + + return obj.transform; + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs.meta new file mode 100644 index 00000000..0cc5bba9 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/WorldFixedObjectProcessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 050f048a6b6a4714818573e27d1c19ac +timeCreated: 1691905335 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs new file mode 100644 index 00000000..a42e9c30 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace nadena.dev.modular_avatar.core +{ + [AddComponentMenu("Modular Avatar/MA World Fixed Object")] + [DisallowMultipleComponent] + public class ModularAvatarWorldFixedObject : AvatarTagComponent + { + // no configuration needed + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs.meta b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs.meta new file mode 100644 index 00000000..9a468132 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarWorldFixedObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e2d9f1d69e34b92a96e6cc162770fad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/docs/docs/reference/world-fixed-object.md b/docs/docs/reference/world-fixed-object.md new file mode 100644 index 00000000..7c871996 --- /dev/null +++ b/docs/docs/reference/world-fixed-object.md @@ -0,0 +1,21 @@ +# World Fixed Object + +![World Fixed Object component](world-fixed-object.png) + +This component can be used to make a GameObject stay in place relative to the world, even when the avatar moves. + +## When should I use it? + +When you want to have a prop or object stay in place when your avatar moves. + +## Setting up World Fixed Object + +Attach a `World Fixed Object` component to a GameObject. There are no configuration options to set. + +The component will automatically generate a world-origin fixed GameObject at the avatar root and move your GameObject +to its child. You can control the position of GameObjects within a World Fixed Object using e.g. Parent Constraints. + +Only one constraint will be generated, even if multiple World Fixed Object components are used. +As such, the performance impact of this component is the same whether you use one or dozens. + +Due to technical limitations on the Quest, this component has no effect when building for Quest standalone. \ No newline at end of file diff --git a/docs/docs/reference/world-fixed-object.png b/docs/docs/reference/world-fixed-object.png new file mode 100644 index 00000000..c1ed6711 Binary files /dev/null and b/docs/docs/reference/world-fixed-object.png differ diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.md b/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.md new file mode 100644 index 00000000..cf151ef6 --- /dev/null +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.md @@ -0,0 +1,20 @@ +# World Fixed Object + +![World Fixed Object component](world-fixed-object.png) + +このコンポーネントを付けることで、GameObjectをワールドに固定できます。 + +## いつ使うもの? + +GameObjectをワールド固定したいとき。 + +## セットアップ方法 + +ワールド固定したいGameObjectにWorld Fixed Objectコンポーネントをつけるだけです。設定はありません。 + +このコンポーネントは自動的にワールド原点に固定されたGameObjectをアタバー直下に作り、その子にWorld Fixed ObjectのついたGameObjectを移動させます。 +World Fixed ObjectのついたGameObjectはParent Constraint等を使用してワールド内の位置を調整できます。 + +複数のWorld Fixed Objectコンポーネントを使っても、Constraintは一つだけです。なので、複数のGameObjectを指定しても、その分重くなることはありません。 + +技術的な制約により、Quest単体では動作できません。Quest向けのビルドはつけたままにしてもいいが、効果は発揮しません。 diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.png b/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.png new file mode 100644 index 00000000..24662471 Binary files /dev/null and b/docs/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-fixed-object.png differ