-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
1,536 additions
and
816 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// <copyright file="Avatar.cs"></copyright> | ||
#pragma warning disable SA1600 // Elements should be documented | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using com.github.pandrabox.emoteprefab.editor; | ||
using com.github.pandrabox.emoteprefab.runtime; | ||
using nadena.dev.modular_avatar.core; | ||
using nadena.dev.modular_avatar.core.editor; | ||
using nadena.dev.ndmf; | ||
using UnityEditor; | ||
using UnityEditor.Animations; | ||
using UnityEngine; | ||
using VRC.SDK3.Avatars.Components; | ||
using static com.github.pandrabox.emoteprefab.runtime.Generic; | ||
|
||
namespace com.github.pandrabox.emoteprefab.editor | ||
{ | ||
/// <summary> | ||
/// Avatarを管理するクラス | ||
/// </summary> | ||
public static class Avatar | ||
{ | ||
private static VRCAvatarDescriptor _descriptor; | ||
private static bool _initialized = false; | ||
private static AnimatorController _actionController; | ||
private static AnimatorController _fXController; | ||
|
||
public static AnimatorController ActionController | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _actionController; | ||
} | ||
} | ||
public static AnimatorController FXController | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _fXController; | ||
} | ||
} | ||
|
||
public static VRCAvatarDescriptor Descriptor | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _initialized ? _descriptor : null; | ||
} | ||
} | ||
|
||
public static GameObject RootObject | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _descriptor.gameObject; | ||
} | ||
} | ||
|
||
public static Transform RootTransform | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _descriptor.transform; | ||
} | ||
} | ||
|
||
public static VRCAvatarDescriptor.CustomAnimLayer ActionLayer | ||
{ | ||
get | ||
{ | ||
InitCheck(); | ||
return _descriptor.baseAnimationLayers[3]; | ||
} | ||
} | ||
|
||
public static void clear() | ||
{ | ||
_initialized = false; | ||
} | ||
|
||
public static void Init(VRCAvatarDescriptor descriptor) | ||
{ | ||
if (!_initialized) | ||
{ | ||
_initialized = true; | ||
_descriptor = descriptor; | ||
ActionReplace(); | ||
MergeGeneratedFX(); | ||
EmoteLayersSync(); | ||
} | ||
else | ||
{ | ||
WriteWarning("Avatar.Init", "Initial twice time"); | ||
} | ||
} | ||
|
||
private static void InitCheck() | ||
{ | ||
if (!_initialized) | ||
{ | ||
WriteWarning("Avatar", "Avatar class was accessed before it was initialized"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Actionレイヤの置き換え | ||
/// </summary> | ||
private static void ActionReplace() | ||
{ | ||
_actionController = AssetDatabase.LoadAssetAtPath<AnimatorController>(Config.GeneratedActionLayer); | ||
_descriptor.baseAnimationLayers[3].animatorController = _actionController ?? throw new Exception("EmotePrefab ActionLayerReplace AssignController Not Found"); | ||
_descriptor.baseAnimationLayers[3].isDefault = false; | ||
} | ||
|
||
/// <summary> | ||
/// GeneratedFXをMergeするプレハブ生成 | ||
/// </summary> | ||
private static void MergeGeneratedFX() | ||
{ | ||
GameObject targetObj = new GameObject("EmotePrefab_FX"); | ||
targetObj.transform.SetParent(Avatar.RootTransform); | ||
var mergeAnimator = targetObj.AddComponent<ModularAvatarMergeAnimator>(); | ||
_fXController = AssetDatabase.LoadAssetAtPath<AnimatorController>(Config.GeneratedFXLayer); | ||
if (_fXController == null) | ||
{ | ||
WriteWarning("MergeGeneratedFX", "GeneratedFX Not Found"); | ||
} | ||
|
||
mergeAnimator.animator = _fXController; | ||
mergeAnimator.pathMode = MergeAnimatorPathMode.Absolute; | ||
mergeAnimator.matchAvatarWriteDefaults = true; | ||
mergeAnimator.layerPriority = 9999999; | ||
} | ||
|
||
/// <summary> | ||
/// EmoteLayersを同期確保するプレハブ生成 | ||
/// </summary> | ||
private static void EmoteLayersSync() | ||
{ | ||
GameObject obj = new GameObject("EmotePrefab_EmoteLayersSync"); | ||
obj.transform.SetParent(Avatar.RootTransform); | ||
ModularAvatarParameters mparams = obj.AddComponent<ModularAvatarParameters>(); | ||
mparams.parameters.Add(new ParameterConfig() | ||
{ | ||
nameOrPrefix = "CN_IS_ACTION_ACTIVE", | ||
syncType = ParameterSyncType.Bool, | ||
localOnly = true, | ||
}); | ||
mparams.parameters.Add(new ParameterConfig() | ||
{ | ||
nameOrPrefix = "CN_IS_ACTION_ACTIVE_FX1", | ||
syncType = ParameterSyncType.Bool, | ||
localOnly = true, | ||
}); | ||
mparams.parameters.Add(new ParameterConfig() | ||
{ | ||
nameOrPrefix = "CN_IS_ACTION_ACTIVE_FX2", | ||
syncType = ParameterSyncType.Bool, | ||
localOnly = true, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
|
||
|
||
/* For Reviwer | ||
* Please review this code for each element and identify areas for improvement, providing a final score out of 100. | ||
* Please review this code in Japanese. | ||
*/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// <copyright file="DividedClip.cs"></copyright> | ||
#pragma warning disable SA1600 // Elements should be documented | ||
#pragma warning disable SA1401 // Fields should be private | ||
|
||
using System.Linq; | ||
using UnityEditor; | ||
using UnityEngine; | ||
|
||
namespace com.github.pandrabox.emoteprefab.editor | ||
{ | ||
/// <summary> | ||
/// EmoteClipの分割を管掌するクラス | ||
/// </summary> | ||
public class DividedClip | ||
{ | ||
public AnimationClip Original; | ||
public AnimationClip HumanoidClip; | ||
public AnimationClip BodyShapeBlockerClip; | ||
public AnimationClip UnhumanoidClip; | ||
public AnimationClip FakeWriteDefaultClip; | ||
public bool HasHumanoid; | ||
public bool HasBodyShape; | ||
public bool HasUnhumanoid; | ||
|
||
/// <summary> | ||
/// EmoteClipの分割 | ||
/// </summary> | ||
/// <param name="clip">分割するClip</param> | ||
public DividedClip() | ||
{ | ||
Original = UnityEngine.Object.Instantiate(EmoteManager.EmotePrefab.Motion); | ||
AddKeyframesAtEnd(); | ||
TypeCheck(); | ||
CreateHumanoidClip(); | ||
CreateUnhumanoidClip(); | ||
CreateBodyShapeBlockerClip(); | ||
CreateFakeWriteDefaultClip(); | ||
} | ||
|
||
/// <summary> | ||
/// clipの全キーの最終フレームを打つ(分割後の長さ調整の為) | ||
/// </summary> | ||
private void AddKeyframesAtEnd() | ||
{ | ||
float clipLength = Original.length; | ||
EditorCurveBinding[] curves = AnimationUtility.GetCurveBindings(Original); | ||
|
||
foreach (var binding in curves) | ||
{ | ||
AnimationCurve curve = AnimationUtility.GetEditorCurve(Original, binding); | ||
|
||
if (curve != null && curve.keys.Length > 0) | ||
{ | ||
Keyframe lastKey = curve.keys[curve.keys.Length - 1]; | ||
if (lastKey.time < clipLength) | ||
{ | ||
curve.AddKey(new Keyframe(clipLength, lastKey.value, lastKey.inTangent, lastKey.outTangent)); | ||
AnimationUtility.SetEditorCurve(Original, binding, curve); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// clipタイプの解析 | ||
/// </summary> | ||
private void TypeCheck() | ||
{ | ||
EditorCurveBinding[] curves = AnimationUtility.GetCurveBindings(Original); | ||
HasHumanoid = curves.Any(c => c.type == typeof(Animator)); | ||
HasUnhumanoid = curves.Any(c => c.type != typeof(Animator)); | ||
HasBodyShape = HasUnhumanoid && curves.Any(c => (c.path.ToLower() == "body" && c.propertyName.StartsWith("blendShape."))); | ||
} | ||
|
||
/// <summary> | ||
/// HumanoidClip(正確にはAAPClip)の生成 | ||
/// </summary> | ||
private void CreateHumanoidClip() | ||
{ | ||
HumanoidClip = UnityEngine.Object.Instantiate(Original); | ||
foreach (var binding in AnimationUtility.GetCurveBindings(HumanoidClip)) | ||
{ | ||
if (binding.type != typeof(Animator)) | ||
{ | ||
AnimationUtility.SetEditorCurve(HumanoidClip, binding, null); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// UnhumanoidClip(正確にはUnAAPClip)の生成 | ||
/// </summary> | ||
private void CreateUnhumanoidClip() | ||
{ | ||
UnhumanoidClip = UnityEngine.Object.Instantiate(Original); | ||
foreach (var binding in AnimationUtility.GetCurveBindings(UnhumanoidClip)) | ||
{ | ||
if (binding.type == typeof(Animator)) | ||
{ | ||
AnimationUtility.SetEditorCurve(UnhumanoidClip, binding, null); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Bodyのシェイプキーを0にするクリップの生成 | ||
/// </summary> | ||
private void CreateBodyShapeBlockerClip() | ||
{ | ||
SkinnedMeshRenderer bodyMesh = Avatar.RootTransform.Find("Body")?.GetComponent<SkinnedMeshRenderer>(); | ||
BodyShapeBlockerClip = new AnimationClip() | ||
{ | ||
wrapMode = WrapMode.ClampForever, | ||
}; | ||
float referenceClipLength = Original.length; | ||
int blendShapeCount = bodyMesh.sharedMesh.blendShapeCount; | ||
for (int i = 0; i < blendShapeCount; i++) | ||
{ | ||
var name = bodyMesh.sharedMesh.GetBlendShapeName(i); | ||
AnimationCurve curve = AnimationCurve.Constant(0, referenceClipLength, 0); | ||
BodyShapeBlockerClip.SetCurve(string.Empty, typeof(SkinnedMeshRenderer), $"blendShape.{name}", curve); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// デフォルト値に戻すクリップの生成 | ||
/// </summary> | ||
private void CreateFakeWriteDefaultClip() | ||
{ | ||
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(UnhumanoidClip); | ||
FakeWriteDefaultClip = new AnimationClip(); | ||
foreach (EditorCurveBinding binding in bindings) | ||
{ | ||
float currentValue; | ||
AnimationUtility.GetFloatValue(Avatar.RootObject, binding, out currentValue); | ||
Keyframe keyframe = new Keyframe(0, currentValue); | ||
AnimationCurve curve = new AnimationCurve(keyframe); | ||
AnimationUtility.SetEditorCurve(FakeWriteDefaultClip, binding, curve); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/* For Reviwer | ||
* Please review this code for each element and identify areas for improvement, providing a final score out of 100. | ||
* Please review this code in Japanese. | ||
*/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.