diff --git a/.docs/content/docs/reference/remove-zero-sized-polygon/component.png b/.docs/content/docs/reference/remove-zero-sized-polygon/component.png
new file mode 100644
index 000000000..760d2ba5e
Binary files /dev/null and b/.docs/content/docs/reference/remove-zero-sized-polygon/component.png differ
diff --git a/.docs/content/docs/reference/remove-zero-sized-polygon/index.ja.md b/.docs/content/docs/reference/remove-zero-sized-polygon/index.ja.md
new file mode 100644
index 000000000..61f6e2c56
--- /dev/null
+++ b/.docs/content/docs/reference/remove-zero-sized-polygon/index.ja.md
@@ -0,0 +1,38 @@
+---
+title: Remove Zero Sized Polygon
+weight: 100
+---
+
+# Remove Zero Sized Polygon
+
+面積がゼロのポリゴンを削除します。
+
+このコンポーネントは、SkinnedMeshRendererコンポーネントのあるGameObjectに追加してください。
+
+{{< hint warning >}}
+
+このコンポーネントはビルドの最後の方で実行されるため、[Modifying Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#modifying-component) では**ありません**。
+
+このコンポーネントを[Merge Skinned Mesh](../merge-skinned-mesh)の統合対象となるSkinnedMeshRendererに追加しても効果がありません。
+
+{{< /hint >}}
+
+## 利点 {#benefits}
+
+面積がゼロのポリゴンを削除することで、描画負荷を減らすことができます。
+見た目に影響を与えることはほとんどありません。
+
+## 備考 {#notes}
+
+モデルファイルでのポリゴンの大きさがゼロであっても、シェーダーによって何かを描画していることがあるため、見た目に影響が出ることがあります。
+
+## 設定 {#settings}
+
+今のところ、このコンポーネントに設定項目はありません。
+
+![component.png](component.png)
+
+## 備考 {#notes}
+
+このコンポーネントは[Trace and Optimize](../trace-and-optimize)コンポーネントによって自動的に追加されます。
+このコンポーネントを手動で追加するよりも、Trace and Optimizeを使うことをお勧めします。
diff --git a/.docs/content/docs/reference/remove-zero-sized-polygon/index.md b/.docs/content/docs/reference/remove-zero-sized-polygon/index.md
new file mode 100644
index 000000000..72fda2586
--- /dev/null
+++ b/.docs/content/docs/reference/remove-zero-sized-polygon/index.md
@@ -0,0 +1,39 @@
+---
+title: Remove Zero Sized Polygon
+weight: 100
+---
+
+# Remove Zero Sized Polygon
+
+Remove polygons whose area are zero.
+
+This component should be added to a GameObject which has a SkinnedMeshRenderer component.
+
+{{< hint warning >}}
+
+Since this component works very late in the build process, this component is **NOT** [Modifying Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#modifying-component).
+
+Adding this component to the SkinnedMeshRenderers to be merged by [Merge Skinned Mesh](../merge-skinned-mesh) component has no effect.
+
+{{< /hint >}}
+
+## Benefits
+
+By removing polygons whose area are zero, you can reduce rendering cost.
+This will have almost zero effect on the appearance.
+
+## Notes
+
+In some shaders, even if size of polygon in model file is zero, something can be rendered so
+there may be effect on the appearance.
+
+## Settings
+
+This Component doesn't have any configuration for now.
+
+![component.png](component.png)
+
+## Notes
+
+This component will be added by [Trace and Optimize](../trace-and-optimize) component.
+I recommend you to use Trace and Optimize instead of adding this component manually.
diff --git a/.docs/content/docs/reference/trace-and-optimize/component.png b/.docs/content/docs/reference/trace-and-optimize/component.png
index 3ed90a1cd..cf656f785 100644
Binary files a/.docs/content/docs/reference/trace-and-optimize/component.png and b/.docs/content/docs/reference/trace-and-optimize/component.png differ
diff --git a/.docs/content/docs/reference/trace-and-optimize/index.ja.md b/.docs/content/docs/reference/trace-and-optimize/index.ja.md
index bf9346acf..e25afae91 100644
--- a/.docs/content/docs/reference/trace-and-optimize/index.ja.md
+++ b/.docs/content/docs/reference/trace-and-optimize/index.ja.md
@@ -21,6 +21,8 @@ aliases:
アニメーションなどを走査して、使われていないObject(GameObjectやコンポーネントなど)を自動的に削除します。
- `endボーンを残す`
親が削除されていないendボーン[^endbone]を削除しないようにします。
+- `面積がゼロのポリゴンを自動的に削除する`
+ 面積がゼロのポリゴンを削除します。
また、以下の設定で自動設定を調節できます。
- `MMDワールドとの互換性`
diff --git a/.docs/content/docs/reference/trace-and-optimize/index.md b/.docs/content/docs/reference/trace-and-optimize/index.md
index f2ac51c63..a55ff55d3 100644
--- a/.docs/content/docs/reference/trace-and-optimize/index.md
+++ b/.docs/content/docs/reference/trace-and-optimize/index.md
@@ -21,6 +21,8 @@ Currently the following optimizations are applied automatically.
By scanning animation etc., automatically removes unused Objects (e.g. GameObjects, Components).
- `Preserve EndBone`
Prevents removing end bones[^endbone] whose parent is not removed.
+- `Automatically Remove Zero Sized Polygons`
+ Removes polygons whose area are zero.
Also, You can adjust optimization with the following settings
- `MMD World Compatibility`
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 40f1b1a09..5019306d0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -47,6 +47,14 @@ jobs:
hugo-version: '0.111.3'
extended: true
+ - name: Check release is public
+ if: github.event.inputs.release_kind == 'stable'
+ run:
+ if [[ "$(jq '.private == true' < package.json)" == "true" ]]; then
+ echo "package.json is private"
+ exit 255
+ fi
+
- name: Update Version Name
id: update-version
run: |
diff --git a/API-Editor/ComponentInformation.cs b/API-Editor/ComponentInformation.cs
index 3b1f2ad99..41099d7c9 100644
--- a/API-Editor/ComponentInformation.cs
+++ b/API-Editor/ComponentInformation.cs
@@ -48,7 +48,7 @@ internal sealed override void CollectMutationsInternal(Component component,
ComponentMutationsCollector collector) =>
CollectMutations((TComponent)component, collector);
- internal override void ApplySpecialMappingInternal(Component component, MappingSource collector) =>
+ internal sealed override void ApplySpecialMappingInternal(Component component, MappingSource collector) =>
ApplySpecialMapping((TComponent)component, collector);
///
@@ -207,23 +207,51 @@ internal ComponentMutationsCollector()
{
}
+ ///
+ /// Registers of the will be changed by current component.
+ ///
+ /// The component current component will modifies
+ /// The list of properties current component will modifies
[PublicAPI]
- public abstract void ModifyProperties([NotNull] Component component, [NotNull] IEnumerable properties);
+ public abstract void ModifyProperties([NotNull] Component component,
+ [NotNull] [ItemNotNull] IEnumerable properties);
+ ///
[PublicAPI]
- public void ModifyProperties([NotNull] Component component, [NotNull] string[] properties) =>
- ModifyProperties(component, (IEnumerable) properties);
+ public void ModifyProperties([NotNull] Component component,
+ [NotNull] [ItemNotNull] params string[] properties) =>
+ ModifyProperties(component, (IEnumerable)properties);
}
+ ///
+ /// The class provides object and property replaced by Avatar Optimizer.
+ ///
+ /// Avatar Optimizer may replaces or merges component to another component.
+ /// This class provide the information about the replacement.
+ /// In addition, Avatar Optimizer may replace or merges some properties of the component.
+ /// This class also provide the information about the property replacement.
+ ///
public abstract class MappingSource
{
internal MappingSource()
{
}
+ ///
+ /// Returns about the component instance.
+ /// The instance can be a missing component.
+ ///
+ /// The component to get information about
+ /// The type of component
[PublicAPI]
public abstract MappedComponentInfo GetMappedComponent(T component) where T : Component;
+ ///
+ /// Returns about the GameObject instance.
+ /// The instance can be a missing component.
+ ///
+ /// The component to get information about
+ /// The type of component
[PublicAPI]
public abstract MappedComponentInfo GetMappedGameObject(GameObject component);
}
@@ -237,10 +265,11 @@ internal MappedComponentInfo()
///
/// The mapped component (or GameObject).
/// The component may be removed without mapped component.
- /// If there are not mapped component, this will be null.
+ /// If there are no mapped component, this will be null.
///
- /// Even if the component is removed without mapped component,
- /// each animation property can be mapped to another component.
+ /// Even if the component is removed without mapped component, some animation property can be mapped
+ /// to a property on another component so you should use if the component is highly related
+ /// to animation property, for example, blendShape related SkinnedMeshRenderer.
///
[PublicAPI]
public abstract T MappedComponent { get; }
@@ -249,6 +278,10 @@ internal MappedComponentInfo()
/// Maps animation property name to component and MappedPropertyInfo.
/// If the property is not removed, returns true and is set.
/// If the property is removed, returns false and will be default.
+ ///
+ /// To get mapped property probably, you must register the property as modified property by
+ /// .
+ /// Unless that, renaming or moving the property may not be tracked by Avatar Optimizer.
///
/// The name of property will be mapped
/// The result parameter
@@ -259,9 +292,15 @@ internal MappedComponentInfo()
public readonly struct MappedPropertyInfo
{
+ ///
+ /// The Component or GameObject the property is on.
+ ///
[PublicAPI]
public Object Component { get; }
+ ///
+ /// The name of the mapped property.
+ ///
[PublicAPI]
public string Property { get; }
diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md
index 87fe4466a..7dc9f6ac3 100644
--- a/CHANGELOG-PRERELEASE.md
+++ b/CHANGELOG-PRERELEASE.md
@@ -8,6 +8,9 @@ The format is based on [Keep a Changelog].
## [Unreleased]
### Added
+- Remove Zero Sized Polygons `#659`
+- Add support for UniVRM components `#653`
+- Support for Mesh Topologies other than Triangles `#692`
### Changed
- When you're animating activeness/enablement of source renderers, warning is shown since this release `#675`
@@ -17,6 +20,7 @@ The format is based on [Keep a Changelog].
### Removed
### Fixed
+- proxy animation can be modified `#678`
### Security
@@ -40,6 +44,10 @@ The format is based on [Keep a Changelog].
- Prefab blinks when we see editor of PrefabSafeSet of prefab asset [`#645`](https://github.com/anatawa12/AvatarOptimizer/pull/645) [`#664`](https://github.com/anatawa12/AvatarOptimizer/pull/664)
- Fixes in 1.5.9 [`#654`](https://github.com/anatawa12/AvatarOptimizer/pull/654)
+## [1.5.10] - 2023-11-04
+### Fixed
+- RigidBody Joint can be broken [`#683`](https://github.com/anatawa12/AvatarOptimizer/pull/683)
+
## [1.5.9] - 2023-10-29
## [1.5.9-rc.1] - 2023-10-28
### Fixed
@@ -976,7 +984,8 @@ This release is mistake.
[Unreleased]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.6.0-beta.2...HEAD
[1.6.0-beta.2]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.6.0-beta.1...v1.6.0-beta.2
-[1.6.0-beta.1]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.9...v1.6.0-beta.1
+[1.6.0-beta.1]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.10...v1.6.0-beta.1
+[1.5.10]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.9...v1.5.10
[1.5.9]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.9-rc.1...v1.5.9
[1.5.9-rc.1]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.8...v1.5.9-rc.1
[1.5.8]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.8-rc.1...v1.5.8
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d16f64bd..3408d616d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,18 +8,20 @@ The format is based on [Keep a Changelog].
## [Unreleased]
### Added
-- Public API for registering component information `#632`
+- Public API for registering component information `#632` `#668`
- Disabling PhysBone animation based on mesh renderer enabled animation `#640`
- If you toggles your clothes with simple toggle, PhysBones on the your avatar will also be toggled automatically!
- Small performance improve `#641`
- Ability to prevent changing enablement of component `#668`
+- Remove Zero Sized Polygons `#659`
+- Add support for UniVRM components `#653`
+- Support for Mesh Topologies other than Triangles `#692`
### Changed
- All logs passed to ErrorReport is now shown on the console log `#643`
- Improved Behaviour with multi-material multi pass rendering `#662`
- Previously, multi-material multi pass rendering are flattened.
- Since 1.6, flattened if component doesn't support that.
-- BREAKING API CHANGES: Behaviour components are renamed to HeavyBehaviour `#668`
- When you're animating activeness/enablement of source renderers, warning is shown since this release `#675`
### Deprecated
@@ -36,6 +38,10 @@ The format is based on [Keep a Changelog].
### Security
+## [1.5.10] - 2023-11-04
+### Fixed
+- RigidBody Joint can be broken [`#683`](https://github.com/anatawa12/AvatarOptimizer/pull/683)
+
## [1.5.9] - 2023-10-29
### Fixed
- Animation clip length can be changed [`#647`](https://github.com/anatawa12/AvatarOptimizer/pull/647)
@@ -641,7 +647,8 @@ The format is based on [Keep a Changelog].
- Merge Bone
- Clear Endpoint Position
-[Unreleased]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.9...HEAD
+[Unreleased]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.10...HEAD
+[1.5.10]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.9...v1.5.10
[1.5.9]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.8...v1.5.9
[1.5.8]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.7...v1.5.8
[1.5.7]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.5.6...v1.5.7
diff --git a/Editor/APIInternal/ComponentInfos.VRCSDK.cs b/Editor/APIInternal/ComponentInfos.VRCSDK.cs
index 85a1c5f27..fbb0724af 100644
--- a/Editor/APIInternal/ComponentInfos.VRCSDK.cs
+++ b/Editor/APIInternal/ComponentInfos.VRCSDK.cs
@@ -56,7 +56,7 @@ protected override void CollectMutations(T component, ComponentMutationsCollecto
case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape when component.VisemeSkinnedMesh != null:
{
collector.ModifyProperties(component.VisemeSkinnedMesh,
- new[] { $"blendShape.{component.MouthOpenBlendShapeName}" });
+ $"blendShape.{component.MouthOpenBlendShapeName}");
break;
}
case VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape when component.VisemeSkinnedMesh != null:
diff --git a/Editor/APIInternal/ComponentInfos.VRM0.cs b/Editor/APIInternal/ComponentInfos.VRM0.cs
new file mode 100644
index 000000000..eda2e3945
--- /dev/null
+++ b/Editor/APIInternal/ComponentInfos.VRM0.cs
@@ -0,0 +1,159 @@
+#if AAO_VRM0
+
+using System.Linq;
+using Anatawa12.AvatarOptimizer.API;
+using Anatawa12.AvatarOptimizer.ErrorReporting;
+using UnityEngine;
+using VRM;
+
+namespace Anatawa12.AvatarOptimizer.APIInternal
+{
+
+ // NOTE: VRM0 bones are not animated, therefore no need to configure ComponentDependencyInfo
+
+ [ComponentInformation(typeof(VRMMeta))]
+ internal class VRMMetaInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMMeta component, ComponentDependencyCollector collector)
+ {
+ collector.MarkEntrypoint();
+ }
+ }
+
+ [ComponentInformation(typeof(VRMSpringBone))]
+ internal class VRMSpringBoneInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMSpringBone component, ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ foreach (var transform in component.GetComponentsInChildren()) collector.AddDependency(transform);
+ foreach (var collider in component.ColliderGroups) collector.AddDependency(collider);
+ }
+
+ protected override void CollectMutations(VRMSpringBone component, ComponentMutationsCollector collector)
+ {
+ foreach (var transform in component.GetComponentsInChildren())
+ collector.TransformPositionAndRotation(transform);
+ }
+ }
+
+ [ComponentInformation(typeof(VRMSpringBoneColliderGroup))]
+ internal class VRMSpringBoneColliderGroupInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMSpringBoneColliderGroup component,
+ ComponentDependencyCollector collector)
+ {
+ }
+ }
+
+ [ComponentInformation(typeof(VRMBlendShapeProxy))]
+ internal class VRMBlendShapeProxyInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMBlendShapeProxy component, ComponentDependencyCollector collector)
+ {
+ var avatarRootTransform = component.transform;
+
+ collector.MarkHeavyBehaviour();
+ foreach (var clip in component.BlendShapeAvatar.Clips)
+ {
+ foreach (var binding in clip.Values)
+ {
+ var target = avatarRootTransform.Find(binding.RelativePath);
+ collector.AddDependency(target, component);
+ collector.AddDependency(target);
+ }
+ foreach (var materialBinding in clip.MaterialValues)
+ {
+ // TODO: I don't know what to do with BlendShape materials, so I pretend material names does not change (ex. MergeToonLitMaterial)
+ }
+ }
+ }
+ }
+
+ [ComponentInformation(typeof(VRMLookAtHead))]
+ internal class VRMLookAtHeadInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMLookAtHead component, ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.Head, component);
+ collector.AddDependency(component.Head);
+ }
+ }
+
+ [ComponentInformation(typeof(VRMLookAtBoneApplyer))]
+ internal class VRMLookAtBoneApplyerInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMLookAtBoneApplyer component, ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.GetComponent());
+ collector.AddDependency(component.LeftEye.Transform);
+ collector.AddDependency(component.RightEye.Transform);
+ }
+
+ protected override void CollectMutations(VRMLookAtBoneApplyer component, ComponentMutationsCollector collector)
+ {
+ collector.TransformRotation(component.LeftEye.Transform);
+ collector.TransformRotation(component.RightEye.Transform);
+ }
+ }
+
+
+ [ComponentInformation(typeof(VRMLookAtBlendShapeApplyer))]
+ internal class VRMLookAtBlendShapeApplyerInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMLookAtBlendShapeApplyer component, ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.GetComponent());
+ }
+
+ }
+
+
+ [ComponentInformation(typeof(VRMFirstPerson))]
+ internal class VRMFirstPersonInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRMFirstPerson component, ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.FirstPersonBone, component);
+ collector.AddDependency(component.FirstPersonBone);
+ }
+
+ protected override void ApplySpecialMapping(VRMFirstPerson component, MappingSource mappingSource)
+ {
+ component.Renderers = component.Renderers
+ .Select(r => new VRMFirstPerson.RendererFirstPersonFlags
+ {
+ Renderer = mappingSource.GetMappedComponent(r.Renderer).MappedComponent,
+ FirstPersonFlag = r.FirstPersonFlag
+ })
+ .Where(r => r.Renderer)
+ .GroupBy(r => r.Renderer, r => r.FirstPersonFlag)
+ .Select(grouping =>
+ {
+ FirstPersonFlag mergedFirstPersonFlag;
+ var firstPersonFlags = grouping.Distinct().ToArray();
+ if (firstPersonFlags.Length == 1)
+ {
+ mergedFirstPersonFlag = firstPersonFlags[0];
+ }
+ else
+ {
+ mergedFirstPersonFlag = firstPersonFlags.Contains(FirstPersonFlag.Both) ? FirstPersonFlag.Both : FirstPersonFlag.Auto;
+ BuildReport.LogWarning("MergeSkinnedMesh:warning:VRM:FirstPersonFlagsMismatch", mergedFirstPersonFlag.ToString());
+ }
+
+ return new VRMFirstPerson.RendererFirstPersonFlags
+ {
+ Renderer = grouping.Key,
+ FirstPersonFlag = mergedFirstPersonFlag
+ };
+ }).ToList();
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Editor/APIInternal/ComponentInfos.VRM0.cs.meta b/Editor/APIInternal/ComponentInfos.VRM0.cs.meta
new file mode 100644
index 000000000..8f4538747
--- /dev/null
+++ b/Editor/APIInternal/ComponentInfos.VRM0.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 82842faa704e42cca084ea181df31a3e
+timeCreated: 1698426374
\ No newline at end of file
diff --git a/Editor/APIInternal/ComponentInfos.VRM1.cs b/Editor/APIInternal/ComponentInfos.VRM1.cs
new file mode 100644
index 000000000..df82e6a59
--- /dev/null
+++ b/Editor/APIInternal/ComponentInfos.VRM1.cs
@@ -0,0 +1,199 @@
+#if AAO_VRM1
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Anatawa12.AvatarOptimizer.API;
+using UniGLTF.Extensions.VRMC_vrm;
+using UnityEngine;
+using UniVRM10;
+using Humanoid = UniHumanoid.Humanoid;
+
+namespace Anatawa12.AvatarOptimizer.APIInternal
+{
+
+ // NOTE: VRM1 bones are not animated their enabled states, therefore no need to configure ComponentDependencyInfo
+
+ [ComponentInformation(typeof(Vrm10Instance))]
+ internal class Vrm10InstanceInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Vrm10Instance component, ComponentDependencyCollector collector)
+ {
+ var avatarRootTransform = component.transform;
+
+ collector.MarkEntrypoint();
+
+ // SpringBones
+
+ foreach (var spring in component.SpringBone.Springs)
+ {
+ foreach (var joint in spring.Joints) collector.AddDependency(joint);
+ foreach (var collider in spring.ColliderGroups) collector.AddDependency(collider);
+ }
+
+ // Expressions
+
+ foreach (var clip in component.Vrm.Expression.Clips.Select(c => c.Clip))
+ {
+ foreach (var binding in clip.MorphTargetBindings)
+ {
+ var target = avatarRootTransform.Find(binding.RelativePath);
+ collector.AddDependency(target, component);
+ collector.AddDependency(target);
+ }
+ foreach (var materialUVBinding in clip.MaterialUVBindings)
+ {
+ // TODO: I don't know what to do with BlendShape materials, so I pretend material names does not change (ex. MergeToonLitMaterial)
+ }
+ foreach (var materialColorBinding in clip.MaterialColorBindings)
+ {
+ // TODO: I don't know what to do with BlendShape materials, so I pretend material names does not change (ex. MergeToonLitMaterial)
+ }
+ }
+
+ // First Person and LookAt
+ // NOTE: these dependencies are satisfied by either Animator or Humanoid
+ // collector.AddDependency(GetBoneTransformForVrm10(component, HumanBodyBones.Head));
+
+ // if (component.Vrm.LookAt.LookAtType == LookAtType.bone)
+ // {
+ // if (GetBoneTransformForVrm10(component, HumanBodyBones.LeftEye) is Transform leftEye)
+ // {
+ // collector.AddDependency(leftEye);
+ // }
+ // if (GetBoneTransformForVrm10(component, HumanBodyBones.RightEye) is Transform rightEye)
+ // {
+ // collector.AddDependency(rightEye);
+ // }
+ // }
+ }
+
+ protected override void CollectMutations(Vrm10Instance component, ComponentMutationsCollector collector)
+ {
+ // SpringBones
+ foreach (var joint in component.SpringBone.Springs.SelectMany(spring => spring.Joints))
+ {
+ collector.TransformPositionAndRotation(joint.transform);
+ }
+
+ // Expressions
+
+ // First Person and LookAt
+ if (component.Vrm.LookAt.LookAtType == LookAtType.bone)
+ {
+ if (GetBoneTransformForVrm10(component, HumanBodyBones.LeftEye) is Transform leftEye)
+ {
+ collector.TransformRotation(leftEye);
+ }
+ if (GetBoneTransformForVrm10(component, HumanBodyBones.RightEye) is Transform rightEye)
+ {
+ collector.TransformRotation(rightEye);
+ }
+ }
+ }
+
+ Transform GetBoneTransformForVrm10(Vrm10Instance component, HumanBodyBones bones)
+ {
+ if (component.GetComponent() is Humanoid avatarHumanoid)
+ {
+ return avatarHumanoid.GetBoneTransform(bones);
+ }
+
+ if (component.GetComponent() is Animator avatarAnimator)
+ {
+ return avatarAnimator.GetBoneTransform(bones);
+ }
+
+ return null;
+ }
+ }
+
+ [ComponentInformation(typeof(VRM10SpringBoneColliderGroup))]
+ internal class Vrm10SpringBoneColliderGroupInformation : ComponentInformation
+ {
+ protected override void CollectDependency(VRM10SpringBoneColliderGroup component,
+ ComponentDependencyCollector collector)
+ {
+ foreach (var collider in component.Colliders) collector.AddDependency(collider);
+ }
+ }
+
+ [ComponentInformation(typeof(VRM10SpringBoneJoint))]
+ [ComponentInformation(typeof(VRM10SpringBoneCollider))]
+ internal class Vrm10ReferenceHolderInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Component component,
+ ComponentDependencyCollector collector)
+ {
+ }
+ }
+
+ [ComponentInformation(typeof(Vrm10AimConstraint))]
+ internal class Vrm10AimConstraintInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Vrm10AimConstraint component,
+ ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.transform, component.Source);
+ }
+
+ protected override void CollectMutations(Vrm10AimConstraint component, ComponentMutationsCollector collector)
+ {
+ collector.TransformRotation(component.transform);
+ }
+ }
+
+ [ComponentInformation(typeof(Vrm10RollConstraint))]
+ internal class Vrm10RollConstraintInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Vrm10RollConstraint component,
+ ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.transform, component.Source);
+ }
+
+ protected override void CollectMutations(Vrm10RollConstraint component, ComponentMutationsCollector collector)
+ {
+ collector.TransformRotation(component.transform);
+ }
+ }
+
+ [ComponentInformation(typeof(Vrm10RotationConstraint))]
+ internal class Vrm10RotationConstraintInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Vrm10RotationConstraint component,
+ ComponentDependencyCollector collector)
+ {
+ collector.MarkHeavyBehaviour();
+ collector.AddDependency(component.transform, component.Source);
+ }
+
+ protected override void CollectMutations(Vrm10RotationConstraint component, ComponentMutationsCollector collector)
+ {
+ collector.TransformRotation(component.transform);
+ }
+ }
+
+ [ComponentInformation(typeof(Humanoid))]
+ internal class HumanoidInformation : ComponentInformation
+ {
+ protected override void CollectDependency(Humanoid component, ComponentDependencyCollector collector)
+ {
+ // VRM1 Humanoid has side effect because it overwrites Animator's Avatar on VRM1 export
+ collector.MarkEntrypoint();
+
+ // Use reflection to support UniVRM 0.99.4
+ var boneMapProperty = typeof(Humanoid).GetProperty("BoneMap", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ var boneMap = (IEnumerable<(Transform, HumanBodyBones)>)boneMapProperty.GetValue(component);
+ foreach ((Transform transform, HumanBodyBones) bone in boneMap)
+ {
+ if (bone.transform) collector.AddDependency(bone.transform);
+ }
+ }
+ }
+
+}
+
+#endif
\ No newline at end of file
diff --git a/Editor/APIInternal/ComponentInfos.VRM1.cs.meta b/Editor/APIInternal/ComponentInfos.VRM1.cs.meta
new file mode 100644
index 000000000..ff06b75c8
--- /dev/null
+++ b/Editor/APIInternal/ComponentInfos.VRM1.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: cda357a894554e8bb67cb0fd3cd02659
+timeCreated: 1698673843
\ No newline at end of file
diff --git a/Editor/APIInternal/ComponentInfos.cs b/Editor/APIInternal/ComponentInfos.cs
index 6151e8b59..c8f387b27 100644
--- a/Editor/APIInternal/ComponentInfos.cs
+++ b/Editor/APIInternal/ComponentInfos.cs
@@ -266,8 +266,17 @@ internal class JointInformation : ComponentInformation
{
protected override void CollectDependency(Joint component, ComponentDependencyCollector collector)
{
- collector.AddDependency(component.GetComponent(), component);
- collector.AddDependency(component.connectedBody);
+ var rigidBody = component.GetComponent();
+ if (rigidBody)
+ {
+ collector.AddDependency(rigidBody, component);
+ collector.AddDependency(rigidBody);
+ }
+ if (component.connectedBody)
+ {
+ collector.AddDependency(component.connectedBody, component);
+ collector.AddDependency(component.connectedBody);
+ }
}
}
@@ -404,6 +413,19 @@ void DeriveMergeSkinnedMeshProperties(MergeSkinnedMesh mergeSkinnedMesh)
}
}
+ [ComponentInformation(typeof(RemoveZeroSizedPolygon))]
+ internal class RemoveZeroSizedPolygonInformation : ComponentInformation
+ {
+ protected override void CollectDependency(RemoveZeroSizedPolygon component, ComponentDependencyCollector collector)
+ {
+ collector.AddDependency(component.GetComponent(), component);
+ }
+
+ protected override void CollectMutations(RemoveZeroSizedPolygon component, ComponentMutationsCollector collector)
+ {
+ }
+ }
+
[ComponentInformation(typeof(MergeBone))]
internal class MergeBoneInformation : ComponentInformation
{
diff --git a/Editor/AnimatorParsers/AnimatorParser.cs b/Editor/AnimatorParsers/AnimatorParser.cs
index abef9721d..579632c08 100644
--- a/Editor/AnimatorParsers/AnimatorParser.cs
+++ b/Editor/AnimatorParsers/AnimatorParser.cs
@@ -174,6 +174,18 @@ private IModificationsContainer CollectAvatarRootAnimatorModifications(BuildCont
if (descriptor)
CollectAvatarDescriptorModifications(modificationsContainer, descriptor);
#endif
+
+#if AAO_VRM0
+ var blendShapeProxy = session.AvatarRootObject.GetComponent();
+ if (blendShapeProxy)
+ CollectBlendShapeProxyModifications(session, modificationsContainer, blendShapeProxy);
+#endif
+
+#if AAO_VRM1
+ var vrm10Instance = session.AvatarRootObject.GetComponent();
+ if (vrm10Instance)
+ CollectVrm10InstanceModifications(session, modificationsContainer, vrm10Instance);
+#endif
return modificationsContainer;
}
@@ -356,6 +368,35 @@ private static RuntimeAnimatorController GetPlayableLayerController(VRCAvatarDes
}
#endif
+#if AAO_VRM0
+ private void CollectBlendShapeProxyModifications(BuildContext context, ModificationsContainer modificationsContainer, VRM.VRMBlendShapeProxy vrmBlendShapeProxy)
+ {
+
+ var bindings = vrmBlendShapeProxy.BlendShapeAvatar.Clips.SelectMany(clip => clip.Values);
+ foreach (var binding in bindings)
+ {
+ var skinnedMeshRenderer = context.AvatarRootTransform.Find(binding.RelativePath).GetComponent();
+ var blendShapePropName = $"blendShape.{skinnedMeshRenderer.sharedMesh.GetBlendShapeName(binding.Index)}";
+ modificationsContainer.ModifyObject(skinnedMeshRenderer)
+ .AddModificationAsNewLayer(blendShapePropName, AnimationFloatProperty.Variable(vrmBlendShapeProxy));
+ }
+ }
+#endif
+
+#if AAO_VRM1
+ private void CollectVrm10InstanceModifications(BuildContext context, ModificationsContainer modificationsContainer, UniVRM10.Vrm10Instance vrm10Instance)
+ {
+ var bindings = vrm10Instance.Vrm.Expression.Clips.SelectMany(clip => clip.Clip.MorphTargetBindings);
+ foreach (var binding in bindings)
+ {
+ var skinnedMeshRenderer = context.AvatarRootTransform.Find(binding.RelativePath).GetComponent();
+ var blendShapePropName = $"blendShape.{skinnedMeshRenderer.sharedMesh.GetBlendShapeName(binding.Index)}";
+ modificationsContainer.ModifyObject(skinnedMeshRenderer)
+ .AddModificationAsNewLayer(blendShapePropName, AnimationFloatProperty.Variable(vrm10Instance));
+ }
+ }
+#endif
+
#endregion
#region Animator
diff --git a/Editor/AutomaticConfiguration.cs b/Editor/AutomaticConfiguration.cs
index c95e69941..c4d35b5db 100644
--- a/Editor/AutomaticConfiguration.cs
+++ b/Editor/AutomaticConfiguration.cs
@@ -10,6 +10,7 @@ internal class TraceAndOptimizeEditor : AvatarGlobalComponentEditorBase
private SerializedProperty _freezeBlendShape;
private SerializedProperty _removeUnusedObjects;
private SerializedProperty _preserveEndBone;
+ private SerializedProperty _removeZeroSizedPolygons;
private SerializedProperty _mmdWorldCompatibility;
private SerializedProperty _advancedSettings;
private GUIContent _advancedSettingsLabel = new GUIContent();
@@ -19,6 +20,7 @@ private void OnEnable()
_freezeBlendShape = serializedObject.FindProperty(nameof(TraceAndOptimize.freezeBlendShape));
_removeUnusedObjects = serializedObject.FindProperty(nameof(TraceAndOptimize.removeUnusedObjects));
_preserveEndBone = serializedObject.FindProperty(nameof(TraceAndOptimize.preserveEndBone));
+ _removeZeroSizedPolygons = serializedObject.FindProperty(nameof(TraceAndOptimize.removeZeroSizedPolygons));
_mmdWorldCompatibility = serializedObject.FindProperty(nameof(TraceAndOptimize.mmdWorldCompatibility));
_advancedSettings = serializedObject.FindProperty(nameof(TraceAndOptimize.advancedSettings));
}
@@ -39,6 +41,7 @@ protected override void OnInspectorGUIInner()
EditorGUILayout.PropertyField(_preserveEndBone);
EditorGUI.indentLevel--;
}
+ EditorGUILayout.PropertyField(_removeZeroSizedPolygons);
_advancedSettingsLabel.text = CL4EE.Tr("TraceAndOptimize:prop:advancedSettings");
if (EditorGUILayout.PropertyField(_advancedSettings, _advancedSettingsLabel, false))
diff --git a/Editor/ObjectMapping/AnimationObjectMapper.cs b/Editor/ObjectMapping/AnimationObjectMapper.cs
index 0a1002652..db3c2e776 100644
--- a/Editor/ObjectMapping/AnimationObjectMapper.cs
+++ b/Editor/ObjectMapping/AnimationObjectMapper.cs
@@ -116,22 +116,28 @@ public string MapPath(string srcPath, Type type)
}
[CanBeNull]
- public EditorCurveBinding[] MapBinding(EditorCurveBinding binding)
+ public (string path, Type type, string propertyName)[] MapBinding(string path, Type type, string propertyName)
{
- var gameObjectInfo = GetGameObjectInfo(binding.path);
+ var gameObjectInfo = GetGameObjectInfo(path);
if (gameObjectInfo == null)
return null;
- var (instanceId, componentInfo) = gameObjectInfo.GetComponentByType(binding.type);
+ var (instanceId, componentInfo) = gameObjectInfo.GetComponentByType(type);
if (componentInfo != null)
{
// there's mapping about component.
// this means the component is merged or some prop has mapping
- if (componentInfo.PropertyMapping.TryGetValue(binding.propertyName, out var newProp))
+ if (componentInfo.PropertyMapping.TryGetValue(propertyName, out var newProp))
{
+ // if mapped one is exactly same as original, return null
+ if (newProp.AllCopiedTo.Length == 1
+ && newProp.AllCopiedTo[0].InstanceId == instanceId
+ && newProp.AllCopiedTo[0].Name == propertyName)
+ return null;
+
// there are mapping for property
- var curveBindings = new EditorCurveBinding[newProp.AllCopiedTo.Length];
+ var mappedBindings = new (string path, Type type, string propertyName)[newProp.AllCopiedTo.Length];
var copiedToIndex = 0;
for (var i = 0; i < newProp.AllCopiedTo.Length; i++)
{
@@ -148,28 +154,24 @@ public EditorCurveBinding[] MapBinding(EditorCurveBinding binding)
// this means moved to out of the animator scope
// TODO: add warning
- if (newPath == null) return Array.Empty();
+ if (newPath == null) return Array.Empty<(string path, Type type, string propertyName)>();
- binding.path = newPath;
- binding.type = descriptor.Type;
- binding.propertyName = descriptor.Name;
- curveBindings[i] = binding; // copy
+ mappedBindings[i] = (newPath, descriptor.Type, descriptor.Name);
}
- if (copiedToIndex != curveBindings.Length)
- return curveBindings.AsSpan().Slice(0, copiedToIndex).ToArray();
- return curveBindings;
+ if (copiedToIndex != mappedBindings.Length)
+ return mappedBindings.AsSpan().Slice(0, copiedToIndex).ToArray();
+ return mappedBindings;
}
else
{
var component = new ComponentOrGameObject(EditorUtility.InstanceIDToObject(componentInfo.MergedInto));
- if (!component) return Array.Empty(); // this means removed.
+ if (!component) return Array.Empty<(string path, Type type, string propertyName)>(); // this means removed.
var newPath = Utils.RelativePath(_rootGameObject.transform, component.transform);
- if (newPath == null) return Array.Empty(); // this means moved to out of the animator scope
- if (binding.path == newPath) return null;
- binding.path = newPath;
- return new []{ binding };
+ if (newPath == null) return Array.Empty<(string path, Type type, string propertyName)>(); // this means moved to out of the animator scope
+ if (path == newPath) return null;
+ return new []{ (newPath, type, propertyName) };
}
}
else
@@ -177,13 +179,32 @@ public EditorCurveBinding[] MapBinding(EditorCurveBinding binding)
// The component is not merged & no prop mapping so process GameObject mapping
var component = EditorUtility.InstanceIDToObject(instanceId);
- if (!component) return Array.Empty(); // this means removed
+ if (!component) return Array.Empty<(string path, Type type, string propertyName)>(); // this means removed
- if (gameObjectInfo.NewPath == null) return Array.Empty();
- if (binding.path == gameObjectInfo.NewPath) return null;
- binding.path = gameObjectInfo.NewPath;
- return new[] { binding };
+ if (gameObjectInfo.NewPath == null) return Array.Empty<(string path, Type type, string propertyName)>();
+ if (path == gameObjectInfo.NewPath) return null;
+ return new[] { (gameObjectInfo.NewPath, type, propertyName) };
+ }
+ }
+
+ [CanBeNull]
+ public EditorCurveBinding[] MapBinding(EditorCurveBinding binding)
+ {
+ var mappedBindings = MapBinding(binding.path, binding.type, binding.propertyName);
+ if (mappedBindings == null)
+ {
+ return null;
+ }
+
+ var curveBindings = new EditorCurveBinding[mappedBindings.Length];
+ for (var i = 0; i < mappedBindings.Length; i++)
+ {
+ binding.path = mappedBindings[i].path;
+ binding.type = mappedBindings[i].type;
+ binding.propertyName = mappedBindings[i].propertyName;
+ curveBindings[i] = binding; // copy everything else
}
+ return curveBindings;
}
}
}
diff --git a/Editor/ObjectMapping/ObjectMappingContext.cs b/Editor/ObjectMapping/ObjectMappingContext.cs
index b5d0736ba..71a4d95fb 100644
--- a/Editor/ObjectMapping/ObjectMappingContext.cs
+++ b/Editor/ObjectMapping/ObjectMappingContext.cs
@@ -44,16 +44,25 @@ public void OnDeactivate(BuildContext context)
if (mapping.MapComponentInstance(p.objectReferenceInstanceIDValue, out var mappedComponent))
p.objectReferenceValue = mappedComponent;
- if (p.objectReferenceValue is RuntimeAnimatorController controller)
+ var objectReferenceValue = p.objectReferenceValue;
+ switch (objectReferenceValue)
{
- if (mapper == null)
- mapper = new AnimatorControllerMapper(mapping.CreateAnimationMapper(component.gameObject));
-
- // ReSharper disable once AccessToModifiedClosure
- var mapped = BuildReport.ReportingObject(controller,
- () => mapper.MapAnimatorController(controller));
- if (mapped != controller)
- p.objectReferenceValue = mapped;
+ case RuntimeAnimatorController _:
+#if AAO_VRM0
+ case VRM.BlendShapeAvatar _:
+#endif
+#if AAO_VRM1
+ case UniVRM10.VRM10Object _:
+#endif
+ if (mapper == null)
+ mapper = new AnimatorControllerMapper(mapping.CreateAnimationMapper(component.gameObject));
+
+ // ReSharper disable once AccessToModifiedClosure
+ var mapped = BuildReport.ReportingObject(objectReferenceValue,
+ () => mapper.MapObject(objectReferenceValue));
+ if (mapped != objectReferenceValue)
+ p.objectReferenceValue = mapped;
+ break;
}
}
@@ -135,6 +144,9 @@ public AnimatorControllerMapper(AnimationObjectMapper mapping)
public T MapAnimatorController(T controller) where T : RuntimeAnimatorController =>
DeepClone(controller, CustomClone);
+ public T MapObject(T obj) where T : Object =>
+ DeepClone(obj, CustomClone);
+
// https://github.com/bdunderscore/modular-avatar/blob/db49e2e210bc070671af963ff89df853ae4514a5/Packages/nadena.dev.modular-avatar/Editor/AnimatorMerger.cs#L199-L241
// Originally under MIT License
// Copyright (c) 2022 bd_
@@ -142,6 +154,10 @@ private Object CustomClone(Object o)
{
if (o is AnimationClip clip)
{
+#if AAO_VRCSDK3_AVATARS
+ // TODO: when BuildContext have property to check if it is for VRCSDK3, additionally use it.
+ if (clip.IsProxy()) return clip;
+#endif
var newClip = new AnimationClip();
newClip.name = "rebased " + clip.name;
@@ -228,11 +244,62 @@ private Object CustomClone(Object o)
newMask.SetTransformActive(dstI, mask.GetTransformActive(srcI));
dstI++;
}
+ if (path != newPath) _mapped = true;
}
newMask.transformCount = dstI;
return newMask;
}
+#if AAO_VRM0
+ else if (o is VRM.BlendShapeClip blendShapeClip)
+ {
+ var newBlendShapeClip = DefaultDeepClone(blendShapeClip, CustomClone);
+ newBlendShapeClip.Prefab = null; // This likely to point prefab before mapping, which is invalid by now
+ newBlendShapeClip.name = "rebased " + blendShapeClip.name;
+ newBlendShapeClip.Values = newBlendShapeClip.Values.SelectMany(binding =>
+ {
+ var mappedBindings = _mapping.MapBinding(binding.RelativePath, typeof(SkinnedMeshRenderer), VProp.BlendShapeIndex(binding.Index));
+ if (mappedBindings == null)
+ {
+ return new[] { binding };
+ }
+ _mapped = true;
+ return mappedBindings
+ .Select(mapped => new VRM.BlendShapeBinding
+ {
+ RelativePath = _mapping.MapPath(mapped.path, typeof(SkinnedMeshRenderer)),
+ Index = VProp.ParseBlendShapeIndex(mapped.propertyName),
+ Weight = binding.Weight
+ });
+ }).ToArray();
+ return newBlendShapeClip;
+ }
+#endif
+#if AAO_VRM1
+ else if (o is UniVRM10.VRM10Expression vrm10Expression)
+ {
+ var newVrm10Expression = DefaultDeepClone(vrm10Expression, CustomClone);
+ newVrm10Expression.Prefab = null; // This likely to point prefab before mapping, which is invalid by now
+ newVrm10Expression.name = "rebased " + vrm10Expression.name;
+ newVrm10Expression.MorphTargetBindings = newVrm10Expression.MorphTargetBindings.SelectMany(binding =>
+ {
+ var mappedBindings = _mapping.MapBinding(binding.RelativePath, typeof(SkinnedMeshRenderer), VProp.BlendShapeIndex(binding.Index));
+ if (mappedBindings == null)
+ {
+ return new[] { binding };
+ }
+ _mapped = true;
+ return mappedBindings
+ .Select(mapped => new UniVRM10.MorphTargetBinding
+ {
+ RelativePath = _mapping.MapPath(mapped.path, typeof(SkinnedMeshRenderer)),
+ Index = VProp.ParseBlendShapeIndex(mapped.propertyName),
+ Weight = binding.Weight
+ });
+ }).ToArray();
+ return newVrm10Expression;
+ }
+#endif
else if (o is RuntimeAnimatorController controller)
{
using (new MappedScope(this))
@@ -244,6 +311,62 @@ private Object CustomClone(Object o)
return newController;
}
}
+#if AAO_VRM0
+ else if (o is VRM.BlendShapeAvatar blendShapeAvatar)
+ {
+ using (new MappedScope(this))
+ {
+ var newBlendShapeAvatar = DefaultDeepClone(blendShapeAvatar, CustomClone);
+ newBlendShapeAvatar.name = blendShapeAvatar.name + " (rebased)";
+ if (!_mapped) newBlendShapeAvatar = blendShapeAvatar;
+ _cache[blendShapeAvatar] = newBlendShapeAvatar;
+ return newBlendShapeAvatar;
+ }
+ }
+#endif
+#if AAO_VRM1
+ else if (o is UniVRM10.VRM10Object vrm10Object)
+ {
+ using (new MappedScope(this))
+ {
+ var newVrm10Object = DefaultDeepClone(vrm10Object, CustomClone);
+ newVrm10Object.name = vrm10Object.name + " (rebased)";
+ if (!_mapped) newVrm10Object = vrm10Object;
+ _cache[vrm10Object] = newVrm10Object;
+
+ newVrm10Object.FirstPerson.Renderers = newVrm10Object.FirstPerson.Renderers
+ .Select(r => new UniVRM10.RendererFirstPersonFlags
+ {
+ Renderer = _mapping.MapPath(r.Renderer, typeof(Renderer)),
+ FirstPersonFlag = r.FirstPersonFlag
+ })
+ .Where(r => r.Renderer != null)
+ .GroupBy(r => r.Renderer, r => r.FirstPersonFlag)
+ .Select(grouping =>
+ {
+ UniGLTF.Extensions.VRMC_vrm.FirstPersonType mergedFirstPersonFlag;
+ var firstPersonFlags = grouping.Distinct().ToArray();
+ if (firstPersonFlags.Length == 1)
+ {
+ mergedFirstPersonFlag = firstPersonFlags[0];
+ }
+ else
+ {
+ mergedFirstPersonFlag = firstPersonFlags.Contains(UniGLTF.Extensions.VRMC_vrm.FirstPersonType.both) ? UniGLTF.Extensions.VRMC_vrm.FirstPersonType.both : UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto;
+ BuildReport.LogWarning("MergeSkinnedMesh:warning:VRM:FirstPersonFlagsMismatch", mergedFirstPersonFlag.ToString());
+ }
+
+ return new UniVRM10.RendererFirstPersonFlags
+ {
+ Renderer = grouping.Key,
+ FirstPersonFlag = mergedFirstPersonFlag
+ };
+ }).ToList();
+
+ return newVrm10Object;
+ }
+ }
+#endif
else
{
return null;
@@ -290,10 +413,24 @@ private T DeepClone(T original, Func