mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 18:55:06 +08:00
feat: Remove Vertex Color (#1378)
This commit is contained in:
parent
f35283db51
commit
2c3e24333a
39
Editor/Inspector/RemoveVertexColorEditor.cs
Normal file
39
Editor/Inspector/RemoveVertexColorEditor.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using UnityEditor;
|
||||||
|
using static nadena.dev.modular_avatar.core.editor.Localization;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(ModularAvatarRemoveVertexColor.RemoveMode))]
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
internal class RVCModeDrawer : EnumDrawer<ModularAvatarRemoveVertexColor.RemoveMode>
|
||||||
|
{
|
||||||
|
protected override string localizationPrefix => "remove-vertex-color.mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
[CustomEditor(typeof(ModularAvatarRemoveVertexColor))]
|
||||||
|
internal class RemoveVertexColorEditor : MAEditorBase
|
||||||
|
{
|
||||||
|
private SerializedProperty _p_mode;
|
||||||
|
|
||||||
|
protected void OnEnable()
|
||||||
|
{
|
||||||
|
_p_mode = serializedObject.FindProperty(nameof(ModularAvatarRemoveVertexColor.Mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInnerInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
EditorGUILayout.PropertyField(_p_mode, G("remove-vertex-color.mode"));
|
||||||
|
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowLanguageUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
Editor/Inspector/RemoveVertexColorEditor.cs.meta
Normal file
3
Editor/Inspector/RemoveVertexColorEditor.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bfcaf601e9f94ba2900e66d66f469037
|
||||||
|
timeCreated: 1733085477
|
@ -284,5 +284,8 @@
|
|||||||
|
|
||||||
"ro_sim.effect_group.rule_inverted": "This rule is inverted",
|
"ro_sim.effect_group.rule_inverted": "This rule is inverted",
|
||||||
"ro_sim.effect_group.rule_inverted.tooltip": "This rule will be applied when one of its conditions is NOT met",
|
"ro_sim.effect_group.rule_inverted.tooltip": "This rule will be applied when one of its conditions is NOT met",
|
||||||
"ro_sim.effect_group.conditions": "Conditions"
|
"ro_sim.effect_group.conditions": "Conditions",
|
||||||
|
"remove-vertex-color.mode": "Mode",
|
||||||
|
"remove-vertex-color.mode.Remove": "Remove Vertex Colors",
|
||||||
|
"remove-vertex-color.mode.DontRemove": "Keep Vertex Colors"
|
||||||
}
|
}
|
||||||
|
@ -276,5 +276,8 @@
|
|||||||
"ro_sim.effect_group.material.tooltip": "上記の Reactive Component がアクティブな時に設定されるマテリアル",
|
"ro_sim.effect_group.material.tooltip": "上記の Reactive Component がアクティブな時に設定されるマテリアル",
|
||||||
"ro_sim.effect_group.rule_inverted": "このルールの条件は反転されています",
|
"ro_sim.effect_group.rule_inverted": "このルールの条件は反転されています",
|
||||||
"ro_sim.effect_group.rule_inverted.tooltip": "このルールは、いずれかの条件が満たされていない場合に適用されます",
|
"ro_sim.effect_group.rule_inverted.tooltip": "このルールは、いずれかの条件が満たされていない場合に適用されます",
|
||||||
"ro_sim.effect_group.conditions": "条件"
|
"ro_sim.effect_group.conditions": "条件",
|
||||||
|
"remove-vertex-color.mode": "モード",
|
||||||
|
"remove-vertex-color.mode.Remove": "頂点カラーを削除する",
|
||||||
|
"remove-vertex-color.mode.DontRemove": "頂点カラーを削除しない"
|
||||||
}
|
}
|
||||||
|
3
Editor/MiscPreview.meta
Normal file
3
Editor/MiscPreview.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea61a438a5d54a289c6abbb1e05c56da
|
||||||
|
timeCreated: 1733085642
|
121
Editor/MiscPreview/RemoveVertexColorPreview.cs
Normal file
121
Editor/MiscPreview/RemoveVertexColorPreview.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using nadena.dev.ndmf.preview;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
internal class RemoveVertexColorPreview : IRenderFilter
|
||||||
|
{
|
||||||
|
private static string ToPathString(ComputeContext ctx, Transform t)
|
||||||
|
{
|
||||||
|
return string.Join("/", ctx.ObservePath(t).Select(t2 => t2.gameObject.name).Reverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<RenderGroup> GetTargetGroups(ComputeContext context)
|
||||||
|
{
|
||||||
|
var roots = context.GetAvatarRoots();
|
||||||
|
var removers = roots
|
||||||
|
.SelectMany(r => context.GetComponentsInChildren<ModularAvatarRemoveVertexColor>(r, true))
|
||||||
|
.Select(rvc => (ToPathString(context, rvc.transform),
|
||||||
|
context.Observe(rvc, r => r.Mode) == ModularAvatarRemoveVertexColor.RemoveMode.Remove))
|
||||||
|
.OrderBy(pair => pair.Item1)
|
||||||
|
.ToList();
|
||||||
|
var targets = roots.SelectMany(
|
||||||
|
r => context.GetComponentsInChildren<SkinnedMeshRenderer>(r, true)
|
||||||
|
.Concat(
|
||||||
|
context.GetComponentsInChildren<MeshFilter>(r, true)
|
||||||
|
.SelectMany(mf => context.GetComponents<Renderer>(mf.gameObject))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
targets = targets.Where(target =>
|
||||||
|
{
|
||||||
|
var stringPath = ToPathString(context, target.transform);
|
||||||
|
var index = removers.BinarySearch((stringPath, true));
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
// There is a component on this mesh
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var priorIndex = ~index - 1;
|
||||||
|
if (priorIndex < 0) return false; // no match
|
||||||
|
|
||||||
|
var (maybeParent, mode) = removers[priorIndex];
|
||||||
|
if (!stringPath.StartsWith(maybeParent)) return false; // no parent matched
|
||||||
|
return mode;
|
||||||
|
});
|
||||||
|
|
||||||
|
return targets.Select(RenderGroup.For).ToImmutableList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IRenderFilterNode> Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs,
|
||||||
|
ComputeContext context)
|
||||||
|
{
|
||||||
|
Dictionary<Mesh, Mesh> conversionMap = new();
|
||||||
|
|
||||||
|
foreach (var (_, proxy) in proxyPairs)
|
||||||
|
{
|
||||||
|
Component c = proxy;
|
||||||
|
if (!(c is SkinnedMeshRenderer))
|
||||||
|
{
|
||||||
|
c = context.GetComponent<MeshFilter>(proxy.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == null) continue;
|
||||||
|
|
||||||
|
RemoveVertexColorPass.ForceRemove(_ => false, c, conversionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<IRenderFilterNode>(new Node(conversionMap.Values.FirstOrDefault()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Node : IRenderFilterNode
|
||||||
|
{
|
||||||
|
private readonly Mesh? _theMesh;
|
||||||
|
|
||||||
|
public Node(Mesh? theMesh)
|
||||||
|
{
|
||||||
|
_theMesh = theMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IRenderFilterNode> Refresh(IEnumerable<(Renderer, Renderer)> proxyPairs, ComputeContext context,
|
||||||
|
RenderAspects updatedAspects)
|
||||||
|
{
|
||||||
|
if (updatedAspects.HasFlag(RenderAspects.Mesh)) return Task.FromResult<IRenderFilterNode>(null);
|
||||||
|
if (_theMesh == null) return Task.FromResult<IRenderFilterNode>(null);
|
||||||
|
|
||||||
|
return Task.FromResult<IRenderFilterNode>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderAspects WhatChanged => RenderAspects.Mesh;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_theMesh != null) Object.DestroyImmediate(_theMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnFrame(Renderer original, Renderer proxy)
|
||||||
|
{
|
||||||
|
if (_theMesh == null) return;
|
||||||
|
|
||||||
|
switch (proxy)
|
||||||
|
{
|
||||||
|
case SkinnedMeshRenderer smr: smr.sharedMesh = _theMesh; break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
var mf = proxy.GetComponent<MeshFilter>();
|
||||||
|
if (mf != null) mf.sharedMesh = _theMesh;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
Editor/MiscPreview/RemoveVertexColorPreview.cs.meta
Normal file
3
Editor/MiscPreview/RemoveVertexColorPreview.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b05d5c04f86b4924bf8acdd135448463
|
||||||
|
timeCreated: 1733085648
|
@ -88,6 +88,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
var maContext = ctx.Extension<ModularAvatarContext>().BuildContext;
|
var maContext = ctx.Extension<ModularAvatarContext>().BuildContext;
|
||||||
FixupExpressionsMenuPass.FixupExpressionsMenu(maContext);
|
FixupExpressionsMenuPass.FixupExpressionsMenu(maContext);
|
||||||
});
|
});
|
||||||
|
seq.Run(RemoveVertexColorPass.Instance).PreviewingWith(new RemoveVertexColorPreview());
|
||||||
#endif
|
#endif
|
||||||
seq.Run(RebindHumanoidAvatarPass.Instance);
|
seq.Run(RebindHumanoidAvatarPass.Instance);
|
||||||
seq.Run("Purge ModularAvatar components", ctx =>
|
seq.Run("Purge ModularAvatar components", ctx =>
|
||||||
|
93
Editor/RemoveVertexColorPass.cs
Normal file
93
Editor/RemoveVertexColorPass.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using nadena.dev.ndmf;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
internal class RemoveVertexColorPass : Pass<RemoveVertexColorPass>
|
||||||
|
{
|
||||||
|
protected override void Execute(ndmf.BuildContext context)
|
||||||
|
{
|
||||||
|
var removers = context.AvatarRootTransform.GetComponentsInChildren<ModularAvatarRemoveVertexColor>(true)!;
|
||||||
|
|
||||||
|
Dictionary<Mesh, Mesh> conversionMap = new();
|
||||||
|
|
||||||
|
foreach (var remover in removers)
|
||||||
|
{
|
||||||
|
foreach (var smr in remover!.GetComponentsInChildren<SkinnedMeshRenderer>(true))
|
||||||
|
{
|
||||||
|
TryRemove(context.IsTemporaryAsset, smr, conversionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mf in remover.GetComponentsInChildren<MeshFilter>(true))
|
||||||
|
{
|
||||||
|
TryRemove(context.IsTemporaryAsset, mf, conversionMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string PropPath = "m_Mesh";
|
||||||
|
|
||||||
|
private static void TryRemove(
|
||||||
|
Func<Mesh, bool> isTempAsset,
|
||||||
|
Component c,
|
||||||
|
Dictionary<Mesh, Mesh> conversionMap
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var nearestRemover = c.GetComponentInParent<ModularAvatarRemoveVertexColor>()!;
|
||||||
|
if (nearestRemover.Mode != ModularAvatarRemoveVertexColor.RemoveMode.Remove) return;
|
||||||
|
|
||||||
|
ForceRemove(isTempAsset, c, conversionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ForceRemove(Func<Mesh, bool> isTempAsset, Component c,
|
||||||
|
Dictionary<Mesh, Mesh> conversionMap)
|
||||||
|
{
|
||||||
|
var obj = new SerializedObject(c);
|
||||||
|
var prop = obj.FindProperty("m_Mesh");
|
||||||
|
if (prop == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Property not found: " + PropPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var mesh = prop.objectReferenceValue as Mesh;
|
||||||
|
if (mesh == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalMesh = mesh;
|
||||||
|
|
||||||
|
if (conversionMap.TryGetValue(mesh, out var converted))
|
||||||
|
{
|
||||||
|
prop.objectReferenceValue = converted;
|
||||||
|
obj.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh.GetVertexAttributes().All(va => va.attribute != VertexAttribute.Color))
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTempAsset(mesh))
|
||||||
|
{
|
||||||
|
mesh = Object.Instantiate(mesh);
|
||||||
|
prop.objectReferenceValue = mesh;
|
||||||
|
obj.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.colors = null;
|
||||||
|
|
||||||
|
conversionMap[originalMesh] = mesh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
Editor/RemoveVertexColorPass.cs.meta
Normal file
3
Editor/RemoveVertexColorPass.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a227da6f9f1548c3867b1ed113f28e9d
|
||||||
|
timeCreated: 1733008734
|
23
Runtime/RemoveVertexColor.cs
Normal file
23
Runtime/RemoveVertexColor.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core
|
||||||
|
{
|
||||||
|
[AddComponentMenu("Modular Avatar/MA Remove Vertex Color")]
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/remove-vertex-color?lang=auto")]
|
||||||
|
[PublicAPI]
|
||||||
|
public class ModularAvatarRemoveVertexColor : AvatarTagComponent
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
[PublicAPI]
|
||||||
|
public enum RemoveMode
|
||||||
|
{
|
||||||
|
Remove,
|
||||||
|
DontRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveMode Mode = RemoveMode.Remove;
|
||||||
|
}
|
||||||
|
}
|
11
Runtime/RemoveVertexColor.cs.meta
Normal file
11
Runtime/RemoveVertexColor.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dc5f8bfae24244aeaedcd6c2bb7264f9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
docs~/docs/reference/remove-vertex-color-after.png
Normal file
BIN
docs~/docs/reference/remove-vertex-color-after.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 378 KiB |
BIN
docs~/docs/reference/remove-vertex-color-before.png
Normal file
BIN
docs~/docs/reference/remove-vertex-color-before.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 380 KiB |
34
docs~/docs/reference/remove-vertex-color.md
Normal file
34
docs~/docs/reference/remove-vertex-color.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Remove Vertex Color
|
||||||
|
|
||||||
|
![Remove Vertex Color](remove-vertex-color.png)
|
||||||
|
|
||||||
|
The Remove Vertex Color component removes vertex colors from the object it is attached to and its children.
|
||||||
|
|
||||||
|
## When should I use it?
|
||||||
|
|
||||||
|
Sometimes, models come with vertex colors that aren't intended for display. When changing to a shader that
|
||||||
|
makes use of vertex colors, such as the VRChat mobile shaders, this can result in undesired discoloration. You can use
|
||||||
|
this component to remove these vertex colors nondestructively.
|
||||||
|
|
||||||
|
<div style={{display: "flex", "flex-direction": "row"}}>
|
||||||
|
<div style={{margin: "1em"}}>
|
||||||
|
<div>
|
||||||
|
![With unwanted vertex colors](remove-vertex-color-before.png)
|
||||||
|
</div>
|
||||||
|
*Without Remove Vertex Color, some unwanted vertex colors discolor this avatar's hair.*
|
||||||
|
</div>
|
||||||
|
<div style={{margin: "1em"}}>
|
||||||
|
<div>
|
||||||
|
![After removing vertex colors](remove-vertex-color-after.png)
|
||||||
|
</div>
|
||||||
|
*After adding Remove Vertex Color, the avatar's hair is the correct color.*
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Detailed usage
|
||||||
|
|
||||||
|
Simply attach the Remove Vertex Color component to an object in your avatar - often, you can just add it to the root
|
||||||
|
object. All objects below that object in the hierarchy will have their vertex colors removed.
|
||||||
|
|
||||||
|
If you want to exclude some objects, add a Remove Vertex Color component to the object you want to exclude and set
|
||||||
|
the mode to "Keep Vertex Colors". Any objects below this object will not have their vertex colors removed.
|
BIN
docs~/docs/reference/remove-vertex-color.png
Normal file
BIN
docs~/docs/reference/remove-vertex-color.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 378 KiB |
Binary file not shown.
After Width: | Height: | Size: 380 KiB |
@ -0,0 +1,33 @@
|
|||||||
|
# Remove Vertex Color
|
||||||
|
|
||||||
|
![Remove Vertex Color](remove-vertex-color.png)
|
||||||
|
|
||||||
|
Remove Vertex Color コンポーネントは、アタッチされたオブジェクトとその子オブジェクトから頂点カラーを削除します。
|
||||||
|
|
||||||
|
## いつ使うものか?
|
||||||
|
|
||||||
|
時々、アバターや衣装には意図されていない頂点カラーが付いていることがあります。VRChat Mobile系統など頂点カラーを使用するシェーダーに変更すると、
|
||||||
|
変色してしまうことがあります。このコンポーネントを使えば、非破壊的に問題の頂点カラーを削除できます。
|
||||||
|
|
||||||
|
<div style={{display: "flex", "flex-direction": "row"}}>
|
||||||
|
<div style={{margin: "1em"}}>
|
||||||
|
<div>
|
||||||
|
![不要な頂点カラーがある場合](remove-vertex-color-before.png)
|
||||||
|
</div>
|
||||||
|
*Remove Vertex Color を使わないと、このアバターの髪の毛に不要な頂点カラーで変色してしまいます。*
|
||||||
|
</div>
|
||||||
|
<div style={{margin: "1em"}}>
|
||||||
|
<div>
|
||||||
|
![頂点カラーを削除した後](remove-vertex-color-after.png)
|
||||||
|
</div>
|
||||||
|
*Remove Vertex Color を追加した後、アバターの髪の色が正しくなります。*
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 詳細な使い方
|
||||||
|
|
||||||
|
Remove Vertex Color コンポーネントをアバターのオブジェクトに追加してください。通常、ルートオブジェクトに追加するだけで十分です。
|
||||||
|
このオブジェクト以下のすべてのオブジェクトの頂点カラーが削除されます。
|
||||||
|
|
||||||
|
特定のオブジェクトを除外したい場合は、除外したいオブジェクトに Remove Vertex Color コンポーネントを追加し、モードを「頂点カラーを削除しない」
|
||||||
|
に設定してください。このオブジェクト以下のオブジェクトの頂点カラーは削除されません。
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Loading…
Reference in New Issue
Block a user