Skip to content

Commit

Permalink
揺れ物の変換について、リフレクションを利用するようにした
Browse files Browse the repository at this point in the history
参照:
C# リフレクションを無理矢理用いて、ジェネリックコレクションを扱う — s-kita’s blog
<http://s-kita.hatenablog.com/entry/20091114/1258207948>
  • Loading branch information
esperecyan committed Apr 27, 2019
1 parent e4f847d commit 13685b4
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 80 deletions.
55 changes: 0 additions & 55 deletions Editor/ComponentsReplacer.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEditor;
using VRM;
using UniGLTF;
using VRCSDK2;

namespace Esperecyan.Unity.VRMConverterForVRChat
{
/// <summary>
/// キャラクター情報、視点、揺れ物に関する設定。
/// </summary>
[InitializeOnLoad]
public class ComponentsReplacer
{
/// <summary>
Expand All @@ -35,12 +30,6 @@ public enum SwayingObjectsConverterSetting
/// <returns></returns>
public delegate DynamicBoneParameters SwayingParametersConverter(SpringBoneParameters springBoneParameters, BoneInfo boneInfo);

static ComponentsReplacer()
{
EnableClassDependentDependingOptionalAsset();
FixFindDynamicBoneTypesMethodOnAvatarPerformanceClass();
}

/// <summary>
/// <see cref="ComponentsReplacer.SwayingParametersConverter">の既定値。
/// </summary>
Expand Down Expand Up @@ -90,50 +79,6 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
return messages;
}

/// <summary>
/// Dynamic Boneアセットがインポートされていれば場合、同アセットに含まれるクラスを利用しているスクリプトファイルを有効化します。
/// </summary>
private static void EnableClassDependentDependingOptionalAsset()
{
if (Type.GetType("DynamicBone, Assembly-CSharp") != null
&& Type.GetType(typeof(ComponentsReplacer).Namespace + ".SwayingObjectsConverter, Assembly-CSharp-Editor") == null)
{
var path = Path.Combine(Path.Combine(Converter.RootFolderPath, "Editor"), "SwayingObjectsConverter.cs");
AssetDatabase.MoveAsset(oldPath: path + ".bak", newPath: path);
}
}

/// <summary>
/// <see cref="AvatarPerformance"/>クラスを書き替えて、DynamicBoneに関するパフォーマンスが表示されないVRChat SDKのバグを修正します。
/// </summary>
/// <seealso cref="SwayingObjectsConverter.GetMessagesAboutDynamicBoneLimits"/>
/// <remarks>
/// 参照:
/// SDK avatar performance reports always report dynamic bone counts as 0 | Bug Reports | VRChat
/// <https://vrchat.canny.io/bug-reports/p/sdk-avatar-performance-reports-always-report-dynamic-bone-counts-as-0>
/// </remarks>
private static void FixFindDynamicBoneTypesMethodOnAvatarPerformanceClass()
{
string fullPath = UnityPath.FromUnityPath(VRChatUtility.AvatarPerformanceClassPath).FullPath;

string content = File.ReadAllText(path: fullPath, encoding: Encoding.UTF8);
if (content.Contains("DynamicBoneColliderBase"))
{
return;
}

string fixedContent = content.Replace(
oldValue: "System.Type dyBoneColliderType = Validation.GetTypeFromName(\"DynamicBoneCollider\");",
newValue: "System.Type dyBoneColliderType = Validation.GetTypeFromName(\"DynamicBoneColliderBase\");"
);
if (fixedContent == content)
{
return;
}

File.WriteAllText(path: fullPath, contents: fixedContent, encoding: Encoding.UTF8);
}

/// <summary>
/// キャラクターに関する情報を設定します。
/// </summary>
Expand Down
104 changes: 79 additions & 25 deletions Editor/SwayingObjectsConverter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
using System.Linq;
using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEditor;
using VRM;
using UniGLTF;
using VRCSDK2;

namespace Esperecyan.Unity.VRMConverterForVRChat
Expand All @@ -12,6 +17,11 @@ namespace Esperecyan.Unity.VRMConverterForVRChat
/// </summary>
public class SwayingObjectsConverter
{
private static readonly Type DynamicBoneType = Type.GetType("DynamicBone, Assembly-CSharp");
private static readonly Type DynamicBoneColliderType = Type.GetType("DynamicBoneCollider, Assembly-CSharp");
private static readonly Type DynamicBoneColliderBaseListType
= typeof(List<>).MakeGenericType(Type.GetType("DynamicBoneColliderBase, Assembly-CSharp"));

internal static IEnumerable<Converter.Message> Apply(
GameObject avatar,
ComponentsReplacer.SwayingObjectsConverterSetting setting,
Expand All @@ -23,7 +33,7 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
return new List<Converter.Message>();
}

IDictionary<VRMSpringBoneColliderGroup, IEnumerable<DynamicBoneColliderBase>> dynamicBoneColliderGroups = null;
IDictionary<VRMSpringBoneColliderGroup, IEnumerable<MonoBehaviour>> dynamicBoneColliderGroups = null;
if (setting == ComponentsReplacer.SwayingObjectsConverterSetting.ConvertVrmSpringBonesAndVrmSpringBoneColliderGroups)
{
RemoveUnusedColliderGroups(avatar: avatar);
Expand All @@ -38,6 +48,38 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
return GetMessagesAboutDynamicBoneLimits(avatar: avatar);
}

/// <summary>
/// <see cref="AvatarPerformance"/>クラスを書き替えて、DynamicBoneに関するパフォーマンスが表示されないVRChat SDKのバグを修正します。
/// </summary>
/// <seealso cref="SwayingObjectsConverter.GetMessagesAboutDynamicBoneLimits"/>
/// <remarks>
/// 参照:
/// SDK avatar performance reports always report dynamic bone counts as 0 | Bug Reports | VRChat
/// <https://vrchat.canny.io/bug-reports/p/sdk-avatar-performance-reports-always-report-dynamic-bone-counts-as-0>
/// </remarks>
[InitializeOnLoadMethod]
private static void FixFindDynamicBoneTypesMethodOnAvatarPerformanceClass()
{
string fullPath = UnityPath.FromUnityPath(VRChatUtility.AvatarPerformanceClassPath).FullPath;

string content = File.ReadAllText(path: fullPath, encoding: Encoding.UTF8);
if (content.Contains("DynamicBoneColliderBase"))
{
return;
}

string fixedContent = content.Replace(
oldValue: "System.Type dyBoneColliderType = Validation.GetTypeFromName(\"DynamicBoneCollider\");",
newValue: "System.Type dyBoneColliderType = Validation.GetTypeFromName(\"DynamicBoneColliderBase\");"
);
if (fixedContent == content)
{
return;
}

File.WriteAllText(path: fullPath, contents: fixedContent, encoding: Encoding.UTF8);
}

/// <summary>
/// <see cref="VRMSpringBone.ColliderGroups"/>から参照されていない<see cref="VRMSpringBoneColliderGroup"/>を削除します。
/// </summary>
Expand All @@ -63,7 +105,7 @@ private static void RemoveUnusedColliderGroups(GameObject avatar)
/// </summary>
/// <param name="avatar"></param>
/// <returns>キーに<see cref="VRMSpringBoneColliderGroup"/>、値に対応する<see cref="DynamicBoneCollider"/>のリストを持つジャグ配列。</returns>
private static IDictionary<VRMSpringBoneColliderGroup, IEnumerable<DynamicBoneColliderBase>> ConvertVRMSpringBoneColliderGroups(GameObject avatar)
private static IDictionary<VRMSpringBoneColliderGroup, IEnumerable<MonoBehaviour>> ConvertVRMSpringBoneColliderGroups(GameObject avatar)
{
return avatar.GetComponentsInChildren<VRMSpringBoneColliderGroup>().ToDictionary(
keySelector: colliderGroup => colliderGroup,
Expand All @@ -77,18 +119,18 @@ private static IDictionary<VRMSpringBoneColliderGroup, IEnumerable<DynamicBoneCo
/// <param name="colliderGroup"></param>
/// <param name="bonesForCollisionWithOtherAvatar"></param>
/// <returns>生成した<see cref="DynamicBoneCollider"/>のリスト。</returns>
private static IEnumerable<DynamicBoneColliderBase> ConvertVRMSpringBoneColliderGroup(
private static IEnumerable<MonoBehaviour> ConvertVRMSpringBoneColliderGroup(
VRMSpringBoneColliderGroup colliderGroup
)
{
var bone = colliderGroup.gameObject;

return colliderGroup.Colliders.Select(collider => {
var dynamicBoneCollider = bone.AddComponent<DynamicBoneCollider>();
dynamicBoneCollider.m_Center = collider.Offset;
dynamicBoneCollider.m_Radius = collider.Radius;
return dynamicBoneCollider as DynamicBoneColliderBase;
}).ToList();
var dynamicBoneCollider = bone.AddComponent(DynamicBoneColliderType);
DynamicBoneColliderType.GetField("m_Center").SetValue(dynamicBoneCollider, collider.Offset);
DynamicBoneColliderType.GetField("m_Radius").SetValue(dynamicBoneCollider, collider.Radius);
return dynamicBoneCollider as MonoBehaviour;
});
}

/// <summary>
Expand All @@ -99,7 +141,7 @@ VRMSpringBoneColliderGroup colliderGroup
/// <param name="swayingParametersConverter"></param>
private static void ConvertVRMSpringBones(
GameObject avatar,
IDictionary<VRMSpringBoneColliderGroup, IEnumerable<DynamicBoneColliderBase>> dynamicBoneColliderGroups,
IDictionary<VRMSpringBoneColliderGroup, IEnumerable<MonoBehaviour>> dynamicBoneColliderGroups,
ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
)
{
Expand All @@ -117,7 +159,7 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
/// <param name="swayingParametersConverter"></param>
private static void ConvertVRMSpringBone(
VRMSpringBone springBone,
IDictionary<VRMSpringBoneColliderGroup, IEnumerable<DynamicBoneColliderBase>> dynamicBoneColliderGroups,
IDictionary<VRMSpringBoneColliderGroup, IEnumerable<MonoBehaviour>> dynamicBoneColliderGroups,
ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
)
{
Expand All @@ -126,9 +168,9 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter

foreach (var transform in springBone.RootBones)
{
var dynamicBone = springBone.gameObject.AddComponent<DynamicBone>();
dynamicBone.m_Root = transform;
dynamicBone.m_Exclusions = new List<Transform>();
var dynamicBone = springBone.gameObject.AddComponent(DynamicBoneType);
DynamicBoneType.GetField("m_Root").SetValue(dynamicBone, transform);
DynamicBoneType.GetField("m_Exclusions").SetValue(dynamicBone, new List<Transform>());

DynamicBoneParameters dynamicBoneParameters = null;
if (swayingParametersConverter != null)
Expand All @@ -140,21 +182,33 @@ ComponentsReplacer.SwayingParametersConverter swayingParametersConverter
}
if (dynamicBoneParameters != null)
{
dynamicBone.m_Damping = dynamicBoneParameters.Damping;
dynamicBone.m_DampingDistrib = dynamicBoneParameters.DampingDistrib;
dynamicBone.m_Elasticity = dynamicBoneParameters.Elasticity;
dynamicBone.m_ElasticityDistrib = dynamicBoneParameters.ElasticityDistrib;
dynamicBone.m_Stiffness = dynamicBoneParameters.Stiffness;
dynamicBone.m_StiffnessDistrib = dynamicBoneParameters.StiffnessDistrib;
dynamicBone.m_Inert = dynamicBoneParameters.Inert;
dynamicBone.m_InertDistrib = dynamicBoneParameters.InertDistrib;
DynamicBoneType.GetField("m_Damping").SetValue(dynamicBone, dynamicBoneParameters.Damping);
DynamicBoneType.GetField("m_DampingDistrib")
.SetValue(dynamicBone, dynamicBoneParameters.DampingDistrib);
DynamicBoneType.GetField("m_Elasticity").SetValue(dynamicBone, dynamicBoneParameters.Elasticity);
DynamicBoneType.GetField("m_ElasticityDistrib")
.SetValue(dynamicBone, dynamicBoneParameters.ElasticityDistrib);
DynamicBoneType.GetField("m_Stiffness").SetValue(dynamicBone, dynamicBoneParameters.Stiffness);
DynamicBoneType.GetField("m_StiffnessDistrib")
.SetValue(dynamicBone, dynamicBoneParameters.StiffnessDistrib);
DynamicBoneType.GetField("m_Inert").SetValue(dynamicBone, dynamicBoneParameters.Inert);
DynamicBoneType.GetField("m_InertDistrib")
.SetValue(dynamicBone, dynamicBoneParameters.InertDistrib);
}

dynamicBone.m_Gravity = springBone.m_gravityDir * springBone.m_gravityPower;
DynamicBoneType.GetField("m_Gravity")
.SetValue(dynamicBone, springBone.m_gravityDir * springBone.m_gravityPower);
if (dynamicBoneColliderGroups != null)
{
dynamicBone.m_Colliders = new List<DynamicBoneColliderBase>();
dynamicBone.m_Colliders.AddRange(springBone.ColliderGroups.SelectMany(colliderGroup => dynamicBoneColliderGroups[colliderGroup]).ToList());
var colliders = Activator.CreateInstance(type: DynamicBoneColliderBaseListType);
MethodInfo addMethod = DynamicBoneColliderBaseListType.GetMethod("Add");
foreach (var collider in springBone.ColliderGroups.SelectMany(
colliderGroup => dynamicBoneColliderGroups[colliderGroup]
))
{
addMethod.Invoke(colliders, new[] { collider });
}
DynamicBoneType.GetField("m_Colliders").SetValue(dynamicBone, colliders);
}
}
}
Expand Down

0 comments on commit 13685b4

Please sign in to comment.