Skip to content

Commit

Permalink
全面改訂
Browse files Browse the repository at this point in the history
  • Loading branch information
pandrabox committed Sep 12, 2024
1 parent 3f4bf30 commit e0da311
Show file tree
Hide file tree
Showing 30 changed files with 1,536 additions and 816 deletions.
177 changes: 177 additions & 0 deletions Editor/Avatar.cs
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.
*/
2 changes: 1 addition & 1 deletion Editor/PanActionLayer.cs.meta → Editor/Avatar.cs.meta

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

147 changes: 147 additions & 0 deletions Editor/DividedClip.cs
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.
*/
2 changes: 1 addition & 1 deletion Editor/EmotePrefab.cs.meta → Editor/DividedClip.cs.meta

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

Loading

0 comments on commit e0da311

Please sign in to comment.