Merge branch 'main' into enhance_a-t_pose_conversion

This commit is contained in:
Sayamame-beans 2024-09-28 12:53:59 +09:00 committed by GitHub
commit dcf1151276
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 291 additions and 230 deletions

View File

@ -19,7 +19,7 @@
"dependencies": {}
},
"nadena.dev.ndmf": {
"version": "1.5.0-rc.7"
"version": "1.5.0-rc.8"
}
}
}

View File

@ -42,7 +42,9 @@ namespace nadena.dev.modular_avatar.animation
throw new Exception("Unknown RuntimeAnimatorContoller type " + controller.GetType());
}
return merger.Finish();
var clone = merger.Finish();
ObjectRegistry.RegisterReplacedObject(controller, clone);
return clone;
}
internal static void CloneAllControllers(BuildContext context)

View File

@ -95,7 +95,7 @@ namespace nadena.dev.modular_avatar.core.editor
var t = (ModularAvatarBoneProxy) targets[i];
Undo.RecordObjects(targets, "Set targets");
var xform = ((TempObjRef) objRefs[i]).target;
if (RuntimeUtil.FindAvatarTransformInParents(xform)?.gameObject != parentAvatar) continue;
if (xform != null && RuntimeUtil.FindAvatarTransformInParents(xform)?.gameObject != parentAvatar) continue;
t.target = xform;
}
}
@ -159,4 +159,4 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
}
}
}

View File

@ -35,6 +35,9 @@ namespace nadena.dev.modular_avatar.core.editor
case VisibleHeadAccessoryValidation.ReadyStatus.ParentMarked:
EditorGUILayout.HelpBox(Localization.S("fpvisible.normal"), MessageType.Info);
break;
case VisibleHeadAccessoryValidation.ReadyStatus.NotUnderHead:
EditorGUILayout.HelpBox(Localization.S("fpvisible.NotUnderHead"), MessageType.Warning);
break;
default:
{
var label = "fpvisible." + status;
@ -49,4 +52,4 @@ namespace nadena.dev.modular_avatar.core.editor
Localization.ShowLanguageUI();
}
}
}
}

View File

@ -18,7 +18,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
return (EditorStyles.label?.lineHeight ?? 0) * 3;
}
catch (NullReferenceException e)
catch (NullReferenceException)
{
// This can happen in early initialization...
return 0;

View File

@ -3,7 +3,6 @@
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
#endregion
@ -148,7 +147,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
{
return targetObject?.GetComponent<Renderer>()?.sharedMaterials;
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
return null;
}

View File

@ -25,10 +25,11 @@ namespace nadena.dev.modular_avatar.core.editor
internal static class ParameterIntrospectionCache
{
internal static PropCache<GameObject, ImmutableList<ProvidedParameter>> ProvidedParameterCache = new (GetParametersForObject_miss);
internal static PropCache<GameObject, ImmutableList<ProvidedParameter>> ProvidedParameterCache =
new("GetParametersForObject", GetParametersForObject_miss);
internal static PropCache<GameObject, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping>>
ParameterRemappingCache = new(GetParameterRemappingsAt_miss);
ParameterRemappingCache = new("GetParameterRemappingsAt", GetParameterRemappingsAt_miss);
private static ImmutableList<ProvidedParameter> GetParametersForObject_miss(ComputeContext ctx, GameObject obj)
{

View File

@ -30,7 +30,7 @@ namespace nadena.dev.modular_avatar.core.editor
createInstaller = false;
}
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
// ignore
}

View File

@ -61,7 +61,7 @@ namespace nadena.dev.modular_avatar.core.editor
var grouped = ctx.Observe(target,
t => (t.GroupedBones ?? Array.Empty<GameObject>())
.Select(obj => obj.transform)
.ToHashSet(new ObjectIdentityComparer<Transform>()),
.ToHashSet(),
(x, y) => x.SetEquals(y)
);

View File

@ -64,7 +64,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
.Select(x => mesh.GetBlendShapeName(x))
.ToList();
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
shapeNames = null;
}

View File

@ -100,7 +100,7 @@
"worldfixed.quest": "This component is not compatible with Android builds 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.NotUnderHead": "This component has no effect when not placed under the head bone.\nIf this will be placed under the head bone through Bone Proxy etc., this warning can be ignored.",
"fpvisible.quest": "This component is not compatible with Android builds and will have no effect.",
"fpvisible.InPhysBoneChain": "This object is controlled by a Physics Bone chain and cannot be made visible in first person safely. Select the start of the chain instead.",
"blendshape.mesh": "Mesh",
@ -241,7 +241,7 @@
"setup_outfit.err.no_avatar_descriptor": "No avatar descriptor found in {0}'s parents. Make sure your outfit is placed inside your avatar.",
"setup_outfit.err.no_animator": "Your avatar does not have an Animator component.",
"setup_outfit.err.no_hips": "Your avatar does not have a Hips bone. Setup Outfit only works on humanoid avatars.",
"setup_outfit.err.no_outfit_hips": "Unable to identify the Hips object for the outfit. Searched for objects containing the following names:",
"setup_outfit.err.no_outfit_hips": "Unable to identify the Hips object for the outfit. Searched for objects containing the following names(case-insensitive):",
"move_independently.group-header": "Objects to move together",
"scale_adjuster.scale": "Scale adjustment",
"scale_adjuster.adjust_children": "Adjust position of child objects",

View File

