Skip to content

Commit

Permalink
Resolve runtime VRM model loading errors and refactor configuration p…
Browse files Browse the repository at this point in the history
…rocess

- Resolved an issue where ignorable errors occurred during the runtime loading of VRM 3D models. These errors no longer appear, improving user experience.
- Refactored the runtime character 3D model configuration process, enhancing maintainability and readability of the codebase.
  • Loading branch information
uezo committed Nov 20, 2024
1 parent 4d6948e commit d45b0bb
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 103 deletions.
17 changes: 0 additions & 17 deletions Editor/ModelControllerEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,6 @@
[CustomEditor(typeof(ModelController))]
public class FaceClipEditor : Editor
{
public override void OnInspectorGUI()
{
var app = target as ModelController;

if (EditorApplication.isPlaying)
{
if (GUILayout.Button("Change Avatar"))
{
app.SetAvatar(activation: true);
}

GUILayout.Space(20.0f);
}

base.OnInspectorGUI();
}

// Setup ModelController
[MenuItem("CONTEXT/ModelController/Setup ModelController")]
private static void Setup(MenuCommand menuCommand)
Expand Down
21 changes: 5 additions & 16 deletions Extension/VRM/VRMBlink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ public class VRMBlink : MonoBehaviour, IBlink
public bool IsBlinkEnabled { get; private set; } = false;
private Action blinkAction;
private CancellationTokenSource blinkTokenSource;

private void Awake()
{
Setup(gameObject.GetComponent<ModelController>().AvatarModel);
}
private bool blinkLoopAlreadyStarted = false; // This doesn't back to false after once it turns to true

public void Setup(GameObject avatarObject)
{
Expand All @@ -38,11 +34,6 @@ public void Setup(GameObject avatarObject)
blinkTokenSource = new CancellationTokenSource();
}

private void Start()
{
_ = StartBlinkAsync(true);
}

private void LateUpdate()
{
blinkAction?.Invoke();
Expand All @@ -54,10 +45,10 @@ private void OnDestroy()
}

// Initialize and start blink
public async UniTask StartBlinkAsync(bool startNew = false)
public async UniTask StartBlinkAsync()
{
// Return with doing nothing when already blinking
if (IsBlinkEnabled && startNew == false)
if (IsBlinkEnabled && blinkLoopAlreadyStarted)
{
return;
}
Expand All @@ -74,12 +65,10 @@ public async UniTask StartBlinkAsync(bool startNew = false)
// Enable blink
IsBlinkEnabled = true;

if (!startNew)
{
return;
}
if (blinkLoopAlreadyStarted) return;

// Start new blink loop
blinkLoopAlreadyStarted = true;
while (true)
{
if (blinkTokenSource.Token.IsCancellationRequested)
Expand Down
7 changes: 2 additions & 5 deletions Extension/VRM/VRMFaceExpressionProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ public class VRMFaceExpressionProxy : MonoBehaviour, IFaceExpressionProxy
private float valueToApply;
private float velocityAtStart;

private void Awake()
{
Setup(gameObject.GetComponent<ModelController>().AvatarModel);
}

public void Setup(GameObject avatarObject)
{
blendShapeProxy = avatarObject.GetComponent<VRMBlendShapeProxy>();
Expand All @@ -30,6 +25,8 @@ public void Setup(GameObject avatarObject)

private void Update()
{
if (blendShapeProxy == null || blinker == null) return; // Do nothing if not setup yet

if (changeStartAt > 0)
{
if (currentFaceName == "Neutral")
Expand Down
31 changes: 14 additions & 17 deletions Extension/VRM/VRMLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private void Start()
Debug.Log($"Get VRM from url: {VRMFilePath}");
_ = LoadCharacterAsync(VRMFilePath, "GET");
}
else
else if (!string.IsNullOrEmpty(VRMFilePath.Trim()))
{
Debug.Log($"Get VRM from file: {VRMFilePath}");
_ = LoadCharacterAsync(VRMFilePath);
Expand Down Expand Up @@ -70,15 +70,17 @@ public async UniTask LoadCharacterAsync(byte[] vrmBytes)

try
{
// Destroy current character and stop blinking
if (vrmInstance != null)
{
Destroy(vrmInstance);
}
if (characterObject != null)
{
Destroy(characterObject);
}
// Deactivate and unload
modelController.DeactivateAvatar(() => {
if (vrmInstance != null)
{
Destroy(vrmInstance);
}
if (characterObject != null)
{
Destroy(characterObject);
}
});

Debug.Log($"VRM size: {vrmBytes.Length} bytes");

Expand All @@ -94,19 +96,14 @@ public async UniTask LoadCharacterAsync(byte[] vrmBytes)
// Dispose to prevent memory leak
context.Dispose();

// Setup ChatdollKit
characterObject = vrmInstance.gameObject;
characterObject.name = "CharacterVRM";
modelController.AvatarModel = characterObject;
var animator = characterObject.GetComponent<Animator>();
animator.runtimeAnimatorController = animatorController;
var lipSyncHelper = modelController.gameObject.GetComponent<VRMuLipSyncHelper>();
lipSyncHelper.ConfigureViseme(characterObject);

// Initialize ChatdollKit
modelController.gameObject.SetActive(true);
// Apply avatar to ModelController
modelController.ActivateAvatar(characterObject, true);

// Wait before show to finish transition to initial animation
await UniTask.Delay(WaitBeforeShowCharacter);

// Show character
Expand Down
29 changes: 6 additions & 23 deletions Scripts/Model/Blink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,7 @@ public class Blink : MonoBehaviour, IBlink
public bool IsBlinkEnabled { get; private set; } = false;
private Action blinkAction;
private CancellationTokenSource blinkTokenSource;

private void Awake()
{
blinkTokenSource = new CancellationTokenSource();
}

private void Start()
{
if (string.IsNullOrEmpty(blinkBlendShapeName))
{
Debug.LogWarning("Blink is disabled because BlinkBlendShapeName is not defined");
}
else
{
_ = StartBlinkAsync(true);
}
}
private bool blinkLoopAlreadyStarted = false; // This doesn't back to false after once it turns to true

private void LateUpdate()
{
Expand All @@ -62,6 +46,7 @@ public void Setup(GameObject avatarObject)
{
Debug.LogWarning("BlendShape for blink not found.");
}
blinkTokenSource = new CancellationTokenSource();
}

public string GetBlinkShapeName()
Expand Down Expand Up @@ -89,10 +74,10 @@ private static string GetBlinkTargetName(SkinnedMeshRenderer skinnedMeshRenderer
}

// Initialize and start blink
public async UniTask StartBlinkAsync(bool startNew = false)
public async UniTask StartBlinkAsync()
{
// Return with doing nothing when already blinking
if (IsBlinkEnabled && startNew == false)
if (IsBlinkEnabled && blinkLoopAlreadyStarted)
{
return;
}
Expand All @@ -109,12 +94,10 @@ public async UniTask StartBlinkAsync(bool startNew = false)
// Enable blink
IsBlinkEnabled = true;

if (!startNew)
{
return;
}
if (blinkLoopAlreadyStarted) return;

// Start new blink loop
blinkLoopAlreadyStarted = true;
while (true)
{
if (blinkTokenSource.Token.IsCancellationRequested)
Expand Down
2 changes: 1 addition & 1 deletion Scripts/Model/IBlink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace ChatdollKit.Model
{
public interface IBlink
{
UniTask StartBlinkAsync(bool startNew = false);
UniTask StartBlinkAsync();
void StopBlink();
void Setup(GameObject avatarObject);
}
Expand Down
62 changes: 38 additions & 24 deletions Scripts/Model/ModelController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,32 @@ public class ModelController : MonoBehaviour
private List<FaceExpression> faceQueue = new List<FaceExpression>();
private float faceStartAt { get; set; }
private FaceExpression currentFace { get; set; }
private IBlink blinker { get; set; }

// History recorder for debug and test
public ActionHistoryRecorder History;

private void Awake()
{
animator = AvatarModel.gameObject.GetComponent<Animator>();
GetAnimation = GetIdleAnimation;

// Get lipSyncHelper
// LipSyncHelper
lipSyncHelper = gameObject.GetComponent<ILipSyncHelper>();

// Face expression proxy
faceExpressionProxy = gameObject.GetComponent<IFaceExpressionProxy>();

// Blinker
blinker = gameObject.GetComponent<IBlink>();

if (AvatarModel == null)
{
enabled = false;
}
else
{
ActivateAvatar();
}
}

private void Start()
Expand Down Expand Up @@ -544,37 +556,39 @@ public FaceExpression GetFaceExpression()
#endregion

#region Avatar
public void SetAvatar(GameObject avatarObject = null, bool activation = false)
public void ActivateAvatar(GameObject avatarObject = null, bool configureViseme = false)
{
var currentAvatarObject = animator.gameObject;
var newAvatarObject = avatarObject == null ? AvatarModel : avatarObject;

if (activation)
if (avatarObject != null)
{
currentAvatarObject.SetActive(false);
AvatarModel = avatarObject;
}

// Animator
animator = newAvatarObject.gameObject.GetComponent<Animator>();
animator = AvatarModel.GetComponent<Animator>();

// Blink (Blink at first because FaceExpression depends blink)
// TODO: Make the dependency simple
GetComponent<IBlink>()?.Setup(newAvatarObject);

// Face expression
faceExpressionProxy.Setup(newAvatarObject);
blinker.Setup(AvatarModel);
faceExpressionProxy.Setup(AvatarModel);

// LipSync
lipSyncHelper.ConfigureViseme(newAvatarObject);
if (configureViseme)
{
lipSyncHelper.ConfigureViseme(AvatarModel);
}

// Start idling
_ = blinker.StartBlinkAsync();
currentAnimation = null; // Set null to newly start idling animation
StartIdling(true);

if (activation)
{
newAvatarObject.SetActive(true);
}
AvatarModel.SetActive(true);
enabled = true;
}

public void DeactivateAvatar(Action unloadAction = null)
{
if (AvatarModel == null) return;

enabled = false;
blinker.StopBlink();

AvatarModel.SetActive(false);
unloadAction?.Invoke();
}
#endregion
}
Expand Down

0 comments on commit d45b0bb

Please sign in to comment.