Add BoundsOverride module and BoundsOverrideBlocker module

This commit is contained in:
raiti-chan 2023-05-31 22:37:10 +09:00
parent f4d8383ced
commit 17589fceb5
15 changed files with 421 additions and 0 deletions

View File

@ -199,6 +199,7 @@ namespace nadena.dev.modular_avatar.core.editor
new MergeArmatureHook().OnPreprocessAvatar(context, avatarGameObject);
new BoneProxyProcessor().OnPreprocessAvatar(avatarGameObject);
new VisibleHeadAccessoryProcessor(vrcAvatarDescriptor).Process(context);
new BoundsOverrideProcessor().OnProcessAvatar(avatarGameObject);
new RemapAnimationPass(vrcAvatarDescriptor).Process(context.AnimationDatabase);
new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(avatarGameObject, context);
PhysboneBlockerPass.Process(avatarGameObject);

View File

@ -0,0 +1,156 @@
/*
* MIT License
*
* Copyright (c) 2022 bd_
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System.Collections.Generic;
using System.Linq;
using nadena.dev.modular_avatar.editor.ErrorReporting;
using UnityEngine;
namespace nadena.dev.modular_avatar.core.editor
{
internal class BoundsOverrideProcessor
{
internal void OnProcessAvatar(GameObject avatarObject)
{
Queue<ModularAvatarBoundsOverride> overrides = new Queue<ModularAvatarBoundsOverride>();
Queue<ModularAvatarBoundsOverrideBlocker> blockers = new Queue<ModularAvatarBoundsOverrideBlocker>();
FindTopLevelOverrides(avatarObject.transform, overrides);
while (overrides.Count > 0 || blockers.Count > 0)
{
while (overrides.Count > 0)
{
var processTargetOverride = overrides.Dequeue();
BuildReport.ReportingObject(processTargetOverride,
() => ProcessOverride(processTargetOverride, overrides, blockers));
}
while (blockers.Count > 0)
{
var processTargetBlocker = blockers.Dequeue();
foreach (Transform children in processTargetBlocker.transform.OfType<Transform>())
{
FindTopLevelOverrides(children, overrides);
}
}
}
}
private static void FindTopLevelOverrides(Transform root, Queue<ModularAvatarBoundsOverride> overrides)
{
Queue<Transform> childrenQueue = new Queue<Transform>();
childrenQueue.Enqueue(root);
while (childrenQueue.Count > 0)
{
var currentTransform = childrenQueue.Dequeue();
var currentOverride = currentTransform.GetComponent<ModularAvatarBoundsOverride>();
if (currentOverride != null)
{
overrides.Enqueue(currentOverride);
continue;
}
foreach (Transform children in currentTransform.OfType<Transform>())
{
childrenQueue.Enqueue(children);
}
}
}
private static void ProcessOverride(ModularAvatarBoundsOverride targetOverride,
Queue<ModularAvatarBoundsOverride> overrides, Queue<ModularAvatarBoundsOverrideBlocker> blockers)
{
var targetRenderer = targetOverride.GetComponent<SkinnedMeshRenderer>();
if (targetRenderer != null)
{
var rootBone = targetOverride.rootBoneTarget.Get(targetOverride)?.transform;
if (rootBone != null)
{
targetRenderer.rootBone = targetOverride.rootBoneTarget.Get(targetOverride)?.transform;
}
targetRenderer.localBounds = targetOverride.bounds;
}
var processTargetBlocker = targetOverride.GetComponent<ModularAvatarBoundsOverrideBlocker>();
if (processTargetBlocker != null)
{
blockers.Enqueue(processTargetBlocker);
return;
}
ProcessOverrideChildren(targetOverride, overrides, blockers);
}
private static void ProcessOverrideChildren(ModularAvatarBoundsOverride targetOverride,
Queue<ModularAvatarBoundsOverride> overrides, Queue<ModularAvatarBoundsOverrideBlocker> blockers)
{
Queue<Transform> childrenQueue = new Queue<Transform>();
foreach (Transform children in targetOverride.transform.OfType<Transform>())
{
childrenQueue.Enqueue(children);
}
while (childrenQueue.Count > 0)
{
var currentTransform = childrenQueue.Dequeue();
var currentOverride = currentTransform.GetComponent<ModularAvatarBoundsOverride>();
if (currentOverride != null)
{
overrides.Enqueue(currentOverride);
continue;
}
var currentBlocker = currentTransform.GetComponent<ModularAvatarBoundsOverrideBlocker>();
if (currentBlocker != null)
{
blockers.Enqueue(currentBlocker);
continue;
}
var currentRenderer = currentTransform.GetComponent<SkinnedMeshRenderer>();
if (currentRenderer != null)
{
var rootBone = targetOverride.rootBoneTarget.Get(targetOverride)?.transform;
if (rootBone != null)
{
currentRenderer.rootBone = targetOverride.rootBoneTarget.Get(targetOverride)?.transform;
}
currentRenderer.localBounds = targetOverride.bounds;
}
foreach (Transform children in currentTransform.OfType<Transform>())
{
childrenQueue.Enqueue(children);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8585986069504c0f84a6a1d5c559f36c
timeCreated: 1685441711

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using nadena.dev.modular_avatar.core;
using nadena.dev.modular_avatar.core.menu;
using NUnit.Framework;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
@ -21,6 +22,8 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
return CheckInternal(bs);
case ModularAvatarBoneProxy bp:
return CheckInternal(bp);
case ModularAvatarBoundsOverride bo:
return CheckInternal(bo);
case ModularAvatarMenuInstaller mi:
return CheckInternal(mi);
case ModularAvatarMergeAnimator obj:
@ -135,6 +138,19 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
return null;
}
private static List<ErrorLog> CheckInternal(ModularAvatarBoundsOverride bo)
{
if (bo.rootBoneTarget.Get(bo) == null)
{
return new List<ErrorLog>()
{
new ErrorLog(ReportLevel.Validation, "validation.bounds_override.no_target", bo)
};
}
return null;
}
private static List<ErrorLog> CheckInternal(ModularAvatarMenuInstaller mi)
{
// TODO - check that target menu is in the avatar

View File

@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2022 bd_
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using UnityEditor;
using static nadena.dev.modular_avatar.core.editor.Localization;
namespace nadena.dev.modular_avatar.core.editor
{
[CustomEditor(typeof(ModularAvatarBoundsOverrideBlocker))]
[CanEditMultipleObjects]
internal class BoundsOverrideBlockerEditor : MAEditorBase
{
protected override void OnInnerInspectorGUI()
{
EditorGUILayout.HelpBox(S("bounds_override_blocker.help"), MessageType.Info);
Localization.ShowLanguageUI();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 93f879e0ab854080a081f66efdbd490a
timeCreated: 1685421592

View File

@ -0,0 +1,91 @@
/*
* MIT License
*
* Copyright (c) 2022 bd_
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using UnityEditor;
using UnityEngine;
using static nadena.dev.modular_avatar.core.editor.Localization;
namespace nadena.dev.modular_avatar.core.editor
{
[CustomEditor(typeof(ModularAvatarBoundsOverride))]
[CanEditMultipleObjects]
internal class BoundsOverrideEditor : MAEditorBase
{
private SerializedProperty _rootBoneTargetProperty;
private SerializedProperty _boundsProperty;
private void OnEnable()
{
_rootBoneTargetProperty = serializedObject.FindProperty(nameof(ModularAvatarBoundsOverride.rootBoneTarget));
_boundsProperty = serializedObject.FindProperty(nameof(ModularAvatarBoundsOverride.bounds));
}
protected override void OnInnerInspectorGUI()
{
// TODO: 言語ファイル対応
EditorGUILayout.HelpBox(S("bounds_override.help"), MessageType.Info);
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
{
serializedObject.Update();
EditorGUILayout.PropertyField(_rootBoneTargetProperty, G("bounds_override.root_bone"));
EditorGUILayout.PropertyField(_boundsProperty, G("bounds_override.bounds"));
if (changeCheckScope.changed)
{
serializedObject.ApplyModifiedProperties();
}
}
Localization.ShowLanguageUI();
}
[DrawGizmo(GizmoType.Selected)]
private static void DrawGizmo(ModularAvatarBoundsOverride component, GizmoType gizmoType)
{
Matrix4x4 oldMatrix = Gizmos.matrix;
Vector3 center = component.bounds.center;
Vector3 size = component.bounds.size;
try
{
Transform rootBone = component.rootBoneTarget.Get(component)?.transform;
if (rootBone != null)
{
Gizmos.matrix *= rootBone.localToWorldMatrix;
}
} catch (NullReferenceException e)
{
Console.WriteLine(e);
component.rootBoneTarget.referencePath = null;
}
Gizmos.DrawWireCube(center, size);
Gizmos.matrix = oldMatrix;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b5522e0754434ce98886989dca5a09da
timeCreated: 1685420534

View File

@ -64,6 +64,10 @@
"boneproxy.attachment.AsChildKeepWorldPose": "As child; keep position and rotation",
"boneproxy.attachment.AsChildKeepPosition": "As child; keep position",
"boneproxy.attachment.AsChildKeepRotation": "As child; keep rotation",
"bounds_override.help": "Set the Bounds and RootBoon of the Renderer in this object.",
"bounds_override.root_bone": "Root Bone",
"bounds_override.bounds": "Bounds",
"bounds_override_blocker.help": "Within this object will no longer be affected by the parent's BoundsOverride.",
"pb_blocker.help": "This object will not be affected by PhysBones attached to parents.",
"hint.bad_vrcsdk": "Incompatible version of VRCSDK detected.\n\nPlease try upgrading your VRCSDK; if this does not work, check for a newer version of Modular Avatar as well.",
"error.stack_trace": "Stack trace (provide this when reporting bugs!)",
@ -80,6 +84,7 @@
"validation.blendshape_sync.missing_target_renderer": "No renderer found on the target object",
"validation.blendshape_sync.missing_target_mesh": "No mesh found on the renderer on the target object",
"validation.bone_proxy.no_target": "No target object specified (or target object not found)",
"validation.bounds_override.no_target": "No target object specified (or target object not found)",
"validation.menu_installer.no_menu": "No menu to install specified",
"validation.merge_animator.no_animator": "No animator to merge specified",
"validation.merge_armature.no_target": "No merge target specified",

View File

@ -62,6 +62,10 @@
"boneproxy.attachment.AsChildKeepWorldPose": "子として・ワールド位置と向きを維持",
"boneproxy.attachment.AsChildKeepPosition": "子として・ワールド位置を維持",
"boneproxy.attachment.AsChildKeepRotation": "子として・ワールド向きを維持",
"bounds_override.help": "このオブジェクト内のRendererのBounds、RootBoonを設定します。",
"bounds_override.root_bone": "ルートボーン",
"bounds_override.bounds": "バウンズ",
"bounds_override_blocker.help": "このオブジェクト内は親のBoundsOverrideの影響を受けなくなります。",
"pb_blocker.help": "このオブジェクトは親のPhysBoneから影響を受けなくなります。",
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでもだめでしたら、Modular Avatarにも最新版が出てないかチェックしてください。",
"error.stack_trace": "スタックトレース(バグを報告する時は必ず添付してください!)",
@ -78,6 +82,7 @@
"validation.blendshape_sync.missing_target_renderer": "同期元のオブジェクトにはSkinnedMeshRendererがありません。",
"validation.blendshape_sync.missing_target_mesh": "同期元のオブジェクトにはSkinnedMeshRendererがありますが、メッシュがありません。",
"validation.bone_proxy.no_target": "ターゲットオブジェクトが未設定、もしくは存在しません。",
"validation.bounds_override.no_target": "ターゲットオブジェクトが未設定、もしくは存在しません。",
"validation.menu_installer.no_menu": "インストールするメニューがありません。",
"validation.merge_animator.no_animator": "Animator Controllerがありません。",
"validation.merge_armature.no_target": "ターゲットオブジェクトが未設定、もしくは存在しません。",

View File

@ -126,6 +126,8 @@ namespace nadena.dev.modular_avatar.core.editor
}
SetGizmoIconEnabled(typeof(ModularAvatarBoneProxy), false);
SetGizmoIconEnabled(typeof(ModularAvatarBoundsOverride), false);
SetGizmoIconEnabled(typeof(ModularAvatarBoundsOverrideBlocker), false);
SetGizmoIconEnabled(typeof(ModularAvatarBlendshapeSync), false);
SetGizmoIconEnabled(typeof(ModularAvatarMenuInstaller), false);
SetGizmoIconEnabled(typeof(ModularAvatarMergeAnimator), false);

View File

@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) 2022 bd_
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using UnityEngine;
// TODO: 言語の設定
namespace nadena.dev.modular_avatar.core
{
[DisallowMultipleComponent]
[AddComponentMenu("Modular Avatar/MA Bounds Override")]
public class ModularAvatarBoundsOverride : AvatarTagComponent
{
public AvatarObjectReference rootBoneTarget;
public Bounds bounds = new Bounds(Vector3.zero, Vector3.one * 2);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf12d50d3180465f82d7131d7efae861
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
/*
* MIT License
*
* Copyright (c) 2022 bd_
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using UnityEngine;
// TODO: 言語の設定
namespace nadena.dev.modular_avatar.core
{
[DisallowMultipleComponent]
[AddComponentMenu("Modular Avatar/MA Bounds Override Blocker")]
public class ModularAvatarBoundsOverrideBlocker : AvatarTagComponent
{ }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29eb7b2eb11840c48051428a9c6c43a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
userData:
assetBundleName:
assetBundleVariant: