Skip to content

Improve Blink and FaceExpression handling for various 3D avatars#471

Merged
uezo merged 12 commits intouezo:masterfrom
lavender-snow:feature/improve-blink-and-facial-expression
Aug 29, 2025
Merged

Improve Blink and FaceExpression handling for various 3D avatars#471
uezo merged 12 commits intouezo:masterfrom
lavender-snow:feature/improve-blink-and-facial-expression

Conversation

@lavender-snow
Copy link
Contributor

Summary

Since 3D avatar blendshape names are not strictly standardized, some avatars may only include a blink blendshape with names like the Japanese word "まばたき."
In this PR, I made changes so that such avatars can be handled without manually modifying scripts or the 3D models themselves.

Changes

  • When the Blink or VRCFaceExpressionProxy component inspector variables are already set, the Setup function will now use those existing values instead of overriding them.

  • The second parameter of GetFacialSkinnedMeshRenderer can now accept a string. this value will be used when searching for the facial mesh.

  • Fixed an issue where the entire process could fail if Blink component was not set up correctly.

@uezo
Copy link
Owner

uezo commented Aug 24, 2025

Hi @lavender-snow ,
Thank you for awesome PR🙌
This proposal goes right to the heart of what’s needed to turn any avatar into an AI character. I’m confident it’ll be meaningful for a lot of developers!

On the implementation side, how about taking a more general-purpose approach? I love your idea of passing facial blendshape name keywords in from the outside to AvatarUtility.GetFacialSkinnedMeshRenderer. Building on that, I think the currently hardcoded blink and eye_close should also be supplied externally rather than defined inside.

For example, we could add a member like this to ModelController, and pass it through to GetFacialSkinnedMeshRenderer (and any methods that call it internally):

public string[] FaceBlendShapeKeywords = { "blink", "eye_close" };

Would love to hear your thoughts! ✨🙏✨

@lavender-snow
Copy link
Contributor Author

Hello @uezo,
Thank you for reviewing my PR.

Based on your comment, I’ve been thinking about an approach to improve the implementation. How about changing the code to leverage the facial SkinnedMeshRenderer that ModelController already has as an inspector variable?

Here’s the plan I’m considering:

  1. Update GetFacialSkinnedMeshRenderer
    Make it always accept a keyword as the second parameter, and remove any hardcoded strings for facial mesh searches inside the function.

  2. Add FaceBlendShapeKeywords to ModelController
    Introduce this as an inspector variable, which will be utilized in the setupModelController method of ModelControllerEditor for facial mesh lookup.

  3. Pass the facial SkinnedMeshRenderer directly to components
    Will change to pass the facial SkinnedMeshRenderer directly instead of avatarObject to Blink, FaceExpression, uLipsyncHelper.

If these components already have the SkinnedMeshRenderer set via the inspector, the setup function will prioritize the existing value over the passed-in value, as ignoring inspector-set values could result in behavior that users might perceive as unintended.

If this approach sounds reasonable, I’ll proceed with the implementation. I’d appreciate your feedback.

Additionally, I noticed that the current PR was missing the using directive for LINQ in AvatarUtility.cs, which caused a compile error. I’ve already pushed a fix for this. Apologies for the oversight.

@uezo
Copy link
Owner

uezo commented Aug 24, 2025

Hi @lavender-snow , thanks for jumping on this so quickly! I’m happy with your plan as proposed.

One ask: please keep backward compatibility so users on the current version can upgrade without errors(no breaking changes for typical users). 🙏

@lavender-snow
Copy link
Contributor Author

@uezo
After considering the need to maintain compatibility, I realized that making major changes might not be the best approach. I’ve adjusted the changes to focus on the following points:

  • Ensure the original goal: allow handling diverse avatars without requiring edits to scripts or 3D models
  • Minimize the risk of errors after merging this PR
  • Replaced hardcoded values with constants in the code related to this change

I’d appreciate it if you could review these changes.

@uezo
Copy link
Owner

uezo commented Aug 25, 2025

Hi @lavender-snow ,
Thanks for tackling this! The idea of minimizing impact on existing components is great 👏

Just one question: I see VRMuLipSyncHelper is a reimplementation rather than a subclass of uLipSyncHelper. Was that an intentional design choice for a specific reason, or was subclassing simply difficult in this case?

@lavender-snow
Copy link
Contributor Author

lavender-snow commented Aug 26, 2025

@uezo
Thank you for checking.
I was overly cautious about adding new files, which led me to take that approach earlier.
Now, I’ve implemented it by creating a new subclass of uLipSyncHelper without making any changes to uLipSyncHelper and VRMuLipSyncHelper.
I’d appreciate your feedback on this approach.

@uezo uezo changed the title Improve Blink and FaceExpression handling to support diverse 3D avatars Improve Blink and FaceExpression handling for various 3D avatars Aug 29, 2025
@uezo uezo merged commit af2097a into uezo:master Aug 29, 2025
@uezo
Copy link
Owner

uezo commented Aug 29, 2025

@lavender-snow Cool, thanks a lot for your contribution! 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants