Skip to content

Commit

Permalink
Merge pull request #656 from anatawa12/split-componentinfo-of-vrcsdk
Browse files Browse the repository at this point in the history
Split ComponentInfo files for VRCSDK and other external libraries
  • Loading branch information
anatawa12 authored Oct 29, 2023
2 parents 90ccee8 + 96efea7 commit 3fbcacb
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 244 deletions.
29 changes: 24 additions & 5 deletions Editor/APIInternal/ComponentInfoRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Anatawa12.AvatarOptimizer.APIInternal.Externals;
using UnityEditor;
using UnityEngine;

Expand Down Expand Up @@ -64,11 +65,29 @@ private static void LoadType(Type type, ComponentInformationAttributeBase attrib

if (!informationType.IsAssignableFrom(type))
throw new Exception("Unsupported type : Not Extends from ComponentInformation<Target>");
if (InformationByType.ContainsKey(targetType))
throw new Exception($"Target Type duplicated: {targetType}");

var instance = (ComponentInformation)System.Activator.CreateInstance(type);
InformationByType.Add(targetType, instance);
if (InformationByType.TryGetValue(targetType, out var existing))
{
if (existing is IExternalMarker)
{
// if existing is fallback, use new one
var instance = (ComponentInformation)System.Activator.CreateInstance(type);
InformationByType[targetType] = instance;
}
else if (typeof(IExternalMarker).IsAssignableFrom(targetType))
{
// if adding is fallback, use existing one
}
else
{
// otherwise, in other words, If both are not fallback, throw exception
throw new Exception($"Target Type duplicated: {targetType}");
}
}
else
{
var instance = (ComponentInformation)System.Activator.CreateInstance(type);
InformationByType.Add(targetType, instance);
}
}

internal static bool TryGetInformation(Type type, out ComponentInformation information) =>
Expand Down
102 changes: 102 additions & 0 deletions Editor/APIInternal/ComponentInfos.Externals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System.Collections.Generic;
using Anatawa12.AvatarOptimizer.API;
using UnityEngine;

namespace Anatawa12.AvatarOptimizer.APIInternal.Externals
{
/// <summary>
/// Marker interface for fallback ComponentInformation(s) for external library.
///
/// Thanks to this interface, upstream author can register those component types without causing duplication error
/// and override information by Avatar Optimizer.
/// </summary>
internal interface IExternalMarker
{
}

#region Dynamic Bones

[ComponentInformationWithGUID("f9ac8d30c6a0d9642a11e5be4c440740", 11500000)]
internal class DynamicBoneInformation : ComponentInformation<Component>, IExternalMarker
{
protected override void CollectDependency(Component component, ComponentDependencyCollector collector)
{
collector.MarkBehaviour();

foreach (var transform in GetAffectedTransforms(component))
{
collector.AddDependency(transform, component)
.EvenIfDependantDisabled()
.OnlyIfTargetCanBeEnable();
collector.AddDependency(transform);
}

foreach (var collider in (IReadOnlyList<MonoBehaviour>)((dynamic)component).m_Colliders)
{
// DynamicBone ignores enabled/disabled of Collider Component AFAIK
collector.AddDependency(collider);
}
}

protected override void CollectMutations(Component component, ComponentMutationsCollector collector)
{
foreach (var transform in GetAffectedTransforms(component))
collector.TransformRotation(transform);
}

private static IEnumerable<Transform> GetAffectedTransforms(dynamic dynamicBone)
{
var ignores = new HashSet<Transform>(dynamicBone.m_Exclusions);
var queue = new Queue<Transform>();
Transform root = dynamicBone.m_Root;
queue.Enqueue(root ? root : (Transform)dynamicBone.transform);

while (queue.Count != 0)
{
var transform = queue.Dequeue();
yield return transform;

foreach (var child in transform.DirectChildrenEnumerable())
if (!ignores.Contains(child))
queue.Enqueue(child);
}
}
}

[ComponentInformationWithGUID("baedd976e12657241bf7ff2d1c685342", 11500000)]
internal class DynamicBoneColliderInformation : ComponentInformation<Component>, IExternalMarker
{
protected override void CollectDependency(Component component, ComponentDependencyCollector collector)
{
}
}

#endregion

#region Satania's KiseteneEx Components

[ComponentInformationWithGUID("e78466b6bcd24e5409dca557eb81d45b", 11500000)] // KiseteneComponent
[ComponentInformationWithGUID("7f9c3fe1cfb9d1843a9dc7da26352ce2", 11500000)] // FlyAvatarSetupTool
[ComponentInformationWithGUID("95f6e1368d803614f8a351322ab09bac", 11500000)] // BlendShapeOverrider
internal class SataniaKiseteneExComponents : ComponentInformation<Component>, IExternalMarker
{
protected override void CollectDependency(Component component, ComponentDependencyCollector collector)
{
}
}

#endregion


#region VRCQuestTools

[ComponentInformationWithGUID("f055e14e1beba894ea68aedffde8ada6", 11500000)] // VertexColorRemover
internal class VRCQuestToolsComponents : ComponentInformation<Component>, IExternalMarker
{
protected override void CollectDependency(Component component, ComponentDependencyCollector collector)
{
}
}

#endregion
}
3 changes: 3 additions & 0 deletions Editor/APIInternal/ComponentInfos.Externals.cs.meta

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