@ -9,7 +9,7 @@
"menuinstall.showcontents": "メニュー内容を表示",
"menuinstall.showcontents.notselected": "メニューが選択されていません",
"menuinstall.devoptions": "プレハブ開発者向け設定",
"menuinstall.menu_icon_too_large": "メニューに設定されているアイコンが256ピクセルより大きすぎます。",
"menuinstall.menu_icon_too_large": "メニューに設定されているアイコンが256x256より大きいです。",
"menuinstall.menu_icon_uncompressed": "メニューに設定されているアイコンが圧縮設定されていません。",
"menuinstall.srcmenu": "インストールされるメニュー",
"params.syncmode.NotSynced": "Animatorのみ",
@ -31,32 +31,32 @@
"merge_parameter.ui.name": "パラメーター名",
"merge_parameter.ui.prefix": "PhysBone 接頭辞",
"merge_parameter.ui.remapTo": "名前を変更",
"merge_parameter.ui.remapTo.tooltip": "ここに新しい名前を入れることで、このパラメーターの名前を変更できます。これで名前かぶりを回避したり、あるいはあえて複数のギミックを連動できます。",
"merge_parameter.ui.remapTo.tooltip": "ここに新しい名前を入れることで、このパラメーターの名前を変更できます。これで名前かぶりを回避したり、複数のギミックを連動させたりすることができます。",
"merge_parameter.ui.remapTo.automatic": "(自動的に設定)",
"merge_parameter.ui.defaultValue": "初期値",
"merge_parameter.ui.defaultValue.tooltip": "アバターがリセット、または最初に着た時にこの値が採用されます。",
"merge_parameter.ui.defaultValue.tooltip": "アバターをリセットした時、または最初に着た時にこの値が採用されます。",
"merge_parameter.ui.saved": "保存する",
"merge_parameter.ui.saved.tooltip": "保存されたパラメーターは、アバター変更やワールド移動保持されます",
"merge_parameter.ui.saved.tooltip": "保存されたパラメーターは、アバター変更やワールド移動をしても保持されます",
"merge_parameter.ui.internalParameter": "自動リネーム",
"merge_parameter.ui.internalParameter.tooltip": "有効にすると、名前かぶりを回避するために自動的に名前を変更します",
"merge_parameter.ui.isPrefix": "PhysBone 接頭辞",
"merge_parameter.ui.syncType": "パラメーター型",
"merge_parameter.ui.synced": "同期する",
"merge_parameter.ui.synced.tooltip": "有効にすると、ネットワーク上同期されます",
"merge_parameter.ui.synced.tooltip": "有効にすると、このパラメーターは同期されます",
"merge_parameter.ui.unregistered_foldout": "未登録パラメーター",
"merge_parameter.ui.add_button": "追加",
"merge_parameter.ui.details": "パラメーターの詳細設定",
"merge_parameter.ui.overrideAnimatorDefaults": "アニメーターの初期値を設定",
"merge_parameter.ui.overrideAnimatorDefaults": "アニメーターの初期値を設定",
"merge_armature.merge_target": "統合先",
"merge_armature.merge_target.tooltip": "このオブジェクトを統合先のアーマチュアに統合します",
"merge_armature.prefix": "接頭辞",
"merge_armature.prefix.tooltip": "マージするボーンに付いている接頭辞",
"merge_armature.prefix.tooltip": "統合されるボーンに付いている接頭辞",
"merge_armature.suffix": "接尾辞",
"merge_armature.suffix.tooltip": "マージするボーンに付いている接尾辞",
"merge_armature.suffix.tooltip": "統合されるボーンに付いている接尾辞",
"merge_armature.locked": "位置を固定",
"merge_armature.locked.tooltip": "このオブジェクトのボーンを統合先のボーンに常に相互的に位置を合わせる。アニメーション制作向け",
"merge_armature.locked.tooltip": "このオブジェクトのボーンと統合先のボーンの位置を常に合わせます。アニメーション作成時に便利です。",
"merge_armature.adjust_names": "ボーン名を統合先に合わせる",
"merge_armature.adjust_names.tooltip": "統合先のボーン名に合わせて、衣装のボーン名を合わせて変更します。統合先アバターに非対応の衣装導入向け機能です。",
"merge_armature.adjust_names.tooltip": "衣装のボーン名をアバターのボーン名に合わせて変更します。アバターに非対応の衣装を導入する時に便利です。",
"merge_armature.mangle_names": "名前かぶりを回避",
"merge_armature.mangle_names.tooltip": "ほかのアセットとの名前かぶりを裂けるため、新規ボーンの名前を自動で変更する",
"path_mode.Relative": "相対的(このオブジェクトからのパスを使用)",
@ -66,12 +66,12 @@
"merge_animator.delete_attached_animator": "付属アニメーターを削除",
"merge_animator.delete_attached_animator.tooltip": "統合後、このオブジェクトについているアニメーターを削除します",
"merge_animator.path_mode": "パスモード",
"merge_animator.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます",
"merge_animator.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトにつけたアニメーターでアニメーションを編集することができます。",
"merge_animator.match_avatar_write_defaults": "アバターのWrite Defaults設定に合わせる",
"merge_animator.match_avatar_write_defaults.tooltip": "アバターの該当アニメーターのWrite Defaults設定に合わせます。アバター側の設定が矛盾する場合は、統合されるアニメーターのWD値がそのまま採用されます。",
"merge_animator.relative_path_root": "相対的パスのルート",
"merge_animator.relative_path_root.tooltip": "相対的パスはこのオブジェクトを基準に解釈されます。指定がない場合は、このコンポーネントがついているオブジェクトを基準とします。",
"merge_animator.layer_priority": "レイヤー優先度",
"merge_animator.layer_priority": "レイヤー統合優先度",
"merge_animator.layer_priority.tooltip": "アニメーターにレイヤーが統合される順番を制御します。低い値から高い値の順に統合されます。マイナスの場合は元々のAvatar Descriptorについているコントローラーより前に統合され、ゼロ以上の場合はそのあとに統合されます。",
"merge_armature.lockmode": "位置追従モード",
"merge_armature.lockmode.not_locked.title": "追従なし",
@ -81,29 +81,29 @@
"merge_armature.lockmode.bidirectional.title": "アバター <=====> オブジェクト (双方向)",
"merge_armature.lockmode.bidirectional.body": "アバターと統合されるアーマチュアは常に同じ位置になります。元のアバターを操作するアニメーションを作る時に便利かもしれません。有効にするためには、統合されるアーマチュアの位置を統合先と同じにしておく必要があります。",
"merge_armature.reset_pos": "位置を元アバターに合わせてリセット",
"merge_armature.reset_pos.info": "このコマンドは、衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入するとき、アバウトに合わせるために便利です。",
"merge_armature.reset_pos.info": "衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入する際、アバウトに位置を合わせるのに便利です。",
"merge_armature.reset_pos.convert_atpose": "Aポーズ/Tポーズを合わせる",
"merge_armature.reset_pos.adjust_rotation": "回転も合わせる",
"merge_armature.reset_pos.adjust_scale": "スケールも合わせる",
"merge_armature.reset_pos.execute": "実行",
"merge_armature.reset_pos.heuristic_scale": "衣装の全体的なスケールをアバターに合わせる",
"merge_armature.reset_pos.heuristic_scale.tooltip": "腕の長さを参考に、衣装全体のスケールをアバターに合わせます。非対応衣装を導入するときは推奨です。",
"merge_armature.reset_pos.heuristic_scale.tooltip": "腕の長さを参考に、衣装全体のスケールをアバターに合わせます。非対応衣装を導入する時にお勧めです。",
"merge_blend_tree.blend_tree": "ブレンドツリー",
"merge_blend_tree.path_mode": "パスモード",
"merge_blend_tree.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます",
"merge_blend_tree.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトにつけたアニメーターでアニメーションを編集することができます。",
"merge_blend_tree.relative_path_root": "相対的パスのルート",
"merge_blend_tree.relative_path_root.tooltip": "相対的パスはこのオブジェクトを基準に解釈されます。指定がない場合は、このコンポーネントがついているオブジェクトを基準とします。",
"worldfixed.quest": "このコンポーネントはアンドロイドビルド非対応のため無効となっています。",
"worldfixed.quest": "このコンポーネントはアンドロイドビルドに非対応であるため、無効となっています。",
"worldfixed.normal": "このオブジェクトはConstraint等でアバターに追従させない限りワールドに固定されます。",
"fpvisible.normal": "このオブジェクトは一人視点で表示されます。",
"fpvisible.NotUnderHead": "このコンポーネントはヘッドボーン外では効果がありません。",
"fpvisible.quest": "このコンポーネントはアンドロイドビルド非対応のため無効となっています。",
"fpvisible.InPhysBoneChain": "このオブジェクトはPhysBoneに制御されているため、一人視点で表示できません。PhysBoneの始点を指定してください。",
"fpvisible.normal": "このオブジェクトは一人視点で表示されます。",
"fpvisible.NotUnderHead": "このコンポーネントは頭ボーンの配下でないと効果がありません。\nBone Proxyなどで頭ボーンの配下に配置される場合は効果あります",
"fpvisible.quest": "このコンポーネントはアンドロイドビルドに非対応であるため、無効となっています。",
"fpvisible.InPhysBoneChain": "このオブジェクトはPhysBoneで制御されているため、一人称視点でうまく表示させることができません。PhysBoneの始点を指定してください。",
"blendshape.mesh": "メッシュ",
"blendshape.source": "元メッシュのブレンドシェプ",
"blendshape.target": "このメッシュのブレンドシェプ",
"hint.not_in_avatar": "このコンポーネントが正しく動作するには、アバター内に配置する必要があります。",
"boneproxy.err.MovingTarget": "他のモジュラーアバターコンポーネントで移動されるオブジェクト指定できません。",
"blendshape.source": "元メッシュのブレンドシェプ",
"blendshape.target": "このメッシュのブレンドシェプ",
"hint.not_in_avatar": "このコンポーネントを正しく動作させるには、アバター内に配置する必要があります。",
"boneproxy.err.MovingTarget": "他のモジュラーアバターコンポーネントで移動されるオブジェクト指定できません。",
"boneproxy.err.NotInAvatar": "アバター内のオブジェクトを指定してください。",
"boneproxy.attachment": "配置モード",
"boneproxy.attachment.AsChildAtRoot": "子として・ルートに配置",
@ -112,28 +112,28 @@
"boneproxy.attachment.AsChildKeepRotation": "子として・ワールド向きを維持",
"mesh_settings.header_probe_anchor": "Anchor Override 設定",
"mesh_settings.inherit_probe_anchor": "設定モード",
"mesh_settings.probe_anchor": "アンカーオーバーライド",
"mesh_settings.probe_anchor.tooltip": "このオブジェクトとその子のレンダラーのAnchorOverrideを設定します。",
"mesh_settings.probe_anchor": "Anchor Override",
"mesh_settings.probe_anchor.tooltip": "このオブジェクトとその子のレンダラーのAnchor Overrideを設定します。",
"mesh_settings.header_bounds": "Bounds 設定",
"mesh_settings.inherit_bounds": "設定モード",
"mesh_settings.root_bone": "ルートボーン",
"mesh_settings.root_bone.tooltip": "このオブジェクトとその子のメッシュで設定されるルートボーン。メッシュのバウンズを計算するための参照点として使用されます。",
"mesh_settings.bounds": "バウンズ",
"mesh_settings.root_bone.tooltip": "このオブジェクトとその子のメッシュで設定されるルートボーン。メッシュのBoundsを計算する際の基準として使用されます。",
"mesh_settings.bounds": "Bounds",
"mesh_settings.bounds.tooltip": "このオブジェクトとその子のメッシュで設定されるバウンズ。画面外のメッシュのレンダリングを省略するかどうかを決定するために使用されます。",
"mesh_settings.inherit_mode.Inherit": "継承",
"mesh_settings.inherit_mode.Set": "設定",
"mesh_settings.inherit_mode.DontSet": "設定しない(メッシュ本体の設定のまま)",
"mesh_settings.inherit_mode.SetOrInherit": "親が指定されてる時は継承、または設定",
"mesh_settings.inherit_mode.SetOrInherit": "親で指定されている時は継承、それ以外では設定",
"pb_blocker.help": "このオブジェクトは親のPhysBoneから影響を受けなくなります。",
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでもだめでしたら、Modular Avatarにも最新版が出てないかチェックしてください。",
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでも駄目な場合、Modular Avatarにも最新版が出ていないか確認してみてください。",
"error.stack_trace": "スタックトレース(バグを報告する時は必ず添付してください!)",
"error.merge_armature.circular_dependency": "[MA-0001] Merge Armatureに循環参照があります",
"error.merge_armature.circular_dependency:description": "Merge Armature コンポーネントは、自分自身、または自分の子をマージターゲットとして参照しています。",
"error.merge_armature.circular_dependency:hint": "Merge Armature は通常、ターゲットフィールドにアバター本体の Armature オブジェクトを指定する必要があります。衣装自体を指定しないでください。",
"error.merge_armature.physbone_on_humanoid_bone": "[MA-0002] ヒューマイドボーンにPhysBoneコンポーネントを検出",
"error.merge_armature.physbone_on_humanoid_bone:hint": "一部のヒューマイドボーンは、PhysBonesによって制御されています。 マージ対象の対応するヒューマイドボーンとは位置が異なるため、適切にマージすることはできません。マージするにはヒューマイドボーンのPhysBonesを取り除く必要があります。",
"error.merge_armature.circular_dependency:description": "Merge Armatureコンポーネントの統合先として、自分自身、または自分の子が参照されています。",
"error.merge_armature.circular_dependency:hint": "通常、Merge Armatureは統合先としてアバター本体のArmatureオブジェクトを指定する必要があります。衣装自体を指定しないように注意してください。",
"error.merge_armature.physbone_on_humanoid_bone": "[MA-0002] HumanoidボーンにPhysBoneコンポーネントがついています。",
"error.merge_armature.physbone_on_humanoid_bone:hint": "一部のHumanoidボーンがPhysBoneによって制御されています。対応する統合先のHumanoidボーンと位置が異なるため、適切に統合することができません。統合するにはHumanoidボーンについているPhysBoneを取り除く必要があります。",
"error.merge_blend_tree.missing_tree": "[MA-0009] ブレンドツリーが指定されていません",
"error.merge_blend_tree.missing_tree:hint": "Merge Blend Treeが動作するには、どのブレンドツリーを統合するか指定する必要があります。「ブレンドツリー」欄を設定してみてください。",
"error.merge_blend_tree.missing_tree:hint": "Merge Blend Treeが動作するには、どのブレンドツリーを統合するか指定する必要があります。「ブレンドツリー」欄を設定しているかご確認ください。",
"error.internal_error": "[MA-9999] 内部エラーが発生しました:{0}\n以下のオブジェクトの処理中に発生しました",
"error.merge_animator.param_type_mismatch": "[MA-0003] パラメータの型が競合しています",
"error.merge_animator.param_type_mismatch:description": "パラメータ {0} には複数の型が指定されています: {1} != {2}",
@ -143,37 +143,37 @@
"error.rename_params.type_conflict:description": "パラメータ {0} には複数の型が指定されています: {1} != {2}",
"error.rename_params.default_value_conflict": "[MA-0007] 初期値の競合",
"error.rename_params.default_value_conflict:description": "パラメータ {0} には複数の初期値が指定されています: {1} != {2}",
"error.rename_params.default_value_conflict:hint": "予測不可能な動作を避けるために、MAパラメータコンポーネントのデフォルト値フィールドはパラメーター名毎に一個のコンポーネント以外空白のままにしてください。 複数の値が存在する場合、Modular Avatarは階層順に指定された最初のデフォルト値を選択します。",
"error.replace_object.null_target": "[MA-0008] ターゲットが指定されていません",
"error.replace_object.null_target:hint": "Replace Object には置き換えるべきターゲットオブジェクトが必要です。設定してみてください。",
"validation.blendshape_sync.no_local_renderer": "[MA-1000] このオブジェクトにはSkinnedMeshRendererがありません。",
"validation.blendshape_sync.no_local_renderer:hint": "Blendshape Syncは同じGameObject上のSkinned Mesh Rendererに作用します。正しいオブジェクトに追加しましたか?",
"validation.blendshape_sync.no_local_mesh": "[MA-1001] このオブジェクトにはSkinnedMeshRendererがありますが、メッシュがありません。",
"error.rename_params.default_value_conflict:hint": "予測不可能な動作を避けるため、MA Parametersコンポーネントの初期値フィールドはパラメーター名毎に1つだけしか指定しないようにし、他のコンポーネントでは空白のままにしてください。複数の値が存在する場合、Modular Avatarは階層順で最初に指定された初期値を採用します。",
"error.replace_object.null_target": "[MA-0008] 置き換え先が指定されていません",
"error.replace_object.null_target:hint": "Replace Objectは置き換え先のオブジェクトを指定する必要があります。",
"validation.blendshape_sync.no_local_renderer": "[MA-1000] このオブジェクトにはSkinned Mesh Rendererがありません。",
"validation.blendshape_sync.no_local_renderer:hint": "Blendshape Syncは同じGameObject上のSkinned Mesh Rendererに作用します。コンポーネントが正しいオブジェクトに追加されているか確認してください。",
"validation.blendshape_sync.no_local_mesh": "[MA-1001] このオブジェクトにはSkinned Mesh Rendererがありますが、メッシュがありません。",
"validation.blendshape_sync.no_local_mesh:hint": "このオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.blendshape_sync.no_bindings": "[MA-1002] このBlendshapeSyncにはバインドが設定されていません。",
"validation.blendshape_sync.no_bindings": "[MA-1002] このBlendshape Syncにはバインドが設定されていません。",
"validation.blendshape_sync.no_bindings:hint": "Blendshape Syncは、どのブレンドシェイプを同期するかを知る必要があります。追加するには、「+」ボタンをクリックしてください。",
"validation.blendshape_sync.missing_local_shape": "[MA-1003] 同期先のメッシュに該当するブレンドシェープ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:description": "ローカルブレンドシェイプがありません: {0}",
"validation.blendshape_sync.missing_local_shape:hint": "ターゲットオブジェクトから値を受け取るように設定されたブレンドシェイプがありません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.missing_target_shape": "[MA-1004] 同期先メッシュにブレンドシェープ「{0}」が見つかりません",
"validation.blendshape_sync.missing_target_shape:description": "ターゲットブレンドシェイプがありません: {0}",
"validation.blendshape_sync.missing_target_shape:hint": "ローカルオブジェクトに値を「送る」ように設定されたブレンドシェイプがありません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.no_target": "[MA-1005] このBlendshapeSyncには同期元が設定されていないバインドがあります。",
"validation.blendshape_sync.no_target:hint": "どのオブジェクトからBlendshapeを同期するかを教える必要があります。メッシュを設定してみてください。",
"validation.blendshape_sync.missing_target_renderer": "[MA-1006] 同期元のオブジェクトにSkinnedMeshRendererがありません。",
"validation.blendshape_sync.missing_target_renderer:hint": "Blendshape Syncは、対象オブジェクトのSkinned Mesh Rendererからブレンド形状の値を受け取ります。正しいオブジェクトに追加しましたか",
"validation.blendshape_sync.missing_target_mesh": "[MA-1007] 同期元のオブジェクトにはSkinnedMeshRendererがありますが、メッシュがありません。",
"validation.blendshape_sync.missing_target_mesh:hint": "ターゲットオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.bone_proxy.no_target": "[MA-1100] ターゲットオブジェクトが未設定もしくは存在しません。",
"validation.bone_proxy.no_target:hint": "ボーンプロキシがどのオブジェクトに追尾するかを知る必要があります。追尾すべきオブジェクトをターゲットフィールドに設定してみてください。",
"validation.blendshape_sync.missing_local_shape": "[MA-1003] 同期先のメッシュにブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:description": "ブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:hint": "値を受け取るように設定されたブレンドシェイプが同期先のメッシュに存在しません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.missing_target_shape": "[MA-1004] 同期元のメッシュにブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_target_shape:description": "ブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_target_shape:hint": "値を「送る」ように設定されたブレンドシェイプが同期元のメッシュに存在しません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.no_target": "[MA-1005] このBlendshape Syncには同期元が設定されていないバインドがあります。",
"validation.blendshape_sync.no_target:hint": "どのオブジェクトからBlendshapeを同期するか指定する必要があります。メッシュを設定してください。",
"validation.blendshape_sync.missing_target_renderer": "[MA-1006] 同期元のオブジェクトにSkinned Mesh Rendererがありません。",
"validation.blendshape_sync.missing_target_renderer:hint": "Blendshape Syncは、同期元のSkinned Mesh Rendererからブレンドシェイプの値を受け取ります。コンポーネントが正しいオブジェクトに追加されているか確認してください。",
"validation.blendshape_sync.missing_target_mesh": "[MA-1007] 同期元のオブジェクトにはSkinned Mesh Rendererがありますが、メッシュがありません。",
"validation.blendshape_sync.missing_target_mesh:hint": "同期元のオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.bone_proxy.no_target": "[MA-1100] ターゲットオブジェクトが未設定であるか、存在しません。",
"validation.bone_proxy.no_target:hint": "Bone Proxyはどのオブジェクトに追従させるかを指定する必要があります。追従させたい対象のオブジェクトをターゲット欄に設定してみてください。",
"validation.menu_installer.no_menu": "[MA-1200] インストールするメニューがありません。",
"validation.menu_installer.no_menu:hint": "Menu Installer は、どのメニューをインストールするべきか設定する必要があります。 「Prefab開発者向けオプション」内の「インストールするメニュー」フィールドを設定するか、MA Menu Itemコンポーネントを追加してみてください。",
"validation.merge_animator.no_animator": "[MA-1300] Animator Controllerが設定されていません",
"validation.merge_animator.no_animator:hint": "Animatorをマージするには、どのアニメーターを統合するか設定する必要があります。「統合されるアニメーター」を設定してみてください。",
"validation.merge_armature.no_target": "[MA-1400] 統合先が未設定もしくは存在しません。",
"validation.merge_animator.no_animator": "[MA-1300] Animator Controllerが未設定であるか、存在しません。",
"validation.merge_animator.no_animator:hint": "どのアニメーターを統合するか設定する必要があります。「統合されるアニメーター」を設定してみてください。",
"validation.merge_armature.no_target": "[MA-1400] 統合先が未設定であるか、存在しません。",
"validation.merge_armature.no_target:hint": "Merge Armatureはどこに統合するか設定する必要があります。「統合先」を設定してみてください。",
"validation.merge_armature.target_is_child": "[MA-1500] 統合先をこのオブジェクトの子にすることができません",
"validation.merge_armature.target_is_child:hint": "Merge Armature は自身の子に統合できません。「統合先」を別のオブジェクトに設定してみてください。",
"validation.merge_armature.target_is_child": "[MA-1500] 統合先として自身の子を指定することはできません。",
"validation.merge_armature.target_is_child:hint": "Merge Armatureで自身の子に統合することはできません。「統合先」を別のオブジェクトに設定してみてください。",
"submenu_source.Children": "子オブジェクトから生成",
"submenu_source.MenuAsset": "Expressions Menu アセットを指定",
"menuitem.showcontents": "メニュー内容を表示",
@ -183,7 +183,7 @@
"menuitem.prop.type": "タイプ",
"menuitem.prop.type.tooltip": "この項目の種別",
"menuitem.prop.value": "パラメーター値",
"menuitem.prop.value.tooltip": "この項目が操作されたとき、パラメーター設定される値",
"menuitem.prop.value.tooltip": "この項目が操作されたとき、パラメーター設定される値",
"menuitem.prop.automatic_value": "自動",
"menuitem.prop.automatic_value.tooltip": "かぶらない値を自動的に割り振る",
"menuitem.prop.parameter": "パラメーター名",
@ -191,19 +191,19 @@
"menuitem.prop.submenu_asset": "サブメニューアセット",
"menuitem.prop.submenu_asset.tooltip": "サブメニューとして引用するアセット",
"menuitem.prop.submenu_source": "サブメニュー引用元",
"menuitem.prop.submenu_source.tooltip": "このサブメニューの内容をどこから引用するべきか指定",
"menuitem.prop.submenu_source.tooltip": "このサブメニューの内容をどこから引用するべきか指定します。",
"menuitem.prop.source_override": "引用元オブジェクト",
"menuitem.prop.source_override.tooltip": "指定した場合、指定したオブジェクトの子をメニューの内容として指定します。指定されてない場合はこのオブジェクト直下の子を使用します。",
"menuitem.prop.source_override.tooltip": "指定した場合、指定したオブジェクトの子をメニューの内容として指定します。指定されてない場合はこのオブジェクト直下の子を使用します。",
"menuitem.prop.is_default": "初期設定にする",
"menuitem.prop.is_default.tooltip": "ONの場合、アバター初期化の際にこのメニューアイテムを選択します",
"menuitem.prop.is_default.tooltip": "有効になっていると、アバター初期化の際にこのメニューアイテムを選択した状態にします",
"menuitem.prop.is_saved": "保存する",
"menuitem.prop.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動するときこの設定が保持されます。",
"menuitem.prop.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動をしてもこの設定が保持されます。",
"menuitem.prop.is_synced": "同期する",
"menuitem.prop.is_synced.tooltip": "有効の場合はほかのプレイヤーに同期されます。",
"menuitem.param.rotation": "回転パラメーター名",
"menuitem.param.rotation.tooltip": "このメニューアイテムの回転に連動するべきパラメーター",
"menuitem.prop.is_synced.tooltip": "有効になっていると、メニューがほかのプレイヤーに同期されます。",
"menuitem.param.rotation": "ラジアルメニュー用パラメーター名",
"menuitem.param.rotation.tooltip": "このラジアルメニューの操作と連動するべきパラメーター",
"menuitem.param.horizontal": "横パラメーター名",
"menuitem.param.horizontal.tooltip": "操作に連動するパラメーター名",
"menuitem.param.horizontal.tooltip": "左右操作に連動するパラメーター名",
"menuitem.param.vertical": "縦パラメーター名",
"menuitem.param.vertical.tooltip": "上下操作に連動するパラメーター名",
"menuitem.label.control_labels_and_params": "表示名・パラメーター",
@ -219,29 +219,29 @@
"control_group.foldout.actions": "アクション",
"control_group.foldout.menu_items": "関連付けされたメニューアイテム",
"control_group.is_saved": "保存する",
"control_group.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動するときこの設定が保持されます。",
"control_group.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動をしてもこの設定が保持されます。",
"control_group.is_synced": "同期する",
"control_group.is_synced.tooltip": "有効の場合はほかのプレイヤーに同期されます。",
"control_group.is_synced.tooltip": "有効になっていると、メニューがほかのプレイヤーに同期されます。",
"control_group.default_value": "初期値",
"control_group.default_value.unset": "(どれも選択されない)",
"animation_gen.duplicate_binding": "別々のコントロールグループから、同じパラメーターが操作されています。パラメーター:{0}",
"animation_gen.multiple_defaults": "同じコントロールグループに初期設定に指定されたメニューアイテムが複数あります。",
"animation_gen.multiple_defaults": "同じコントロールグループに初期設定にするとして指定されたメニューアイテムが複数あります。",
"menuitem.misc.add_item": "メニューアイテムを追加",
"replace_object.target_object": "上書き先",
"setup_outfit.err.header.notarget": "Setup outfit の処理が失敗しました",
"setup_outfit.err.header": "Setup outfit が「{0}」を処理中に失敗しました。",
"replace_object.target_object": "置き換え先",
"setup_outfit.err.header.notarget": "Setup Outfit の処理に失敗しました",
"setup_outfit.err.header": "Setup Outfit が「{0}」を処理中に失敗しました。",
"setup_outfit.err.unknown": "原因不明のエラーが発生しました。",
"setup_outfit.err.no_selection": "オブジェクトが選択されていません。",
"setup_outfit.err.run_on_avatar_itself": "Setup Outfitはアバター自体ではなく、衣装のほうで実行してください。\n\nキメラアバターを作る場合は、中のほうのAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.multiple_avatar_descriptors": "「{0}」とその親に、複数のavatar descriptorを発見しました。\n\nキメラアバターを作る場合は、中のほうのAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.no_avatar_descriptor": "「{0}」の親に、avatar descriptorが見つかりませんでした。衣装のオブジェクトをアバターの中に配置してください。",
"setup_outfit.err.run_on_avatar_itself": "Setup Outfitはアバター自体ではなく、衣装の方で実行してください。\n\nキメラアバターを作る場合は、中の方のAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.multiple_avatar_descriptors": "「{0}」とその親で複数のAvatar Descriptorを発見しました。\n\nキメラアバターを作る場合は、中の方のAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.no_avatar_descriptor": "「{0}」の親にAvatar Descriptorが見つかりませんでした。衣装のオブジェクトをアバターの中に配置してください。",
"setup_outfit.err.no_animator": "アバターにAnimatorコンポーネントがありません。",
"setup_outfit.err.no_hips": "アバターにHipsボーンがありません。なお、Setup Outfitはヒューマノイドアバター以外には対応していません。",
"setup_outfit.err.no_outfit_hips": "衣装のHipsボーンを発見できませんでした。以下の名前を含むボーンを探しました:",
"move_independently.group-header": "一緒に動かすオブジェクト",
"setup_outfit.err.no_hips": "アバターにHipsボーンがありません。なお、Setup OutfitはHumanoidアバター以外には対応していません。",
"setup_outfit.err.no_outfit_hips": "衣装のHipsボーンを発見できませんでした。アクセサリー等に対してSetup Outfitを試みた場合は、代わりにBone Proxyコンポーネントを使って配置してみてください。\n以下の名前を含むボーンを(大文字・小文字を区別つけずに)探しました:",
"move_independently.group-header": "同時に動かすオブジェクト",
"scale_adjuster.scale": "Scale調整値",
"scale_adjuster.adjust_children": "子オブジェクトの位置を調整",
"world_fixed_object.err.unsupported_platform": "World Fixed Objectこのプラットフォームに対応していません",
"world_fixed_object.err.unsupported_platform": "World Fixed Objectこのプラットフォームに対応していません",
"world_fixed_object.err.unsupported_platform:description": "World Fixed ObjectはAndroid向けビルドには対応していないため、動作しません。",
"ma_info.param_usage_ui.header": "Expressions Parameter 使用状況",
"ma_info.param_usage_ui.other_objects": "このアバター内の他のオブジェクト",
@ -251,8 +251,8 @@
"reactive_object.inverse": "条件を反転",
"reactive_object.material-setter.set-to": "変更先のマテリアル ",
"menuitem.misc.add_toggle": "トグルを追加",
"ro_sim.open_debugger_button": "リアクションデバッグツールを開く",
"ro_sim.window.title": "MA リアクションデバッグツール",
"ro_sim.open_debugger_button": "Reaction デバッガーを開く",
"ro_sim.window.title": "MA Reaction デバッガー",
"ro_sim.header.inspecting": "表示中のオブジェクト",
"ro_sim.header.clear_overrides": "すべてのオーバーライドを解除",
"ro_sim.header.object_state": "オブジェクトのアクティブ状態",
@ -261,16 +261,17 @@
"ro_sim.header.override_gameobject_state": "GameObject のアクティブ状態をオーバーライド",
"ro_sim.header.override_menuitem_state": "MenuItem の選択状態をオーバーライト",
"ro_sim.affected_by.title": "以下のルールに影響されています",
"ro_sim.effect_group.controls_obj_state": "オブジェクトのアクティブ状態を設定する➡",
"ro_sim.effect_group.component": "Reactive Component",
"ro_sim.effect_group.controls_obj_state": "オブジェクトのアクティブ状態を次に設定する➡",
"ro_sim.effect_group.target_component": "コンポーネント",
"ro_sim.effect_group.target_component.tooltip": "Reactive Componentに影響されるコンポーネント",
"ro_sim.effect_group.property": "プロパティ",
"ro_sim.effect_group.property.tooltip": "設定されるプロパティ",
"ro_sim.effect_group.value": "値",
"ro_sim.effect_group.value.tooltip": "上記 Reactive Component が活性状態の時に設定される値",
"ro_sim.effect_group.value.tooltip": "上記の Reactive Component がアクティブな時に設定される値",
"ro_sim.effect_group.material": "マテリアル",
"ro_sim.effect_group.material.tooltip": "上記 Reactive Component が活性状態の時に設定されるマテリアル",
"ro_sim.effect_group.rule_inverted": "このルールの条件反転されています",
"ro_sim.effect_group.material.tooltip": "上記の Reactive Component がアクティブな時に設定されるマテリアル",
"ro_sim.effect_group.rule_inverted": "このルールの条件反転されています",
"ro_sim.effect_group.rule_inverted.tooltip": "このルールは、いずれかの条件が満たされていない場合に適用されます",
"ro_sim.effect_group.conditions": "条件"
}

