mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-28 10:15: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.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.rule_inverted": "このルールの条件は反転されています",
|
||||
"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;
|
||||
FixupExpressionsMenuPass.FixupExpressionsMenu(maContext);
|
||||
});
|
||||
seq.Run(RemoveVertexColorPass.Instance).PreviewingWith(new RemoveVertexColorPreview());
|
||||
#endif
|
||||
seq.Run(RebindHumanoidAvatarPass.Instance);
|
||||
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