Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using KRT.VRCQuestTools.Components;
Expand Down Expand Up @@ -348,11 +349,17 @@ private AvatarPerformanceStats EstimatePerformanceStats(AvatarConverterSettings
{
return null;
}
var pbToKeep = converterSettings.physBonesToKeep.Where(x => x != null).ToArray();
var avatar = new VRChatAvatar(original);

// Map saved PhysBones to providers to properly account for MergePhysBone
var allProviders = avatar.GetPhysBoneProviders();
var savedPbs = new HashSet<VRCPhysBone>(converterSettings.physBonesToKeep.Where(x => x != null));
var providersToKeep = allProviders.Where(p =>
p.GetPhysBones().Any(pb => savedPbs.Contains(pb))).ToArray();

var pbcToKeep = converterSettings.physBoneCollidersToKeep.Where(x => x != null).ToArray();
var contactsToKeep = converterSettings.contactsToKeep.Where(x => x != null).ToArray();
var avatar = new VRChatAvatar(original);
return avatar.EstimatePerformanceStats(pbToKeep, pbcToKeep, contactsToKeep);
return avatar.EstimatePerformanceStats(providersToKeep, pbcToKeep, contactsToKeep);
}

private void AvatarDynamicsPerformanceGUI(AvatarPerformanceStats stats)
Expand Down Expand Up @@ -439,7 +446,14 @@ private void OnClickSelectAvatarDynamicsComponentsButton(VRC_AvatarDescriptor av
{
var window = EditorWindow.GetWindow<AvatarDynamicsSelectorWindow>();
window.converterSettings = converterSettings;
window.physBoneProvidersToKeep = converterSettings.physBonesToKeep.Select(p => new VRCPhysBoneProvider(p)).ToArray();

// Map saved PhysBones to current providers to properly handle MergePhysBone
var avatarObj = new VRChatAvatar(avatar);
var allProviders = avatarObj.GetPhysBoneProviders();
var savedPbs = new HashSet<VRCPhysBone>(converterSettings.physBonesToKeep.Where(p => p != null));
window.physBoneProvidersToKeep = allProviders.Where(p =>
p.GetPhysBones().Any(pb => savedPbs.Contains(pb))).ToArray();

window.physBoneCollidersToKeep = converterSettings.physBoneCollidersToKeep;
window.contactsToKeep = converterSettings.contactsToKeep;
window.Show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal static class AvatarDynamics
{
/// <summary>
/// Calculate performance stats for Avatar Dynamics.
/// Each provider counts as 1 PhysBone component (e.g., MergePhysBone counts as 1).
/// </summary>
/// <param name="root">Avatar root object (VRCAvatarDescriptor).</param>
/// <param name="physbones">PhysBone providers.</param>
Expand All @@ -25,8 +26,13 @@ internal static PerformanceStats CalculatePerformanceStats(
VRCPhysBoneCollider[] colliders,
ContactBase[] contacts)
{
var vrcPhysBones = physbones.Select(pb => pb.Component as VRCPhysBone).Where(pb => pb != null).ToArray();
return CalculatePerformanceStats(root, vrcPhysBones, colliders, contacts);
var vrcPhysBones = physbones.SelectMany(pb => pb.GetPhysBones()).Where(pb => pb != null).ToArray();
var stats = CalculatePerformanceStats(root, vrcPhysBones, colliders, contacts);

// Override PhysBonesCount: each provider counts as 1 post-build component
stats.PhysBonesCount = physbones.Count(p => !IsFinallyEditorOnly(root, p.GameObject));

return stats;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,45 @@ internal VRCPhysBone[] GetPhysBones()

/// <summary>
/// Gets PhysBones as providers for abstraction layer.
/// Includes AAO MergePhysBone providers and excludes PhysBones targeted by MergePhysBone.
/// </summary>
/// <returns>All attached PhysBones as providers.</returns>
internal VRCPhysBoneProviderBase[] GetPhysBoneProviders()
{
return AvatarDescriptor.GetComponentsInChildren<VRCPhysBone>(true)
var mergeProviders = new List<AAOMergePhysBoneProvider>();
var mergedPhysBones = new HashSet<VRCPhysBone>();

var reflectionInfo = AAOMergePhysBoneProvider.AAOMergePhysBoneReflectionInfo.Default;
if (reflectionInfo != null)
{
var mergeComponents = AvatarDescriptor.GetComponentsInChildren(reflectionInfo.MergePhysBoneType, true);
foreach (var comp in mergeComponents)
{
try
{
var provider = new AAOMergePhysBoneProvider(comp);
mergeProviders.Add(provider);
foreach (var pb in provider.GetPhysBones())
{
if (pb != null)
{
mergedPhysBones.Add(pb);
}
}
}
catch (ArgumentException)
{
// Skip invalid components
}
}
}

var regularProviders = AvatarDescriptor.GetComponentsInChildren<VRCPhysBone>(true)
.Where(pb => !mergedPhysBones.Contains(pb))
.Select(pb => new VRCPhysBoneProvider(pb))
.Cast<VRCPhysBoneProviderBase>()
.ToArray();
.Cast<VRCPhysBoneProviderBase>();

return regularProviders.Concat(mergeProviders).ToArray();
}

/// <summary>
Expand Down Expand Up @@ -213,8 +244,33 @@ internal AvatarPerformanceStats EstimatePerformanceStats(
ContactBase[] contacts,
bool isMobile = true)
{
var vrcPhysBones = physbones.SelectMany(pb => pb.GetPhysBones()).ToArray();
return EstimatePerformanceStats(vrcPhysBones, colliders, contacts, isMobile);
var stats = VRCSDKUtility.CalculatePerformanceStats(AvatarDescriptor.gameObject, isMobile);
var dynamicsStats = AvatarDynamics.CalculatePerformanceStats(AvatarDescriptor.gameObject, physbones, colliders, contacts);
stats.physBone = new AvatarPerformanceStats.PhysBoneStats
{
componentCount = dynamicsStats.PhysBonesCount,
transformCount = dynamicsStats.PhysBonesTransformCount,
colliderCount = dynamicsStats.PhysBonesColliderCount,
collisionCheckCount = dynamicsStats.PhysBonesCollisionCheckCount,
};
stats.contactCount = dynamicsStats.ContactsCount;

if (isMobile)
{
stats.audioSourceCount = null;
stats.clothCount = null;
stats.clothMaxVertices = null;
stats.constraintsCount = null;
stats.downloadSizeBytes = null;
stats.lightCount = null;
stats.physicsColliderCount = null;
stats.physicsRigidbodyCount = null;
stats.textureMegabytes = null;
stats.uncompressedSizeBytes = null;
}

stats.CalculateAllPerformanceRatings(isMobile);
return stats;
}

/// <summary>
Expand Down