View File

@ -96,7 +96,6 @@
"worldfixed.quest": "이 컴포넌트는 안드로이드 빌드를 대응하지 않습니다.",
"worldfixed.normal": "이 오브젝트는 Constraint 을 사용하여 아바타에 고정하지 않는 이상 월드축에 고정됩니다.",
"fpvisible.normal": "이 오브젝트는 일인칭 시점에서 보일 것입니다.",
"fpvisible.NotUnderHead": "Head 본(Bone) 내의 오브젝트가 아닌 경우 작동하지 않습니다.",
"fpvisible.quest": "이 컴포넌트는 오큘러스 퀘스트 단독 버전과 호환되지 않으며 영향을 미치지 않습니다.",
"fpvisible.InPhysBoneChain": "이 객체는 Physics Bone 체인에 의해 컨트롤되므로 체인의 세부적인 선택이 불가능합니다. 대신 체인의 시작 부분을 선택하세요.",
"blendshape.mesh": "메시",
@ -144,4 +143,4 @@
"control_group.is_synced": "동기화됨",
"setup_outfit.err.unknown": "알 수 없는 오류",
"ma_info.param_usage_ui.other_objects": "이 아바타안의 다른 오브젝트들"
}
}

View File

@ -94,7 +94,6 @@
"worldfixed.quest": "此组件未生效,因为它与 Oculus Quest 不兼容。",
"worldfixed.normal": "当前对象将会固定于世界,除非您使用约束将它绑定在 Avatar 内。",
"fpvisible.normal": "当前对象将在第一人称视角中可见。",
"fpvisible.NotUnderHead": "此组件未生效,因为它需要放置在 Head 骨骼下。",
"fpvisible.quest": "此组件未生效,因为它与 Oculus Quest 不兼容。",
"fpvisible.InPhysBoneChain": "当前对象由 PhysicsBone 控制,可能无法在第一人称视角中可见;请指定 PhysicsBone 链的起点。",
"blendshape.mesh": "网格",
@ -227,7 +226,6 @@
"setup_outfit.err.no_avatar_descriptor": "在 {0} 的父级中找不到 VRC Avatar Descriptor。请确保您的服装放置在 Avatar 内。",
"setup_outfit.err.no_animator": "您的 Avatar 没有动画控制器 (Animator) 组件。",
"setup_outfit.err.no_hips": "您的 Avatar 没有 Hips 骨骼。Setup Outfit 只能用于 humanoid Avatars。",
"setup_outfit.err.no_outfit_hips": "无法识别服装的 Hips已搜索包含以下名称的对象",
"move_independently.group-header": "要一起移动的对象",
"scale_adjuster.scale": "调整比例",
"scale_adjuster.adjust_children": "调整子级的位置",
@ -237,4 +235,4 @@
"ma_info.param_usage_ui.other_objects": "此 Avatar 上的其他对象",
"ma_info.param_usage_ui.free_space": "未使用的参数空间 ({0} bits)",
"ma_info.param_usage_ui.bits_template": "{0} ({1} bits)"
}
}

