8000 fix: missing gesture layer mask can cause bicycle pose in play mode emulation by bdunderscore · Pull Request #602 · bdunderscore/ndmf · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix: missing gesture layer mask can cause bicycle pose in play mode emulation #602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Fixed
- [#602] Fix issue where, if the hidden "mask" value on the gesture layer in the VRChat avatar controller was missing,
the avatar would be stuck in "bicycle pose" in gesture manager and Av3Emulator.

### Changed

Expand Down
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- These components are very much subject to change in future builds (and are hidden behind the NDMF_EXPERIMENTAL feature flag)

### Fixed
- [#599] Substate machine transitions were not being enumerated (and thus not processed for MA Parameters renaming)
- [#600] Fix issue where loop time (and other clip settings) were not preserved when there was no additive reference clip.
- [#602] Fix issue where, if the hidden "mask" value on the gesture layer in the VRChat avatar controller was missing,
the avatar would be stuck in "bicycle pose" in gesture manager and Av3Emulator.

### Changed

Expand All @@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Deprecated

## [1.7.8] - [2025-04-17]

### Fixed
- [#599] Substate machine transitions were not being enumerated (and thus not processed for MA Parameters renaming)
- [#600] Fix issue where loop time (and other clip settings) were not preserved when there was no additive reference clip.

## [1.7.7] - [2025-04-14]

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,16 @@ void EditLayers(VRCAvatarDescriptor.CustomAnimLayer[] layers)
{
layers[i].animatorController = controller;
layers[i].isDefault = false;
if (layers[i].type is VRCAvatarDescriptor.AnimLayerType.Gesture
or VRCAvatarDescriptor.AnimLayerType.FX)
{
// See AvatarDescriptorEditor3.SetLayerMaskFromController
var innerLayers = ((AnimatorController)controller).layers;
if (innerLayers.Length > 0)
{
layers[i].mask = innerLayers[0].avatarMask;
}
}
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions UnitTests~/AnimationServices/VRChatTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#if NDMF_VRCSDK3_AVATARS

using System.Linq;
using nadena.dev.ndmf;
using nadena.dev.ndmf.animator;
using nadena.dev.ndmf.UnitTestSupport;
using NUnit.Framework;
using UnityEditor;
using UnityEditor.Advertisements;
using UnityEditor.Animations;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
Expand Down Expand Up @@ -280,6 +283,47 @@ public void RemapPlayAudioPaths_InVirtualizedController()

Assert.AreEqual("B/A", behaviour.SourcePath);
}

[Test]
public void AnimatorMaskIsPopulated()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Packages/nadena.dev.ndmf/UnitTests/AnimationServices/_CommonAssets/ShapellAvatar.prefab");
var root = TrackObject(Object.Instantiate(prefab));

// Clear all pre-existing masks
var avDesc = root.GetComponent<VRCAvatarDescriptor>();
for (int i = 0; i < avDesc.baseAnimationLayers.Length; i++)
{
var layer = avDesc.baseAnimationLayers[i];
layer.mask = null;
avDesc.baseAnimationLayers[i] = layer;
}

var ctx = CreateContext(root);
ctx.ActivateExtensionContext<VirtualControllerContext>();
ctx.DeactivateAllExtensionContexts();

foreach (var layer in avDesc.baseAnimationLayers)
{
bool shouldHaveMask = layer.type is VRCAvatarDescriptor.AnimLayerType.FX
or VRCAvatarDescriptor.AnimLayerType.Gesture;

if (shouldHaveMask)
{
Debug.Log("Layer controller type: " + layer.animatorController?.GetType());
Debug.Log("Layer path: " + AssetDatabase.GetAssetPath(layer.animatorController));
Assert.That(layer.animatorController, Is.TypeOf(typeof(AnimatorController)));
var controller = (AnimatorController)layer.animatorController;
Copy link
Preview
Copilot AI Apr 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test assumes that the animator controller always includes at least one layer. Consider adding a check or assertion to verify that controller.layers is not empty before accessing its first element.

Suggested change
var controller = (AnimatorController)layer.animatorController;
var controller = (AnimatorController)layer.animatorController;
Assert.IsTrue(controller.layers.Length > 0, "AnimatorController has no layers.");

Copilot uses AI. Check for mistakes.

var expectedMask = controller.layers[0].avatarMask;

Assert.AreSame(expectedMask, layer.mask);
}
else
{
Assert.IsNull(layer.mask);
}
}
}

private AnimatorController BuildInterlayerController(string prefix)
{
Expand Down
8 changes: 8 additions & 0 deletions UnitTests~/AnimationServices/_CommonAssets.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
0