170 changes: 170 additions & 0 deletions Editor/APIInternal/ComponentInfos.VRCSDK.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#if AAO_VRCSDK3_AVATARS
using System;
using System.Collections.Generic;
using Anatawa12.AvatarOptimizer.API;
using UnityEngine;
using VRC.SDK3;
using VRC.Core;
using VRC.Dynamics;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Dynamics.Contact.Components;
using VRC.SDK3.Dynamics.PhysBone.Components;
using VRC.SDKBase;

namespace Anatawa12.AvatarOptimizer.APIInternal.VRCSDK
{
[ComponentInformation(typeof(VRCTestMarker))]
#pragma warning disable CS0618
[ComponentInformation(typeof(PipelineSaver))]
#pragma warning restore CS0618
[ComponentInformation(typeof(PipelineManager))]
[ComponentInformation(typeof(VRCSpatialAudioSource))]
[ComponentInformation(typeof(VRC_SpatialAudioSource))]
// nadena.dev.ndmf.VRChat.ContextHolder with reflection
internal class EntrypointComponentInformation : ComponentInformation<Component>
{
protected override void CollectDependency(Component component, ComponentDependencyCollector collector)
{
collector.MarkEntrypoint();
}
}

[ComponentInformation(typeof(VRC_AvatarDescriptor))]
internal class VRCAvatarDescriptorInformation<T> : ComponentInformation<T> where T : VRC_AvatarDescriptor
{
protected override void CollectDependency(T component,
ComponentDependencyCollector collector)
{
collector.MarkEntrypoint();
collector.AddDependency(component.GetComponent<PipelineManager>()).EvenIfDependantDisabled();
}
}

[ComponentInformation(typeof(VRCAvatarDescriptor))]
internal class VRCAvatarDescriptorInformation : VRCAvatarDescriptorInformation<VRCAvatarDescriptor>
{
protected override void CollectDependency(VRCAvatarDescriptor component,
ComponentDependencyCollector collector)
{
base.CollectDependency(component, collector);

AddCollider(component.collider_head);
AddCollider(component.collider_torso);
AddCollider(component.collider_footR);
AddCollider(component.collider_footL);
AddCollider(component.collider_handR);
AddCollider(component.collider_handL);
AddCollider(component.collider_fingerIndexL);
AddCollider(component.collider_fingerMiddleL);
AddCollider(component.collider_fingerRingL);
AddCollider(component.collider_fingerLittleL);
AddCollider(component.collider_fingerIndexR);
AddCollider(component.collider_fingerMiddleR);
AddCollider(component.collider_fingerRingR);
AddCollider(component.collider_fingerLittleR);
void AddCollider(VRCAvatarDescriptor.ColliderConfig collider)
{
switch (collider.state)
{
case VRCAvatarDescriptor.ColliderConfig.State.Automatic:
case VRCAvatarDescriptor.ColliderConfig.State.Custom:
collector.AddDependency(collider.transform).EvenIfDependantDisabled();
break;
case VRCAvatarDescriptor.ColliderConfig.State.Disabled:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}

[ComponentInformation(typeof(VRC.SDKBase.VRCStation))]
[ComponentInformation(typeof(VRC.SDK3.Avatars.Components.VRCStation))]
internal class VRCStationInformation : ComponentInformation<VRC.SDKBase.VRCStation>
{
protected override void CollectDependency(VRC.SDKBase.VRCStation component, ComponentDependencyCollector collector)
{
// first, Transform <=> PhysBone
// Transform is used even if the bone is inactive so Transform => PB is always dependency
// PhysBone works only if enabled so PB => Transform is active dependency
collector.MarkEntrypoint();
collector.AddDependency(component.stationEnterPlayerLocation);
collector.AddDependency(component.stationExitPlayerLocation);
collector.AddDependency(component.GetComponentInChildren<Collider>());
}

protected override void CollectMutations(VRC.SDKBase.VRCStation component, ComponentMutationsCollector collector)
{
}
}

[ComponentInformation(typeof(VRCPhysBoneBase))]
[ComponentInformation(typeof(VRCPhysBone))]
internal class VRCPhysBoneInformation : ComponentInformation<VRCPhysBoneBase>
{
protected override void CollectDependency(VRCPhysBoneBase component, ComponentDependencyCollector collector)
{
// first, Transform <=> PhysBone
// Transform is used even if the bone is inactive so Transform => PB is always dependency
// PhysBone works only if enabled so PB => Transform is active dependency
var ignoreTransforms = new HashSet<Transform>(component.ignoreTransforms);
CollectTransforms(component.GetTarget());

void CollectTransforms(Transform bone)
{
collector.AddDependency(bone, component).EvenIfDependantDisabled().OnlyIfTargetCanBeEnable();
collector.AddDependency(bone);
foreach (var child in bone.DirectChildrenEnumerable())
{
if (!ignoreTransforms.Contains(child))
CollectTransforms(child);
}
}

// then, PB => Collider
// in PB, PB Colliders work only if Colliders are enabled
foreach (var physBoneCollider in component.colliders)
collector.AddDependency(physBoneCollider).OnlyIfTargetCanBeEnable();

collector.MarkBehaviour();

// If parameter is not empty, the PB can be required for Animator Parameter so it's Entrypoint Component
// https://github.com/anatawa12/AvatarOptimizer/issues/450
if (!string.IsNullOrEmpty(component.parameter))
collector.MarkEntrypoint();
}

protected override void CollectMutations(VRCPhysBoneBase component, ComponentMutationsCollector collector)
{
foreach (var transform in component.GetAffectedTransforms())
collector.TransformPositionAndRotation(transform);
}
}

[ComponentInformation(typeof(VRCPhysBoneColliderBase))]
[ComponentInformation(typeof(VRCPhysBoneCollider))]
internal class VRCPhysBoneColliderInformation : ComponentInformation<VRCPhysBoneColliderBase>
{
protected override void CollectDependency(VRCPhysBoneColliderBase component,
ComponentDependencyCollector collector)
{
collector.AddDependency(component.rootTransform);
}
}

[ComponentInformation(typeof(ContactBase))]
[ComponentInformation(typeof(ContactReceiver))]
[ComponentInformation(typeof(VRCContactReceiver))]
[ComponentInformation(typeof(ContactSender))]
[ComponentInformation(typeof(VRCContactSender))]
internal class ContactBaseInformation : ComponentInformation<ContactBase>
{
protected override void CollectDependency(ContactBase component, ComponentDependencyCollector collector)
{
collector.MarkEntrypoint();
collector.AddDependency(component.rootTransform);
}
}
}
#endif
3 changes: 3 additions & 0 deletions Editor/APIInternal/ComponentInfos.VRCSDK.cs.meta

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

Loading

0 comments on commit 3fbcacb

Please sign in to comment.