View File

@ -99,7 +99,6 @@
"worldfixed.quest": "此元件未生效,因為它與 Android 環境不相容。",
"worldfixed.normal": "當前物件將會固定於世界,除非你使用約束將它綁在 Avatar 內。",
"fpvisible.normal": "當前物件將在第一人稱視角中可見。",
"fpvisible.NotUnderHead": "此元件未生效,因為它需要放置在 Head 骨骼下。",
"fpvisible.quest": "此元件未生效,因為它與 Android 環境不相容。",
"fpvisible.InPhysBoneChain": "當前物件由 Physics Bone 控制,可能無法在第一人稱視角中可見;請指定 Physics Bone 鏈的起點。",
"blendshape.mesh": "網格",
@ -239,7 +238,6 @@
"setup_outfit.err.no_avatar_descriptor": "在 {0} 的父級中找不到 VRC Avatar Descriptor。請確保你的服裝放置在 Avatar 裡。",
"setup_outfit.err.no_animator": "你的 Avatar 沒有 Animator 元件。",
"setup_outfit.err.no_hips": "你的 Avatar 沒有 Hips 骨骼。Setup Outfit 只能用於 humanoid Avatars。",
"setup_outfit.err.no_outfit_hips": "識別不到服裝的 Hips 骨骼,已搜尋含有以下名稱的骨骼物件:",
"move_independently.group-header": "要一起移動的物件",
"scale_adjuster.scale": "調整比例",
"scale_adjuster.adjust_children": "調整子級的位置",
@ -275,4 +273,4 @@
"ro_sim.effect_group.rule_inverted": "規則的條件已反轉",
"ro_sim.effect_group.rule_inverted.tooltip": "這條規則將在未達成其任一條件時套用。",
"ro_sim.effect_group.conditions": "條件"
}
}

