Skip to content

Commit

Permalink
Merge pull request #1308 from anatawa12/auto-merge-smr-blendhsape
Browse files Browse the repository at this point in the history
feat: Automatically Merge Meshes with BlendShapes
  • Loading branch information
anatawa12 authored Nov 2, 2024
2 parents 55169fc + 5c67775 commit 439ddcf
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 34 deletions.
20 changes: 10 additions & 10 deletions .docs/content/docs/reference/merge-skinned-mesh/index.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ weight: 21

このコンポーネントは、メッシュを指定していないSkinnedMeshRendererコンポーネントがある新規GameObjectに追加してください。(分類: [Source Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#source-component))

{{< hint info >}}

[Trace And Optimize](../trace-and-optimize)が自動で同様の処理を行うため、大抵の場合、このコンポーネントを使用する必要はありません。

{{< /hint >}}

## 利点 {#benefits}

SkinnedMeshRendererを統合することでメッシュを変形させる処理の回数が減り、負荷が軽くなります。
Expand All @@ -27,19 +33,13 @@ Anchor Override等の設定を行うには、MergeSkinnedMeshのあるGameObject

{{< /hint >}}

また、このコンポーネントは、服のメッシュや体のメッシュを統合するのには適していますが、顔のメッシュを統合するのには適していません。\
BlendShapeは、頂点とBlendShapeの数に比例して負荷が大きくなる機能です。
顔のメッシュは一般的に多くのBlendShapeを持っているため、統合対象に含めると頂点数の増加により負荷が大きくなってしまいます。

同様に、体や服のメッシュのBlendShapeは固定・除去することを推奨します。
BlendShapeによる負荷を減らすために、体や服のメッシュのBlendShapeは固定・除去することを推奨します。\
[Freeze BlendShape](../freeze-blendshape)コンポーネントを統合対象・統合先のSkinnedMeshRendererコンポーネントのいずれか(または両方)に追加して、BlendShapeを固定・除去することが出来ます。
[Trace and Optimize](../trace-and-optimize)コンポーネントの`BlendShapeを最適化する`によっても同様の効果を得ることが出来ます。

{{< hint info >}}

いくつかのケースでは、[Trace And Optimize](../trace-and-optimize)が自動で同様の処理を行うため、このコンポーネントを使用する必要がないかもしれません。

{{< /hint >}}
以前のAvatar Optimizerは顔のメッシュを他のメッシュと統合することを推奨していませんでした。
これは、Unity 2019でBlendShapeの多いメッシュを統合するとメッシュの負荷が大幅に増加してしまうためです。\
Unity 2022ではBlendShapeの負荷が改善されているため、その記述は取り下げられました。

## 設定 {#settings}

Expand Down
20 changes: 9 additions & 11 deletions .docs/content/docs/reference/merge-skinned-mesh/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ Merges one or more SkinnedMeshRenderers and MeshRenderers into one SkinnedMeshRe

This component should be added to a new GameObject which has a SkinnedMeshRenderer component without Mesh specified. (Kind: [Source Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#source-component))

{{< hint info >}}

[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in most cases you do not need to use this component.

{{< /hint >}}

## Benefits

Merging SkinnedMeshRenderer will reduce number of deforming mesh (skinning).
Expand All @@ -27,21 +33,13 @@ If you are using [Modular Avatar], you can add [`MA Mesh Settings`] component to

{{< /hint >}}

This component is good for merging your cloth meshes and body meshes but not good for face meshes because BlendShape can cause performance impact.
BlendShape is a feature became heavier in proportion to the count of vertices and BlendShapes.
Merging SkinnedMesh increases vertices and face mesh usually have many BlendShapes.
That's why it's not good to merge face meshes.

In addition, because of same reasons, you should freeze & remove unchanging BlendShapes for body / cloth meshes.
It's better to freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load.\
You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component.
Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes.
Also, you can use `Optimize BlendShapes` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits.

{{< hint info >}}

[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in some cases you do not need to use this component.

{{< /hint >}}
In previous versions of Avatar Optimizer, we recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much in Unity 2019.\
However, in Unity 2022, we no longer recommends not merging face meshes because the BlendShape load has been improved.

## Settings

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ The format is based on [Keep a Changelog].
- This mode will try to correct roll axis by rotating bone.
- This feature allows you to configure the mode for PhysBone Limits in Merge PhysBone.
- This is useful if all configuration is same but roll axis is different.
- Automatically merging meshes which have BlendShapes `#1308`
- In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged.
- This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019.
- However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices.
- This means that increasing the number of vertices in a mesh which has BlendShapes does not increase the load of BlendShape manipulation much.
- Therefore, we decided to automatically merge such meshes.

### Changed

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ The format is based on [Keep a Changelog].
- This mode will try to correct roll axis by rotating bone.
- This feature allows you to configure the mode for PhysBone Limits in Merge PhysBone.
- This is useful if all configuration is same but roll axis is different.
- Automatically merging meshes which have BlendShapes `#1308`
- In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged.
- This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019.
- However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices.
- This means that increasing the number of vertices in a mesh which has BlendShapes does not increase the load of BlendShape manipulation much.
- Therefore, we decided to automatically merge such meshes.

### Changed
- Skip Enablement Mismatched Renderers is now disabled by default `#1169`
Expand Down
34 changes: 21 additions & 13 deletions Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ public static List<MeshInfo2> FilterMergeMeshes(BuildContext context, TraceAndOp
}
}

// if MMD World Compatibility is enabled, body mesh will be animated by MMD World
if (state.MmdWorldCompatibility)
{
var mmdBody = context.AvatarRootTransform.Find("Body");
if (mmdBody != null)
{
var mmdBodyRenderer = mmdBody.GetComponent<SkinnedMeshRenderer>();
if (mmdBodyRenderer != null)
{
componentInfos.GetInfo(mmdBodyRenderer)
.DependantEntrypoint
.Add(context.AvatarRootTransform, GCComponentInfo.DependencyType.Normal);
}
}
}

Profiler.EndSample();

Profiler.BeginSample("Collect Merging Targets");
Expand Down Expand Up @@ -78,8 +94,8 @@ public static List<MeshInfo2> FilterMergeMeshes(BuildContext context, TraceAndOp
meshInfo.Bones.Count > 0
// FlattenMultiPassRendering will increase polygon count by VRChat so it's not good for T&O
&& meshInfo.SubMeshes.All(x => x.SharedMaterials.Length == 1)
// Merging Meshes with BlendShapes can increase rendering cost or break the animation
&& meshInfo.BlendShapes.Count == 0
// Since 1.8.0 (targets 2022) we merge meshes with BlendShapes with RenameToAvoidConflict
// && meshInfo.BlendShapes.Count == 0
// Animating renderer is not supported by this optimization
&& !IsAnimatedForbidden(context.GetAnimationComponent(meshRenderer))
// any other components are not supported
Expand Down Expand Up @@ -415,12 +431,10 @@ private static (Activeness, EqualsHashSet<(bool initial, EqualsHashSet<Animation
{
if (property == Props.EnabledFor(typeof(SkinnedMeshRenderer))) continue; // m_Enabled is proceed separatedly
if (!node.ComponentNodes.Any()) continue; // skip empty nodes, likely means PPtr animation
if (property.StartsWith("blendShape.", StringComparison.Ordinal)) continue; // blendShapes are renamed so we don't need to collect animation location
if (node.ComponentNodes.Any(x => x is not AnimatorParsersV2.AnimatorPropModNode<AnimatorParsersV2.FloatValueInfo>))
return null;

if (property.StartsWith("blendShape.", StringComparison.Ordinal))
continue;

float? defaultValue;
if (node.ApplyState == AnimatorParsersV2.ApplyState.Always)
{
Expand Down Expand Up @@ -634,8 +648,8 @@ private static bool IsAnimatedForbidden(AnimationComponentInfo<PropertyInfo> com
// Note: when you added some other allowed properties,
// You have to add default value handling in GetDefaultValue below

// blendShapes are removed so it's allowed
if (name.StartsWith("blendShapes.", StringComparison.Ordinal)) continue;
// blendShapes are renamed to avoid conflict, so it's allowed
if (name.StartsWith("blendShape.", StringComparison.Ordinal)) continue;
// material properties are allowed, will be merged if animated similarly
if (name.StartsWith("material.", StringComparison.Ordinal)) continue;
// other float properties are forbidden
Expand Down Expand Up @@ -684,12 +698,6 @@ private static bool IsAnimatedForbidden(AnimationComponentInfo<PropertyInfo> com
}
}

if (property.StartsWith("blendShapes.", StringComparison.Ordinal))
{
var blendShapeName = property.Substring("blendShapes.".Length);
return meshInfo.BlendShapes.FirstOrDefault(x => x.name == blendShapeName).weight;
}

throw new InvalidOperationException($"AAO forgot to implement handling for {property}");
}

Expand Down

0 comments on commit 439ddcf

Please sign in to comment.