View File

@ -7,6 +7,7 @@ using System.Linq;
using JetBrains.Annotations;
using nadena.dev.modular_avatar.core.menu;
using nadena.dev.modular_avatar.editor.ErrorReporting;
using nadena.dev.ndmf;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
@ -102,7 +103,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
PushControl(control);
}
if (_menuToInstallerMap.TryGetValue(expMenu, out var installers))
if (_menuToInstallerMap.TryGetValue(ObjectRegistry.GetReference(expMenu), out var installers))
{
foreach (var installer in installers)
{
@ -311,7 +312,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
// initial validation
if (installer.menuToAppend == null && installer.GetComponent<MenuSource>() == null) return;
var target = installer.installTargetMenu ? (object) installer.installTargetMenu : RootMenuKey;
var target = installer.installTargetMenu ? (object) ObjectRegistry.GetReference(installer.installTargetMenu) : RootMenuKey;
if (!_targetMenuToInstaller.TryGetValue(target, out var targets))
{
targets = new List<ModularAvatarMenuInstaller>();
@ -366,7 +367,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
}
// Some menu installers may be bound to the root menu _asset_ directly.
if (menuToInstallerFiltered.TryGetValue(menu, out var installers))
if (menuToInstallerFiltered.TryGetValue(ObjectRegistry.GetReference(menu), out var installers))
{
foreach (var installer in installers)
{

View File

@ -236,8 +236,10 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
private bool? ProbeWriteDefaults(AnimatorController controller)
internal static bool? ProbeWriteDefaults(AnimatorController controller)
{
if (controller == null) return null;
bool hasWDOn = false;
bool hasWDOff = false;

View File

@ -45,7 +45,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
var converters = context.AvatarRootObject.GetComponentsInChildren<ModularAvatarConvertConstraints>(true)
.Select(c => c.gameObject)
.ToHashSet(new ObjectIdentityComparer<GameObject>());
.ToHashSet();
if (converters.Count == 0) return;
var constraintGameObjects = context.AvatarRootObject.GetComponentsInChildren<IConstraint>(true)

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
#if MA_VRCSDK3_AVATARS
using VRC.SDK3.Dynamics.PhysBone.Components;
#endif
@ -123,7 +123,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
}
catch (MissingComponentException _)
catch (MissingComponentException)
{
// No animator? weird. Move on.
}
@ -194,7 +194,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (!referencedGameObjects.Contains(go))
{
UnityEngine.Object.DestroyImmediate(go);
Object.DestroyImmediate(go);
}
}
}

View File

@ -39,6 +39,7 @@ namespace nadena.dev.modular_avatar.core.editor
ParameterNamespace.Animator,
_component, PluginDefinition.Instance, _component.AnimatorControllerParameterType)
{
ExpandTypeOnConflict = true,
WantSynced = _component.isSynced,
IsHidden = hidden,
DefaultValue = _component.isDefault ? _component.Control.value : null

View File

@ -109,7 +109,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
return uxml != null && EditorStyles.label != null;
}
catch (NullReferenceException _)
catch (NullReferenceException)
{
return false;
}

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using nadena.dev.ndmf.preview;
using UnityEngine;
namespace nadena.dev.modular_avatar.core.editor
{
@ -13,21 +12,25 @@ namespace nadena.dev.modular_avatar.core.editor
public PropCache<Key, Value> Owner;
public Key Key;
public Value Value;
public string DebugName;
}
private readonly string _debugName;
private readonly Func<ComputeContext, Key, Value> _operator;
private readonly Func<Value, Value, bool> _equalityComparer;
private readonly Dictionary<Key, CacheEntry> _cache = new();
public PropCache(Func<ComputeContext, Key, Value> operatorFunc, Func<Value, Value, bool> equalityComparer = null)
public PropCache(string debugName, Func<ComputeContext, Key, Value> operatorFunc,
Func<Value, Value, bool> equalityComparer = null)
{
_debugName = debugName;
_operator = operatorFunc;
_equalityComparer = equalityComparer;
}
private static void InvalidateEntry(CacheEntry entry)
{
var newGenContext = new ComputeContext("PropCache for key " + entry.Key);
var newGenContext = new ComputeContext("PropCache/" + entry.DebugName + " key " + entry.Key);
var newValue = entry.Owner._operator(newGenContext, entry.Key);
if (entry.Owner._equalityComparer != null && entry.Owner._equalityComparer(entry.Value, newValue))
{
@ -44,14 +47,15 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (!_cache.TryGetValue(key, out var entry) || entry.GenerateContext.IsInvalidated)
{
var subContext = new ComputeContext("PropCache for key " + key);
var subContext = new ComputeContext("PropCache/" + _debugName + " key " + key);
entry = new CacheEntry
{
GenerateContext = subContext,
ObserverContext = new ComputeContext("Observer for PropCache for key " + key),
Owner = this,
Key = key,
Value = _operator(subContext, key)
Value = _operator(subContext, key),
DebugName = _debugName
};
_cache[key] = entry;

View File

@ -82,7 +82,7 @@ namespace nadena.dev.modular_avatar.core.editor
Parameter = GetActiveSelfProxy(cursor.gameObject),
DebugName = cursor.gameObject.name,
IsConstant = false,
InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
InitialValue = _computeContext.Observe(cursor.gameObject, go => go.activeSelf) ? 1.0f : 0.0f,
ParameterValueLo = 0.5f,
ParameterValueHi = float.PositiveInfinity,
ReferenceObject = cursor.gameObject,
@ -228,7 +228,8 @@ namespace nadena.dev.modular_avatar.core.editor
if (!objectGroups.TryGetValue(key, out var group))
{
group = new AnimatedProperty(key, target.activeSelf ? 1 : 0);
var active = _computeContext.Observe(target, t => t.activeSelf);
group = new AnimatedProperty(key, active ? 1 : 0);
objectGroups[key] = group;
}

View File

@ -67,7 +67,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (_analysisCache == null)
{
_analysisCache = new PropCache<GameObject, AnalysisResult>((ctx, root) =>
_analysisCache = new PropCache<GameObject, AnalysisResult>("ROAnalyzer", (ctx, root) =>
{
var analysis = new ReactiveObjectAnalyzer(ctx);
analysis.ForcePropertyOverrides = ctx.Observe(ROSimulator.PropertyOverrides, a=>a, (a,b) => false)
@ -157,9 +157,7 @@ namespace nadena.dev.modular_avatar.core.editor
if (condition.ReferenceObject != null && !toggledObjects.Contains(condition.ReferenceObject))
condition.IsConstant = asc.AnimationDatabase.ClipsForPath(asc.PathMappings.GetObjectIdentifier(condition.ReferenceObject)).IsEmpty;
var i = 0;
// Remove redundant active conditions.
int retain = 0;
actionGroup.ControllingConditions.RemoveAll(c => c.IsConstant && c.InitiallyActive);
}

View File

@ -25,6 +25,7 @@ namespace nadena.dev.modular_avatar.core.editor
private HashSet<string> activeProps = new();
private AnimationClip _initialStateClip;
private bool _writeDefaults;
public ReactiveObjectPass(ndmf.BuildContext context)
{
@ -33,6 +34,10 @@ namespace nadena.dev.modular_avatar.core.editor
internal void Execute()
{
// Having a WD OFF layer after WD ON layers can break WD. We match the behavior of the existing states,
// and if mixed, use WD ON to maximize compatibility.
_writeDefaults = MergeAnimatorProcessor.ProbeWriteDefaults(FindFxController().animatorController as AnimatorController) ?? true;
var analysis = new ReactiveObjectAnalyzer(context).Analyze(context.AvatarRootObject);
var shapes = analysis.Shapes;
@ -277,7 +282,7 @@ namespace nadena.dev.modular_avatar.core.editor
var initial = new AnimationClip();
var initialState = new AnimatorState();
initialState.motion = initial;
initialState.writeDefaultValues = false;
initialState.writeDefaultValues = _writeDefaults;
initialState.name = "<default>";
asm.defaultState = initialState;
@ -295,7 +300,8 @@ namespace nadena.dev.modular_avatar.core.editor
var transitionBuffer = new List<(AnimatorState, List<AnimatorStateTransition>)>();
var entryTransitions = new List<AnimatorTransition>();
transitionBuffer.Add((initialState, new List<AnimatorStateTransition>()));
var initialStateTransitionList = new List<AnimatorStateTransition>();
transitionBuffer.Add((initialState, initialStateTransitionList));
foreach (var group in info.actionGroups.Skip(lastConstant))
{
@ -315,33 +321,30 @@ namespace nadena.dev.modular_avatar.core.editor
var conditions = GetTransitionConditions(asc, group);
foreach (var (st, transitions) in transitionBuffer)
if (!group.Inverted)
{
if (!group.Inverted)
var transition = new AnimatorStateTransition
{
var transition = new AnimatorStateTransition
isExit = true,
hasExitTime = false,
duration = 0,
hasFixedDuration = true,
conditions = (AnimatorCondition[])conditions.Clone()
};
initialStateTransitionList.Add(transition);
}
else
{
foreach (var cond in conditions)
{
initialStateTransitionList.Add(new AnimatorStateTransition
{
isExit = true,
hasExitTime = false,
duration = 0,
hasFixedDuration = true,
conditions = (AnimatorCondition[])conditions.Clone()
};
transitions.Add(transition);
}
else
{
foreach (var cond in conditions)
{
transitions.Add(new AnimatorStateTransition
{
isExit = true,
hasExitTime = false,
duration = 0,
hasFixedDuration = true,
conditions = new[] { InvertCondition(cond) }
});
}
conditions = new[] { InvertCondition(cond) }
});
}
}
@ -351,7 +354,7 @@ namespace nadena.dev.modular_avatar.core.editor
state.name = group.ControllingConditions[0].DebugName.Replace(".", "_");
state.motion = clip;
state.writeDefaultValues = false;
state.writeDefaultValues = _writeDefaults;
states.Add(new ChildAnimatorState
{
position = new Vector3(x, y),
@ -527,13 +530,13 @@ namespace nadena.dev.modular_avatar.core.editor
private void ApplyController(AnimatorStateMachine asm, string layerName)
{
var fx = context.AvatarDescriptor.baseAnimationLayers
.FirstOrDefault(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX);
var fx = FindFxController();
if (fx.animatorController == null)
{
throw new InvalidOperationException("No FX layer found");
}
if (!context.IsTemporaryAsset(fx.animatorController))
{
throw new InvalidOperationException("FX layer is not a temporary asset");
@ -569,5 +572,13 @@ namespace nadena.dev.modular_avatar.core.editor
}
).ToArray();
}
private VRCAvatarDescriptor.CustomAnimLayer FindFxController()
{
var fx = context.AvatarDescriptor.baseAnimationLayers
.FirstOrDefault(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX);
return fx;
}
}
}

View File

@ -28,7 +28,7 @@ namespace nadena.dev.modular_avatar.core.editor
private const string PREFIX = "m_Materials.Array.data[";
private PropCache<Renderer, ImmutableList<(int, Material)>> _cache = new(
GetMaterialOverridesForRenderer, Enumerable.SequenceEqual
"GetMaterialOverridesForRenderer", GetMaterialOverridesForRenderer, Enumerable.SequenceEqual
);
private static ImmutableList<(int, Material)> GetMaterialOverridesForRenderer(ComputeContext ctx, Renderer r)

View File

@ -87,14 +87,15 @@ namespace nadena.dev.modular_avatar.core.editor
foreach (var (paramName, list) in _mamiByParam)
{
// Assign automatic values first
float defaultValue;
int? defaultValue = null;
if (declaredParams.TryGetValue(paramName, out var p))
{
defaultValue = p.defaultValue;
defaultValue = (int) p.defaultValue;
}
else
{
defaultValue = list.FirstOrDefault(m => m.isDefault && !m.automaticValue)?.Control?.value ?? 0;
var floatDefault = list.FirstOrDefault(m => m.isDefault && !m.automaticValue)?.Control?.value;
if (floatDefault.HasValue) defaultValue = (int) floatDefault.Value;
if (list.Count == 1 && list[0].isDefault && list[0].automaticValue)
// If we have only a single entry, it's probably an on-off toggle, so we'll implicitly let 1
@ -103,7 +104,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
HashSet<int> usedValues = new();
usedValues.Add((int)defaultValue);
if (defaultValue.HasValue) usedValues.Add(defaultValue.Value);
foreach (var item in list)
{
@ -113,6 +114,19 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
if (!defaultValue.HasValue)
{
for (int i = 0; i < 256; i++)
{
if (!usedValues.Contains(i))
{
defaultValue = i;
usedValues.Add(i);
break;
}
}
}
var nextValue = 1;
var valueType = VRCExpressionParameters.ValueType.Bool;
@ -125,7 +139,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (mami.isDefault)
{
mami.Control.value = defaultValue;
mami.Control.value = defaultValue.GetValueOrDefault();
}
else
{
@ -153,7 +167,7 @@ namespace nadena.dev.modular_avatar.core.editor
name = paramName,
valueType = valueType,
saved = isSaved,
defaultValue = defaultValue,
defaultValue = defaultValue.GetValueOrDefault(),
networkSynced = isSynced
};
newParameters[paramName] = newParam;

View File

@ -128,7 +128,7 @@ namespace nadena.dev.modular_avatar.core.editor
n_nrm[i] = o_nrm[newToOrigVertIndex[i]];
n_tan[i] = o_tan[newToOrigVertIndex[i]];
}
catch (IndexOutOfRangeException e)
catch (IndexOutOfRangeException)
{
throw;
}
@ -250,7 +250,6 @@ namespace nadena.dev.modular_avatar.core.editor
{
List<int> n2o = new List<int>(toRetainVertices.Length);
List<int> o2n = new List<int>(toRetainVertices.Length);
int i = 0;
for (int j = 0; j < toRetainVertices.Length; j++)
{

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using nadena.dev.modular_avatar.core.editor.Simulator;
using nadena.dev.ndmf.preview;
using UnityEngine;
using Object = UnityEngine.Object;
@ -60,8 +59,8 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
private PropCache<GameObject, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>>
_blendshapeCache = new(ShapesForAvatar);
private readonly PropCache<GameObject, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>>
_blendshapeCache = new("ShapesForAvatar", ShapesForAvatar);
private static ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>> ShapesForAvatar(ComputeContext context, GameObject avatarRoot)
{
@ -75,7 +74,7 @@ namespace nadena.dev.modular_avatar.core.editor
ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>.Builder rendererStates =
ImmutableDictionary.CreateBuilder<SkinnedMeshRenderer, ImmutableList<(int, float)>>(
new ObjectIdentityComparer<SkinnedMeshRenderer>()
);
var avatarRootTransform = avatarRoot.transform;

View File

@ -286,7 +286,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
// these properties in a closure
_menuItemOverrideProperty = prop;
_menuItemOverrideTarget = mami;
soc.OnStateOverrideChanged += MenuItemOverrideChanged;
soc.OnStateOverrideChanged = MenuItemOverrideChanged;
}
private void MenuItemOverrideChanged(bool? obj)
@ -319,7 +319,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
_propertyOverrideProperty = property;
_propertyOverrideTargetValue = targetValue;
soc.OnStateOverrideChanged += OnParameterOverrideChanged;
soc.OnStateOverrideChanged = OnParameterOverrideChanged;
}
private void OnParameterOverrideChanged(bool? state)
@ -539,11 +539,11 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
soc.SetWithoutNotify(menuOverride);
}
soc.OnStateOverrideChanged += value => { UpdateMenuItemOverride(prop, mami, value); };
soc.OnStateOverrideChanged = value => { UpdateMenuItemOverride(prop, mami, value); };
}
else
{
soc.OnStateOverrideChanged += value => UpdatePropertyOverride(prop, value, targetValue);
soc.OnStateOverrideChanged = value => UpdatePropertyOverride(prop, value, targetValue);
}
var active = condition.InitiallyActive;

View File

@ -16,7 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor
private static StyleSheet uss;
private Button btn_disable, btn_default, btn_enable;
public event System.Action<bool?> OnStateOverrideChanged;
public System.Action<bool?> OnStateOverrideChanged;
public StateOverrideController()
{

View File

@ -31,7 +31,7 @@ namespace nadena.dev.modular_avatar.core.editor.ScaleAdjuster
public BoneState parentHint;
}
private Dictionary<Component, BoneState> _bones = new(new ObjectIdentityComparer<Component>());
private readonly Dictionary<Component, BoneState> _bones = new();
//private List<BoneState> _states = new List<BoneState>();
public void Clear()

View File

@ -59,7 +59,7 @@ namespace nadena.dev.modular_avatar.core.editor
var scaleAdjusters = ctx.GetComponentsByType<ModularAvatarScaleAdjuster>();
var avatarToRenderer =
new Dictionary<GameObject, HashSet<Renderer>>(new ObjectIdentityComparer<GameObject>());
new Dictionary<GameObject, HashSet<Renderer>>();
foreach (var root in ctx.GetAvatarRoots())
{
@ -72,8 +72,8 @@ namespace nadena.dev.modular_avatar.core.editor
{
continue; // nested avatar descriptor
}
var renderers = new HashSet<Renderer>(new ObjectIdentityComparer<Renderer>());
var renderers = new HashSet<Renderer>();
avatarToRenderer.Add(root, renderers);
foreach (var renderer in root.GetComponentsInChildren<Renderer>())
@ -97,6 +97,8 @@ namespace nadena.dev.modular_avatar.core.editor
internal class ScaleAdjusterPreviewNode : IRenderFilterNode
{
private readonly HashSet<Transform> _knownProxies = new();
private readonly GameObject SourceAvatarRoot;
private readonly GameObject VirtualAvatarRoot;
@ -110,12 +112,12 @@ namespace nadena.dev.modular_avatar.core.editor
private readonly Dictionary<Transform, Transform> _shadowBoneMap;
// Map from bones found in initial proxy state to shadow bones (with scale adjuster bones substituted)
private readonly Dictionary<Transform, Transform> _finalBonesMap = new(new ObjectIdentityComparer<Transform>());
private readonly Dictionary<Transform, Transform> _finalBonesMap = new();
private readonly Dictionary<ModularAvatarScaleAdjuster, Transform> _scaleAdjusters =
new(new ObjectIdentityComparer<ModularAvatarScaleAdjuster>());
new();
private Dictionary<Renderer, Transform[]> _rendererBoneStates = new(new ObjectIdentityComparer<Renderer>());
private Dictionary<Renderer, Transform[]> _rendererBoneStates = new();
public ScaleAdjusterPreviewNode(ComputeContext context, RenderGroup group,
IEnumerable<(Renderer, Renderer)> proxyPairs)
@ -167,7 +169,7 @@ namespace nadena.dev.modular_avatar.core.editor
private HashSet<Transform> GetSourceBonesSet(ComputeContext context, List<(Renderer, Renderer)> proxyPairs)
{
var bonesSet = new HashSet<Transform>(new ObjectIdentityComparer<Transform>());
var bonesSet = new HashSet<Transform>();
foreach (var (_, r) in proxyPairs)
{
if (r == null) continue;
@ -230,6 +232,9 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (SourceAvatarRoot == null) return Task.FromResult<IRenderFilterNode>(null);
// Clean any destroyed objects out of _knownProxies to avoid growing this set indefinitely
_knownProxies.RemoveWhere(p => p == null);
var proxyPairList = proxyPairs.ToList();
if (!GetSourceBonesSet(context, proxyPairList).SetEquals(_shadowBoneMap.Keys))
@ -243,7 +248,7 @@ namespace nadena.dev.modular_avatar.core.editor
private Dictionary<Transform, Transform> CreateShadowBones(Transform[] srcBones)
{
var srcToDst = new Dictionary<Transform, Transform>(new ObjectIdentityComparer<Transform>());
var srcToDst = new Dictionary<Transform, Transform>();
for (var i = 0; i < srcBones.Length; i++) GetShadowBone(srcBones[i]);
@ -339,7 +344,15 @@ namespace nadena.dev.modular_avatar.core.editor
if (proxy == null) return;
var curParent = proxy.transform.parent ?? original.transform.parent;
if (_finalBonesMap.TryGetValue(curParent, out var newRoot)) proxy.transform.SetParent(newRoot, false);
if (_finalBonesMap.TryGetValue(curParent, out var newRoot))
{
// We need to remember this proxy so we can avoid destroying it when we destroy VirtualAvatarRoot
// in Dispose
_knownProxies.Add(proxy.transform);
proxy.transform.SetParent(newRoot, false);
}
var smr = proxy as SkinnedMeshRenderer;
if (smr == null) return;
@ -351,6 +364,14 @@ namespace nadena.dev.modular_avatar.core.editor
public void Dispose()
{
foreach (var proxy in _knownProxies)
{
if (proxy != null && proxy.IsChildOf(VirtualAvatarRoot.transform))
{
proxy.transform.SetParent(null, false);
}
}
Object.DestroyImmediate(VirtualAvatarRoot);
_srcBones.Dispose();

View File

@ -390,7 +390,13 @@ namespace nadena.dev.modular_avatar.core.editor
private static bool ValidateSetupOutfit(GameObject gameObj)
{
Object obj;
if (gameObj == null)
{
errorHeader = S("setup_outfit.err.header.notarget");
errorMessageGroups = new string[] { S("setup_outfit.err.no_selection") };
return false;
}
errorHeader = S_f("setup_outfit.err.header", gameObj.name);
var xform = gameObj.transform;

View File

@ -205,6 +205,13 @@ namespace nadena.dev.modular_avatar.core
Transform iter = newTarget;
if (newTarget == null)
{
boneReference = HumanBodyBones.LastBone;
subPath = null;
return;
}
if (newTarget == avatarTransform)
{
boneReference = HumanBodyBones.LastBone;
@ -230,4 +237,4 @@ namespace nadena.dev.modular_avatar.core
_targetCache = newTarget;
}
}
}
}

View File

@ -1,23 +0,0 @@
#region
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#endregion
namespace nadena.dev.modular_avatar
{
internal class ObjectIdentityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return (object)x == (object)y;
}
public int GetHashCode(T obj)
{
if (obj == null) return 0;
return RuntimeHelpers.GetHashCode(obj);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: e674cbd75db24fb2b238674cd7010edb
timeCreated: 1709448428

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
using NUnit.Framework;
@ -28,6 +29,11 @@ namespace modular_avatar_tests
return transformMaskElements.SequenceEqual(other.transformMaskElements);
}
public override int GetHashCode()
{
return HashCode.Combine(humanoidMaskElements, transformMaskElements);
}
public static ExtractedMask FromAvatarMask(AvatarMask mask)
{
var so = new SerializedObject(mask);

View File

@ -19,7 +19,7 @@ namespace UnitTests.PropCacheTest
int seq = 0;
Dictionary<int, List<WeakReference<ComputeContext>>> invalidators = new();
PropCache<int,int> cache = new PropCache<int, int>((ctx, k) =>
PropCache<int,int> cache = new PropCache<int, int>("test", (ctx, k) =>
{
Debug.Log("Generating value for " + k);
if (!invalidators.TryGetValue(k, out var list))

View File

@ -48,13 +48,18 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment
{
TestAssignments(new[] { (false, 2.0f), (true, 0.0f), (true, 0.0f) }, new[] { 2.0f, 1.0f, 3.0f }, null);
TestAssignments(new[] { (false, 2.7f), (true, 0.0f), (true, 0.0f) }, new[] { 2.7f, 1.0f, 3.0f }, null);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 2.0f, 0.0f }, null, overrideExpectedDefaultValue: 1.0f);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 1.0f, 0.0f }, 0);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 1.0f, 0.0f }, 1);
}
void TestAssignments(
(bool, float)[] assignments,
float[] expectedAssignments,
int? defaultIndex = null,
Action<List<ModularAvatarMenuItem>> customize = null
Action<List<ModularAvatarMenuItem>> customize = null,
float? overrideExpectedDefaultValue = null
)
{
var root = CreateRoot("root");
@ -103,7 +108,7 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment
Assert.AreEqual(expected, mami.Control.value);
}
var expectedDefaultValue = defaultIndex.HasValue ? expectedAssignments[defaultIndex.Value] : 0;
var expectedDefaultValue = overrideExpectedDefaultValue ?? (defaultIndex.HasValue ? expectedAssignments[defaultIndex.Value] : 0);
Assert.AreEqual(expectedDefaultValue, avDesc.expressionParameters.parameters.Single().defaultValue);
}
}

View File

@ -23,10 +23,18 @@ designed for a specific avatar.
## Manually configuring Mesh Settings
When you add Mesh Settings to a game object, initially it is configured to do nothing. By setting either
"Anchor Override Mode" or "Bounds Override Mode" to "Set", you can configure the anchor override or bounds
for all meshes under that game object. Alternately, by setting the mode to "Don't set", you can exclude
these meshes from the influence of Mesh Settings higher up on the hierarchy.
When you add Mesh Settings to a game object, initially it is configured to do nothing. In order for the settings
component to have any effect, you need to change the "Anchor Override Mode" and/or "Bounds Override Mode". These
support the following options:
- Inherit: This component does nothing for this setting; it will inherit values set in parent Mesh Settings.
- Set: This component sets the corresponding setting on any meshes on its GameObject or its children.
- Don't set: This component _blocks_ any parent Mesh Settings from having an effect. Meshes will remain at their default
settings.
- Set or inherit: If there is a parent Mesh Settings in Set mode, it will be used. If no parent Mesh Settings applies,
then this component's settings will be used. This is useful for outfit prefabs, to ensure that any avatar-wide
settings
take precedence.
When configuring bounds, the bounding box will be determined relative to the transform you specify as the
"Root Bone". Note that bounds only affects Skinned Mesh Renderers, but Anchor Override also impacts other

View File

@ -22,5 +22,3 @@ Just attach a Visible Head Accessory component under a child of the Head bone. T
The component will automatically generate a clone of the Head bone, which is connected to the real head bone using a parent constraint.
Only one constraint will be generated, even if multiple Visible Head Accessory 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.

View File

@ -21,9 +21,16 @@ Mesh Settingsコンポーネントを使用すると、特定のゲームオブ
## Mesh Settingsの手動設定
ゲームオブジェクトにMesh Settingsを追加すると、最初は何も効果がありません。Anchor Overrideの設定モードまたはBounds Override
の設定モードを「設定」に設定することで、そのゲームオブジェクトの下にあるすべてのメッシュのアンカーオーバーライドまたはバウンズ
を設定できます。また、モードを「設定しない」に設定することで、これらのメッシュを階層の上位にあるMesh Settingsの影響から除外できます。
ゲームオブジェクトにMesh Settingsを追加すると、最初は何も効果がありません。効果を発揮するには、まずは「Anchor Override
設定」あるいは
「Bounds 設定」で「設定モード」を変更する必要があります。以下の選択しがあります。
- 継承このコンポーネントはこの設定に対して何もしません。親のMesh Settingsで設定された値を継承します。
- 設定:このコンポーネントは、そのゲームオブジェクトとその子にあるメッシュの対応する設定を設定します。
- 設定しないこのコンポーネントは、親のMesh Settingsの影響を受けないようにします。メッシュはデフォルトの設定のままです。
- 親が継承された時は継承、または設定親のMesh Settingsが設定モードにある場合、それが使用されます。親のMesh
Settingsが適用されない場合、
このコンポーネントの設定が使用されます。衣装プレハブなどに、アバター全体の設定が優先されるようにするために便利です。
バウンズを設定する場合、バウンディングボックスは「Root Bone」として指定したトランスフォームに対して相対的に決定されます。
また、バウンズはSkinned Mesh Rendererのみに影響しますが、Anchor OverrideはMesh RendererやLine Rendererなどの他のタイプの

View File

@ -22,5 +22,3 @@ Headボーンの子にVisible Head Accessoryコンポーネントを付けるだ
このコンポーネントは自動的にHeadボーンを複製し、Parent Constraintで本物のHeadボーンに追従させます。
複数のVisible Head Accessoryコンポーネントを使っても、Constraintは一つだけです。なので、複数のボーンを指定しても、その分重くなることはありません。
技術的な制約により、Quest単体では動作できません。Quest向けのビルドはつけたままにしてもいいが、効果は発揮しません。

View File

@ -1,7 +1,7 @@
{
"name": "nadena.dev.modular-avatar",
"displayName": "Modular Avatar",
"version": "1.10.0-rc.6",
"version": "1.10.0-rc.9",
"unity": "2022.3",
"description": "A suite of tools for assembling your avatar out of reusable components",
"author": {
@ -16,6 +16,6 @@
},
"vpmDependencies": {
"com.vrchat.avatars": ">=3.7.0",
"nadena.dev.ndmf": ">=1.5.0-rc.7 <2.0.0-a"
"nadena.dev.ndmf": ">=1.5.0-rc.10 <2.0.0-a"
}
}