generated from vrchat-common/template-avatar
Add RekornTools
This commit is contained in:
parent
9eb8736eee
commit
5c3e71a420
250 changed files with 14833 additions and 0 deletions
4439
Assets/02_Assets/01_Avatar/Komado/Chocolat/Animations/F_default.anim
Normal file
4439
Assets/02_Assets/01_Avatar/Komado/Chocolat/Animations/F_default.anim
Normal file
File diff suppressed because it is too large
Load diff
8
Assets/02_Assets/01_Avatar/Komado/Chocolat/Animations/F_default.anim.meta
generated
Normal file
8
Assets/02_Assets/01_Avatar/Komado/Chocolat/Animations/F_default.anim.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3305beb2138e64147a9128f10533c35e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools.meta
generated
Normal file
8
Assets/RekornTools.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 18de0e02918b4b7429642f58b19d1441
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar.meta
generated
Normal file
8
Assets/RekornTools/Avatar.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dcb027a5c532a6a4e98a7c28a182f831
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar/Components.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Components.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1de2ade747570954880c52da75ff2c6f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar/Components/LightEditor.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Components/LightEditor.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f37a7a5cf7195f74e97b23ed35b6ff58
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar/Components/LightEditor/Editor.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Components/LightEditor/Editor.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cef04c0973a08b8478649cd5978e0c78
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,74 @@
|
|||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
using static RekornTools.Avatar.Editor.EditorGUILayoutExtensions;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
public sealed class LightEditor : BaseEditorWindow<LightEditor>
|
||||
{
|
||||
[SerializeField] bool _ignoreLighting;
|
||||
[SerializeField] Light _sun;
|
||||
[SerializeField] LightScenario _scenario;
|
||||
|
||||
[MenuItem("Tools/Rekorn Tools/Light Editor")]
|
||||
static void OnWindowOpen() =>
|
||||
GetWindow<LightEditor>("Light Editor")?.Show();
|
||||
|
||||
protected override void Enable()
|
||||
{
|
||||
EditorSceneManager.activeSceneChangedInEditMode += OnSceneChanged;
|
||||
FindSun();
|
||||
}
|
||||
|
||||
protected override void Disable()
|
||||
{
|
||||
EditorSceneManager.activeSceneChangedInEditMode -= OnSceneChanged;
|
||||
}
|
||||
|
||||
void OnSceneChanged(Scene prev, Scene current) => FindSun();
|
||||
|
||||
void FindSun()
|
||||
{
|
||||
const string sunName = "Directional Light";
|
||||
_sun = GameObject.Find(sunName)?.GetComponent<Light>();
|
||||
if (_sun == null) _sun = new GameObject(sunName).AddComponent<Light>();
|
||||
}
|
||||
|
||||
protected override void Draw()
|
||||
{
|
||||
LabelField("Light Preset", EditorStyles.boldLabel);
|
||||
|
||||
_ignoreLighting = Toggle("Ignore Lighting", _ignoreLighting);
|
||||
SetCurrentSceneViewLighting(!_ignoreLighting);
|
||||
|
||||
_scenario = ObjectField("Scenario", _scenario, false);
|
||||
DrawLightScenario();
|
||||
}
|
||||
|
||||
static void SetCurrentSceneViewLighting(bool isEnable)
|
||||
{
|
||||
var sceneViews = SceneView.sceneViews;
|
||||
if (sceneViews == null) return;
|
||||
|
||||
foreach (SceneView view in sceneViews)
|
||||
{
|
||||
if (view == null) continue;
|
||||
|
||||
view.sceneLighting = isEnable;
|
||||
view.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawLightScenario()
|
||||
{
|
||||
if (_scenario == null) return;
|
||||
|
||||
HorizontalLine();
|
||||
_scenario.DrawEditor(_ignoreLighting);
|
||||
_scenario.Apply(_sun);
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightEditor.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightEditor.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eb83d4315df64c9b9fadb3db1672a1b1
|
||||
timeCreated: 1648893669
|
|
@ -0,0 +1,56 @@
|
|||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Rekorn Tools/Light Scenario")]
|
||||
public sealed partial class LightScenario : ScriptableObject, IValidate
|
||||
{
|
||||
[SerializeField] Material Skybox;
|
||||
[SerializeField] bool UseShadow;
|
||||
[SerializeField] float ReflectionIntensity;
|
||||
|
||||
[SerializeField] Vector3 SunDirection;
|
||||
|
||||
[SerializeField] bool SyncColor;
|
||||
[SerializeField] Color SunColor;
|
||||
[SerializeField] float SunIntensity;
|
||||
|
||||
[SerializeField] bool UseSkyboxColor;
|
||||
[SerializeField] Color SkyColor;
|
||||
[SerializeField] float SkyboxIntensity;
|
||||
|
||||
AmbientMode AmbientMode => UseSkyboxColor && !SyncColor ? AmbientMode.Skybox : AmbientMode.Flat;
|
||||
|
||||
public void OnValidate()
|
||||
{
|
||||
var direction = SunDirection;
|
||||
direction.x = Mathf.Clamp(direction.x, -1f, 1f);
|
||||
direction.y = Mathf.Clamp(direction.y, -1f, 1f);
|
||||
direction.z = Mathf.Clamp(direction.z, -1f, 1f);
|
||||
if (direction == Vector3.zero) direction = -Vector3.up;
|
||||
SunDirection = direction;
|
||||
}
|
||||
|
||||
internal void Apply([CanBeNull] Light light)
|
||||
{
|
||||
RenderSettings.skybox = Skybox;
|
||||
RenderSettings.reflectionIntensity = ReflectionIntensity;
|
||||
RenderSettings.ambientMode = AmbientMode;
|
||||
RenderSettings.ambientIntensity = SkyboxIntensity;
|
||||
RenderSettings.ambientLight = (SyncColor ? SunColor : SkyColor) * SkyboxIntensity;
|
||||
RenderSettings.sun = light;
|
||||
|
||||
if (light != null)
|
||||
{
|
||||
light.gameObject.SetActive(true);
|
||||
light.shadowStrength = 1f;
|
||||
light.shadows = UseShadow ? LightShadows.Soft : LightShadows.None;
|
||||
light.transform.rotation = Quaternion.LookRotation(SunDirection);
|
||||
light.color = SunColor;
|
||||
light.intensity = SunIntensity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightScenario.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightScenario.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 271094caa09e4937b2690fdce7b07442
|
||||
timeCreated: 1647705266
|
|
@ -0,0 +1,55 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
using static RekornTools.Avatar.Editor.EditorGUILayoutExtensions;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
public sealed partial class LightScenario
|
||||
{
|
||||
[CustomEditor(typeof(LightScenario))]
|
||||
sealed class LightScenarioDrawer : BaseEditor<LightScenario>
|
||||
{
|
||||
protected override void Draw(LightScenario t)
|
||||
{
|
||||
LabelField("Light Scenario", EditorStyles.boldLabel);
|
||||
|
||||
t.Skybox = ObjectField("Skybox", t.Skybox, false);
|
||||
t.UseShadow = Toggle("Use Shadow", t.UseShadow);
|
||||
t.ReflectionIntensity = Slider("Reflection Intensity", t.ReflectionIntensity, 0, 1);
|
||||
t.SunDirection = Vector3Field("Sun Direction", t.SunDirection);
|
||||
|
||||
HorizontalLine();
|
||||
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Sun Light", GUILayout.Width(100));
|
||||
t.SyncColor = Toggle(t.SyncColor, GUILayout.Width(20));
|
||||
t.SunColor = ColorField(t.SunColor, GUILayout.Width(60));
|
||||
t.SunIntensity = Slider(t.SunIntensity, 0f, 8f);
|
||||
}
|
||||
EndHorizontal();
|
||||
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Ambient Light", GUILayout.Width(100));
|
||||
|
||||
if (t.SyncColor)
|
||||
{
|
||||
LabelField("Color Synced", EditorStyles.helpBox, GUILayout.Width(80));
|
||||
}
|
||||
else
|
||||
{
|
||||
t.UseSkyboxColor = Toggle(t.UseSkyboxColor, GUILayout.Width(20));
|
||||
|
||||
if (t.UseSkyboxColor) LabelField("Skybox", EditorStyles.helpBox, GUILayout.Width(60));
|
||||
else t.SkyColor = ColorField(t.SkyColor, GUILayout.Width(60));
|
||||
}
|
||||
|
||||
t.SkyboxIntensity = Slider(t.SkyboxIntensity, 0f, 8f);
|
||||
}
|
||||
EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightScenarioDrawer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/LightEditor/Editor/LightScenarioDrawer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f03d24aafb5a4791af11e953c3b57ed1
|
||||
timeCreated: 1647701543
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "GUID:3b7023146530b954c921ee55ab65dbff"
|
||||
}
|
7
Assets/RekornTools/Avatar/Components/LightEditor/Editor/RekornTools.Avatar.Editor.asmref.meta
generated
Normal file
7
Assets/RekornTools/Avatar/Components/LightEditor/Editor/RekornTools.Avatar.Editor.asmref.meta
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30a3de862c721a74b9c29fbf15c0e4c2
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar/Components/SkinnedMeshTools.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Components/SkinnedMeshTools.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2fb37031fdf826a42bf75fbf5585f804
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,154 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public enum DresserMode
|
||||
{
|
||||
DirectTransform,
|
||||
ParentConstraint,
|
||||
WeightTransfer,
|
||||
}
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public sealed class AvatarDresser : MonoBehaviour
|
||||
{
|
||||
[SerializeField] DresserMode _dresserMode = DresserMode.DirectTransform;
|
||||
|
||||
[Header("Avatar Settings")]
|
||||
[SerializeField] Animator _avatar;
|
||||
|
||||
[Header("Cloth Settings")]
|
||||
[SerializeField] string _clothPrefix;
|
||||
[SerializeField] string _clothSuffix;
|
||||
[SerializeField] Transform _clothRoot;
|
||||
[SerializeField] Transform _clothArmature;
|
||||
|
||||
[Header("Bone Exclusions")]
|
||||
[SerializeField] [NotNull] BonePairs _boneExclusions = new BonePairs();
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (_clothRoot != null && _clothArmature == null) _clothArmature = _clothRoot.Find("Armature");
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void Apply()
|
||||
{
|
||||
switch (_dresserMode)
|
||||
{
|
||||
case DresserMode.DirectTransform:
|
||||
ApplyDirectTransform();
|
||||
break;
|
||||
case DresserMode.ParentConstraint:
|
||||
ApplyParentConstraint();
|
||||
break;
|
||||
case DresserMode.WeightTransfer:
|
||||
ApplyWeightTransfer();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyDirectTransform()
|
||||
{
|
||||
if (_avatar == null || _clothRoot == null)
|
||||
{
|
||||
this.ShowConfirmDialog("Avatar or cloth is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_clothArmature == null)
|
||||
{
|
||||
this.ShowConfirmDialog("Cloth armature is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
_clothRoot.gameObject.UnpackPrefab();
|
||||
|
||||
var bones = _clothArmature.GetComponentsInChildren<Transform>();
|
||||
var meshes = _clothRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
|
||||
if (bones == null || meshes == null)
|
||||
{
|
||||
this.ShowConfirmDialog("No bones or meshes found.");
|
||||
Undo.PerformUndo();
|
||||
return;
|
||||
}
|
||||
|
||||
var result = CreateMeshBonePairs(_clothRoot.name, transform);
|
||||
result.Bones.AddRange(bones);
|
||||
result.Meshes.AddRange(meshes);
|
||||
|
||||
TransferBones(bones, _avatar.transform);
|
||||
TransferExcludedBones(_boneExclusions);
|
||||
RenameBones(bones, _clothPrefix, _clothSuffix);
|
||||
TransferMeshes(_clothRoot, _avatar.transform);
|
||||
}
|
||||
|
||||
void ApplyParentConstraint()
|
||||
{
|
||||
this.ShowConfirmDialog("ParentConstraint is not implemented yet.");
|
||||
}
|
||||
|
||||
|
||||
void ApplyWeightTransfer()
|
||||
{
|
||||
this.ShowConfirmDialog("WeightTransfer is not implemented yet.");
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
static MeshBonePairs CreateMeshBonePairs([NotNull] string title, [CanBeNull] Transform parent)
|
||||
{
|
||||
var undo = "CreateMeshBonePairs";
|
||||
{
|
||||
var go = new GameObject($"[{title}]");
|
||||
Undo.RegisterCreatedObjectUndo(go, undo);
|
||||
Undo.SetTransformParent(go.transform, parent, undo);
|
||||
return Undo.AddComponent<MeshBonePairs>(go) ?? throw new NullReferenceException(undo);
|
||||
}
|
||||
}
|
||||
|
||||
static void TransferBones([NotNull] IEnumerable<Transform> bones, [NotNull] Transform targetParent)
|
||||
{
|
||||
var undo = "TransferBones";
|
||||
foreach (var bone in bones)
|
||||
{
|
||||
if (bone == null) continue;
|
||||
|
||||
var avatarBone = targetParent.FindRecursive(bone.name);
|
||||
if (avatarBone != null) Undo.SetTransformParent(bone, avatarBone, undo);
|
||||
}
|
||||
}
|
||||
|
||||
static void TransferExcludedBones([NotNull] BonePairs boneExclusions)
|
||||
{
|
||||
var undo = "TransferExcludedBones";
|
||||
foreach (var exclusion in boneExclusions)
|
||||
{
|
||||
if (exclusion == null || exclusion.Source == null) continue;
|
||||
Undo.SetTransformParent(exclusion.Source, exclusion.Target, undo);
|
||||
}
|
||||
}
|
||||
|
||||
static void RenameBones([NotNull] IEnumerable<Transform> bones, [CanBeNull] string prefix, [CanBeNull] string suffix)
|
||||
{
|
||||
var undo = "RenameBones";
|
||||
foreach (var bone in bones)
|
||||
{
|
||||
if (bone == null) continue;
|
||||
bone.UndoableAction(undo, () => bone.name = $"{prefix}{bone.name}{suffix}");
|
||||
}
|
||||
}
|
||||
|
||||
static void TransferMeshes([NotNull] Transform source, [CanBeNull] Transform parent)
|
||||
{
|
||||
var undo = "TransferMeshes";
|
||||
Undo.SetTransformParent(source, parent, undo);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
11
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/AvatarDresser.cs.meta
generated
Normal file
11
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/AvatarDresser.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1736ac66ad9eb1445bbad42ac0db8a2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,107 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(MeshBonePairs))]
|
||||
public sealed class BoneFinder : MonoBehaviour
|
||||
{
|
||||
[field: SerializeField] [CanBeNull] public Transform MeshParent { get; set; }
|
||||
[field: SerializeField] [CanBeNull] public Transform BoneParent { get; set; }
|
||||
|
||||
[field: SerializeField] [CanBeNull] public string MeshKeyword { get; set; }
|
||||
[field: SerializeField] [CanBeNull] public string BoneKeyword { get; set; }
|
||||
|
||||
[CanBeNull] public SkinnedMeshRenderers Meshes => _meshBonePairs ? _meshBonePairs.Meshes : null;
|
||||
[CanBeNull] public Transforms Bones => _meshBonePairs ? _meshBonePairs.Bones : null;
|
||||
|
||||
[CanBeNull] MeshBonePairs _meshBonePairs;
|
||||
|
||||
void Awake() => _meshBonePairs = GetComponent<MeshBonePairs>();
|
||||
|
||||
public void FindMeshesFromTargetWithKeyword()
|
||||
{
|
||||
if (_meshBonePairs == null) return;
|
||||
|
||||
_meshBonePairs.UndoableAction(() => Meshes?.Initialize(MeshParent, MeshKeyword));
|
||||
if (Meshes?.Count == 0) this.ShowConfirmDialog("No objects found");
|
||||
}
|
||||
|
||||
public void FindBonesFromTargetWithKeyword()
|
||||
{
|
||||
if (_meshBonePairs == null) return;
|
||||
|
||||
_meshBonePairs.UndoableAction(() =>
|
||||
{
|
||||
Bones?.Initialize(BoneParent, BoneKeyword);
|
||||
Bones?.RemoveRange(Meshes?.Select(x => x == null ? null : x.transform));
|
||||
Bones?.Remove(BoneParent);
|
||||
});
|
||||
|
||||
if (Bones?.Count == 0) this.ShowConfirmDialog("No objects found");
|
||||
}
|
||||
|
||||
public void FindBonesFromWeights()
|
||||
{
|
||||
if (_meshBonePairs == null) return;
|
||||
|
||||
if (Meshes?.Count == 0)
|
||||
{
|
||||
this.ShowConfirmDialog("There are no meshes to find bones from.");
|
||||
return;
|
||||
}
|
||||
|
||||
var bones = new List<Transform>();
|
||||
|
||||
foreach (var bone in
|
||||
from m in Meshes
|
||||
where m
|
||||
from b in m.bones
|
||||
where b != null && !bones.Contains(b)
|
||||
select b)
|
||||
{
|
||||
bones.Add(bone);
|
||||
}
|
||||
|
||||
_meshBonePairs.UndoableAction(() => Bones?.Initialize(bones));
|
||||
|
||||
if (Bones?.Count == 0)
|
||||
this.ShowConfirmDialog("Failed to find any bones from meshes.\n" +
|
||||
"You might need to check meshes is valid or bone weights are not set to zero.");
|
||||
}
|
||||
|
||||
public void FindBonesFromWeightsRecursive()
|
||||
{
|
||||
FindBonesFromWeights();
|
||||
|
||||
if (_meshBonePairs == null || Bones == null) return;
|
||||
|
||||
var children = GetChildrenRecursive(Bones);
|
||||
_meshBonePairs.UndoableAction(() => Bones.AddRange(children));
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
static List<Transform> GetChildrenRecursive([NotNull] Transforms target)
|
||||
{
|
||||
var children = new List<Transform>();
|
||||
|
||||
foreach (var child in
|
||||
from t in target
|
||||
where t != null
|
||||
select t.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (child == null) continue;
|
||||
children.AddRange(child.Where(c => !AlreadyInList(c)));
|
||||
}
|
||||
|
||||
return children;
|
||||
|
||||
bool AlreadyInList(Transform b) => target.Contains(b) || children.Contains(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/BoneFinder.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/BoneFinder.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 39ebfe4ca23f40638645de61e45056a3
|
||||
timeCreated: 1647154999
|
|
@ -0,0 +1,47 @@
|
|||
#if UNITY_EDITOR
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public sealed class BoneRenamer : MonoBehaviour
|
||||
{
|
||||
[Header("Source")]
|
||||
[SerializeField] Transform _parent;
|
||||
[SerializeField] RigNamingConvention _sourceNaming = RigNamingConvention.Default;
|
||||
|
||||
[Header("Target")]
|
||||
[SerializeField] RigNamingConvention _targetNaming = RigNamingConvention.Default;
|
||||
|
||||
[Header("Rename Token")]
|
||||
[SerializeField] [NotNull] NamePairs _renameToken = new NamePairs();
|
||||
|
||||
[Header("Rename Exclusions")]
|
||||
[SerializeField] [NotNull] Transforms _exclusions = new Transforms();
|
||||
|
||||
[Button]
|
||||
public void Rename()
|
||||
{
|
||||
if (_parent == null) return;
|
||||
|
||||
_parent.InvokeRecursive(x =>
|
||||
{
|
||||
if (x == null || _exclusions.Contains(x)) return;
|
||||
|
||||
var newName = x.name;
|
||||
|
||||
foreach (var token in _renameToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token?.Source)) continue;
|
||||
newName = newName.Replace(token.Source, token.Target);
|
||||
}
|
||||
|
||||
newName = RigNamingConvention.Convert(newName, _sourceNaming, _targetNaming);
|
||||
|
||||
x.gameObject.UndoableAction(() => x.name = newName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/BoneRenamer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/BoneRenamer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 01a7cdc3de5c42298b1b7fab6d20dd23
|
||||
timeCreated: 1652202099
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5827a1255c85cf849b44c5475b00fa3d
|
||||
timeCreated: 1647270296
|
|
@ -0,0 +1,64 @@
|
|||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static RekornTools.Avatar.Editor.EditorGUILayoutExtensions;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomEditor(typeof(BoneFinder))]
|
||||
public sealed class BoneFinderEditor : BaseEditor<BoneFinder>
|
||||
{
|
||||
protected override void Draw(BoneFinder t)
|
||||
{
|
||||
EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth / 5;
|
||||
|
||||
DrawMeshFinder(t);
|
||||
HorizontalLine();
|
||||
|
||||
if (t.Meshes?.Count > 0)
|
||||
{
|
||||
DrawBoneFinder(t);
|
||||
HorizontalLine();
|
||||
DrawWeightedBoneFinder(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawMeshFinder([NotNull] BoneFinder t)
|
||||
{
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Mesh Finder", EditorStyles.boldLabel);
|
||||
if (GUILayout.Button("Find")) t.FindMeshesFromTargetWithKeyword();
|
||||
}
|
||||
EndHorizontal();
|
||||
|
||||
t.MeshParent = ObjectField("Parent", t.MeshParent, true);
|
||||
t.MeshKeyword = TextField("Keyword", t.MeshKeyword);
|
||||
}
|
||||
|
||||
static void DrawBoneFinder([NotNull] BoneFinder t)
|
||||
{
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Bone Finder", EditorStyles.boldLabel);
|
||||
if (GUILayout.Button("Find")) t.FindBonesFromTargetWithKeyword();
|
||||
}
|
||||
EndHorizontal();
|
||||
|
||||
t.BoneParent = ObjectField("Parent", t.BoneParent, true);
|
||||
t.BoneKeyword = TextField("Keyword", t.BoneKeyword);
|
||||
}
|
||||
|
||||
static void DrawWeightedBoneFinder([NotNull] BoneFinder t)
|
||||
{
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Bone Finder (From Weights)", EditorStyles.boldLabel);
|
||||
if (GUILayout.Button("Find")) t.FindBonesFromWeights();
|
||||
if (GUILayout.Button("Find Recursive")) t.FindBonesFromWeightsRecursive();
|
||||
}
|
||||
EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/BoneFinderEditor.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/BoneFinderEditor.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c53f396d5de4481b92de12c77addb82e
|
||||
timeCreated: 1647274570
|
|
@ -0,0 +1,67 @@
|
|||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomEditor(typeof(MeshBonePairs))]
|
||||
public sealed class MeshBonePairsEditor : BaseEditor<MeshBonePairs>
|
||||
{
|
||||
bool _meshesFoldout = true;
|
||||
bool _bonesFoldout = false;
|
||||
|
||||
protected override void Draw(MeshBonePairs t)
|
||||
{
|
||||
EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth / 5;
|
||||
|
||||
DrawLists();
|
||||
DrawButton(t);
|
||||
}
|
||||
|
||||
void DrawLists()
|
||||
{
|
||||
LabelField("Lists", EditorStyles.boldLabel);
|
||||
{
|
||||
_meshesFoldout = Foldout(_meshesFoldout, "Meshes");
|
||||
if (_meshesFoldout) PropertyField(serializedObject.ResolveProperty(nameof(MeshBonePairs.Meshes)), true);
|
||||
|
||||
_bonesFoldout = Foldout(_bonesFoldout, "Bones");
|
||||
if (_bonesFoldout) PropertyField(serializedObject.ResolveProperty(nameof(MeshBonePairs.Bones)), true);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawButton([NotNull] MeshBonePairs t)
|
||||
{
|
||||
LabelField("Actions", EditorStyles.boldLabel);
|
||||
{
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Select Items", EditorStyles.miniBoldLabel);
|
||||
if (GUILayout.Button("All")) t.SelectAll();
|
||||
if (GUILayout.Button("Meshes")) t.SelectMeshes();
|
||||
if (GUILayout.Button("Bones")) t.SelectBones();
|
||||
}
|
||||
EndHorizontal();
|
||||
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Clear List", EditorStyles.miniBoldLabel);
|
||||
if (GUILayout.Button("All")) t.ClearAll();
|
||||
if (GUILayout.Button("Meshes")) t.ClearMeshes();
|
||||
if (GUILayout.Button("Bones")) t.ClearBones();
|
||||
}
|
||||
EndHorizontal();
|
||||
|
||||
BeginHorizontal();
|
||||
{
|
||||
LabelField("Destroy Items", EditorStyles.miniBoldLabel);
|
||||
if (GUILayout.Button("All")) t.DestroyAll();
|
||||
if (GUILayout.Button("Meshes")) t.DestroyMeshes();
|
||||
if (GUILayout.Button("Bones")) t.DestroyBones();
|
||||
}
|
||||
EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/MeshBonePairsEditor.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/MeshBonePairsEditor.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 400c2ba7b80440c186deb21fbfcaee86
|
||||
timeCreated: 1647273214
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "GUID:3b7023146530b954c921ee55ab65dbff"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c4c27fbd96440e4c80aeb8ee460ff77
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,38 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(RigPair<>), true)]
|
||||
public sealed class RigExclusionDrawer : BasePropertyDrawer
|
||||
{
|
||||
protected override void DrawProperty(Rect rect, SerializedProperty property, GUIContent title, int indent)
|
||||
{
|
||||
rect.height = 0f;
|
||||
|
||||
var avatarBoneName = property.ResolveProperty(nameof(RigPair.Target));
|
||||
var clothBoneName = property.ResolveProperty(nameof(RigPair.Source));
|
||||
|
||||
rect.AppendHeight(avatarBoneName);
|
||||
|
||||
var x = rect.x;
|
||||
var width = rect.width;
|
||||
rect.width = 0f;
|
||||
|
||||
rect.AppendWidth(width * 0.45f);
|
||||
clothBoneName.PropertyField(rect);
|
||||
|
||||
rect.AppendWidth(width * 0.1f);
|
||||
EditorGUI.LabelField(rect, "▶", new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter });
|
||||
|
||||
rect.AppendWidth(width * 0.45f);
|
||||
avatarBoneName.PropertyField(rect);
|
||||
|
||||
rect.x = x;
|
||||
rect.width = width;
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent _) =>
|
||||
EditorGUIExtensions.SingleItemHeight;
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/RigExclusionDrawer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/RigExclusionDrawer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ebe222154c9041a0be3358e9d4631ba8
|
||||
timeCreated: 1652015597
|
|
@ -0,0 +1,58 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(RigNamingConvention))]
|
||||
public sealed class RigNamingConventionDrawer : BasePropertyDrawer
|
||||
{
|
||||
protected override void DrawProperty(Rect rect, SerializedProperty property, GUIContent _, int indent)
|
||||
{
|
||||
var x = rect.x;
|
||||
var fullWidth = rect.width;
|
||||
|
||||
rect.width = 0f;
|
||||
rect.height = 0f;
|
||||
rect.AppendHeight(EditorGUIExtensions.SingleItemHeight);
|
||||
|
||||
var type = property.ResolveProperty(nameof(RigNamingConvention.ModifierPosition));
|
||||
var splitter = property.ResolveProperty(nameof(RigNamingConvention.Splitter));
|
||||
var left = property.ResolveProperty(nameof(RigNamingConvention.ModifierLeft));
|
||||
var right = property.ResolveProperty(nameof(RigNamingConvention.ModifierRight));
|
||||
|
||||
DrawElement(0.3f, left);
|
||||
DrawElement(0.3f, right);
|
||||
DrawElement(0.2f, type);
|
||||
DrawElement(0.2f, splitter);
|
||||
|
||||
rect.x = x;
|
||||
rect.width = fullWidth;
|
||||
|
||||
rect.AppendHeight(EditorGUIExtensions.SingleItemHeight);
|
||||
EditorGUI.HelpBox(rect, GetPreviewText(), MessageType.Info);
|
||||
|
||||
void DrawElement(float weight, SerializedProperty prop)
|
||||
{
|
||||
var padding = EditorGUIUtility.standardVerticalSpacing;
|
||||
rect.AppendWidth(fullWidth * weight - padding);
|
||||
prop.PropertyField(rect);
|
||||
rect.AppendWidth(padding);
|
||||
}
|
||||
|
||||
string GetPreviewText()
|
||||
{
|
||||
var t = type?.enumValueIndex;
|
||||
var s = splitter?.stringValue;
|
||||
var l = left?.stringValue;
|
||||
var r = right?.stringValue;
|
||||
|
||||
return t == (int)ModifierPosition.Front
|
||||
? $"Head / {l}{s}Arm / {r}{s}Leg / {r}{s}Leg.001"
|
||||
: $"Head / Arm{s}{l} / Leg{s}{r} / Leg{s}{r}.001";
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent _) =>
|
||||
EditorGUIExtensions.SingleItemHeight * 2;
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/RigNamingConventionDrawer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/Editor/RigNamingConventionDrawer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 279b4f784c124925bc94a2fd1caf4e42
|
||||
timeCreated: 1652010490
|
|
@ -0,0 +1,49 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public sealed class MeshBonePairs : MonoBehaviour
|
||||
{
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Local
|
||||
[field: SerializeField] [NotNull] public SkinnedMeshRenderers Meshes { get; private set; } = new SkinnedMeshRenderers();
|
||||
[field: SerializeField] [NotNull] public Transforms Bones { get; private set; } = new Transforms();
|
||||
// ReSharper restore AutoPropertyCanBeMadeGetOnly.Local
|
||||
|
||||
public void SelectAll()
|
||||
{
|
||||
var selection = new Object[] { };
|
||||
|
||||
if (Meshes.TryGetSelections(out var meshSelections))
|
||||
selection = selection.Concat(meshSelections).ToArray();
|
||||
if (Bones.TryGetSelections(out var boneSelections))
|
||||
selection = selection.Concat(boneSelections).ToArray();
|
||||
|
||||
if (selection.Length > 0) Selection.objects = selection;
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
ClearMeshes();
|
||||
ClearBones();
|
||||
}
|
||||
|
||||
public void DestroyAll()
|
||||
{
|
||||
DestroyMeshes();
|
||||
DestroyBones();
|
||||
}
|
||||
|
||||
public void SelectMeshes() => Meshes.SelectComponents();
|
||||
public void SelectBones() => Bones.SelectComponents();
|
||||
public void ClearMeshes() => this.UndoableAction(() => Meshes.Clear());
|
||||
public void ClearBones() => this.UndoableAction(() => Bones.Clear());
|
||||
public void DestroyMeshes() => this.UndoableAction(() => Meshes.DestroyItems());
|
||||
public void DestroyBones() => this.UndoableAction(() => Bones.DestroyItems());
|
||||
}
|
||||
}
|
||||
#endif
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/MeshBonePairs.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/MeshBonePairs.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2a8d66205c7440078d1f5d62a41e3baa
|
||||
timeCreated: 1647268279
|
|
@ -0,0 +1,95 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public sealed class MeshOptimizer : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Transform parent;
|
||||
[SerializeField] SkinnedMeshRenderers meshes = new SkinnedMeshRenderers();
|
||||
[SerializeField] Transform anchorOverride;
|
||||
[SerializeField] Bounds boundingBox = new Bounds(Vector3.zero, Vector3.one * 2);
|
||||
|
||||
[SerializeField] [HideInInspector] Transform _prevParent;
|
||||
|
||||
void Awake() => Refresh();
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (_prevParent != parent) Refresh();
|
||||
}
|
||||
|
||||
[Button]
|
||||
void Refresh()
|
||||
{
|
||||
_prevParent = parent;
|
||||
meshes?.Initialize(parent);
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
if (meshes == null) return;
|
||||
foreach (var mesh in meshes)
|
||||
{
|
||||
if (mesh == null) continue;
|
||||
|
||||
var prevBounds = mesh.localBounds;
|
||||
{
|
||||
DrawBounds(mesh, Color.yellow);
|
||||
|
||||
mesh.localBounds = boundingBox;
|
||||
RepaintRenderer(mesh);
|
||||
|
||||
DrawBounds(mesh, Color.green);
|
||||
}
|
||||
mesh.localBounds = prevBounds;
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawBounds([System.Diagnostics.CodeAnalysis.NotNull] Renderer renderer, Color color)
|
||||
{
|
||||
var rotation = renderer.transform.rotation;
|
||||
if (renderer is SkinnedMeshRenderer)
|
||||
{
|
||||
var transformRoot = renderer.transform.root;
|
||||
if (transformRoot != null)
|
||||
rotation = transformRoot.rotation;
|
||||
}
|
||||
|
||||
var bounds = renderer.bounds;
|
||||
var matrix = Matrix4x4.TRS(bounds.center, rotation, bounds.size);
|
||||
Gizmos.matrix = matrix;
|
||||
Gizmos.color = color;
|
||||
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "Unity.InefficientPropertyAccess")]
|
||||
static void RepaintRenderer([System.Diagnostics.CodeAnalysis.NotNull] Renderer renderer)
|
||||
{
|
||||
renderer.enabled = false;
|
||||
renderer.enabled = true;
|
||||
}
|
||||
|
||||
[Button]
|
||||
void Optimize()
|
||||
{
|
||||
if (meshes == null) return;
|
||||
foreach (var mesh in meshes)
|
||||
{
|
||||
if (mesh == null) continue;
|
||||
mesh.UndoableAction(() =>
|
||||
{
|
||||
mesh.probeAnchor = anchorOverride;
|
||||
mesh.localBounds = boundingBox;
|
||||
mesh.updateWhenOffscreen = false;
|
||||
mesh.skinnedMotionVectors = false;
|
||||
mesh.allowOcclusionWhenDynamic = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/MeshOptimizer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/MeshOptimizer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 25aa88844e30426d955b4cd11ffc1b85
|
||||
timeCreated: 1647594398
|
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public enum ModifierPosition
|
||||
{
|
||||
Front,
|
||||
End,
|
||||
}
|
||||
|
||||
public enum ModifierType
|
||||
{
|
||||
None,
|
||||
Both,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct RigNamingConvention
|
||||
{
|
||||
public static RigNamingConvention Default = new RigNamingConvention
|
||||
{
|
||||
ModifierPosition = ModifierPosition.End,
|
||||
Splitter = "_",
|
||||
ModifierLeft = "L",
|
||||
ModifierRight = "R",
|
||||
};
|
||||
|
||||
[field: SerializeField] public ModifierPosition ModifierPosition { get; private set; }
|
||||
[field: SerializeField] [CanBeNull] public string Splitter { get; private set; }
|
||||
[field: SerializeField] [CanBeNull] public string ModifierLeft { get; private set; }
|
||||
[field: SerializeField] [CanBeNull] public string ModifierRight { get; private set; }
|
||||
|
||||
public RigNamingConvention(ModifierPosition modifierPosition, [CanBeNull] string splitter, [CanBeNull] string modifierLeft, [CanBeNull] string modifierRight)
|
||||
{
|
||||
ModifierPosition = modifierPosition;
|
||||
Splitter = splitter;
|
||||
ModifierLeft = modifierLeft;
|
||||
ModifierRight = modifierRight;
|
||||
}
|
||||
|
||||
[NotNull] public string FrontLeft => $"{ModifierLeft}{Splitter}";
|
||||
[NotNull] public string FrontRight => $"{ModifierRight}{Splitter}";
|
||||
[NotNull] public string EndLeft => $"{Splitter}{ModifierLeft}";
|
||||
[NotNull] public string EndRight => $"{Splitter}{ModifierRight}";
|
||||
|
||||
public string GetModifier(ModifierType type)
|
||||
{
|
||||
var pos = ModifierPosition;
|
||||
if (pos == ModifierPosition.Front && type == ModifierType.Left) return FrontLeft;
|
||||
if (pos == ModifierPosition.Front && type == ModifierType.Right) return FrontRight;
|
||||
if (pos == ModifierPosition.End && type == ModifierType.Left) return EndLeft;
|
||||
if (pos == ModifierPosition.End && type == ModifierType.Right) return EndRight;
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModifierType GetModifierType([NotNull] string name)
|
||||
{
|
||||
var isLeft = false;
|
||||
var isRight = false;
|
||||
|
||||
if (ModifierPosition == ModifierPosition.Front)
|
||||
{
|
||||
isLeft = name.Contains(FrontLeft);
|
||||
isRight = name.Contains(FrontRight);
|
||||
}
|
||||
else
|
||||
{
|
||||
isLeft = name.Contains(EndLeft);
|
||||
isRight = name.Contains(EndRight);
|
||||
}
|
||||
|
||||
if (isLeft && isRight) return ModifierType.Both;
|
||||
else if (isLeft) return ModifierType.Left;
|
||||
else if (isRight) return ModifierType.Right;
|
||||
else return ModifierType.None;
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public static string Convert([NotNull] string name, RigNamingConvention src, RigNamingConvention dst)
|
||||
{
|
||||
var type = src.GetModifierType(name);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ModifierType.None:
|
||||
return name;
|
||||
case ModifierType.Both:
|
||||
Debug.LogWarning($"Bone name {name} contains both left and right modifiers. Skipping.");
|
||||
return name;
|
||||
case ModifierType.Left:
|
||||
case ModifierType.Right:
|
||||
var from = src.GetModifier(type) ?? throw new ArgumentException("Unknown modifier type.");
|
||||
var to = dst.GetModifier(type) ?? throw new ArgumentException("Unknown modifier type.");
|
||||
return ReplaceModifier(name, from, to, src.ModifierPosition, dst.ModifierPosition);
|
||||
default:
|
||||
throw new ArgumentException("Unknown modifier type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[NotNull]
|
||||
static string ReplaceModifier([NotNull] string name, [NotNull] string from, [NotNull] string to, ModifierPosition srcPosition, ModifierPosition dstPosition)
|
||||
{
|
||||
if (srcPosition == dstPosition)
|
||||
{
|
||||
return name.Replace(from, to);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (srcPosition == ModifierPosition.Front)
|
||||
{
|
||||
return name.Replace(from, null) + to;
|
||||
}
|
||||
else
|
||||
{
|
||||
return to + name.Replace(from, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/RigNamingConvention.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/RigNamingConvention.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 14b6076337fd4c8683016eb662323a15
|
||||
timeCreated: 1652010530
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public sealed class RigPair : RigPair<object> { }
|
||||
|
||||
[Serializable]
|
||||
public class RigPair<T>
|
||||
{
|
||||
[field: SerializeField] public T Target { get; private set; }
|
||||
[field: SerializeField] public T Source { get; private set; }
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/RigPair.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/RigPair.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f21f9635a0f244929d34173bf8bad8e0
|
||||
timeCreated: 1652015576
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
#region List
|
||||
[Serializable] public sealed class SkinnedMeshRenderers : ComponentList<SkinnedMeshRenderer> { }
|
||||
|
||||
[Serializable] public sealed class Transforms : ComponentList<Transform> { }
|
||||
|
||||
[Serializable] public sealed class BonePairs : SerializedList<BonePair> { }
|
||||
|
||||
[Serializable] public sealed class NamePairs : SerializedList<NamePair> { }
|
||||
|
||||
[Serializable] public sealed class BonePair : RigPair<Transform> { }
|
||||
|
||||
[Serializable] public sealed class NamePair : RigPair<string> { }
|
||||
#endregion // List
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/_Generic.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/SkinnedMeshTools/_Generic.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87c24b26e4ff49d19fef1a3bd6179aa3
|
||||
timeCreated: 1648312351
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c74404ee945c407d89bcc4399747bca0
|
||||
timeCreated: 1649570418
|
|
@ -0,0 +1,62 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public sealed class AssetManager : MonoBehaviour, IValidate
|
||||
{
|
||||
[SerializeField] public Transform Parent;
|
||||
|
||||
[SerializeField] [ReadOnlyList] [NotNull] public Renderers Renderers = new Renderers();
|
||||
[SerializeField] [ReadOnlyList] [NotNull] public Materials Materials = new Materials();
|
||||
[SerializeField] [ReadOnlyList] [NotNull] public Shaders Shaders = new Shaders();
|
||||
[SerializeField] [ReadOnlyList] [NotNull] public Textures Textures = new Textures();
|
||||
|
||||
[SerializeField] [HideInInspector] Transform _prevParent;
|
||||
|
||||
void Awake() => Refresh();
|
||||
|
||||
public void OnValidate()
|
||||
{
|
||||
if (_prevParent != Parent) Refresh();
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void Refresh()
|
||||
{
|
||||
_prevParent = Parent;
|
||||
Renderers.Initialize(Parent);
|
||||
|
||||
Materials.Clear();
|
||||
Shaders.Clear();
|
||||
Textures.Clear();
|
||||
|
||||
foreach (var (material, shader) in
|
||||
from r in Renderers
|
||||
from material in r.sharedMaterials
|
||||
select (material, material.shader))
|
||||
{
|
||||
if (!Materials.Contains(material)) Materials.Add(material);
|
||||
if (!Shaders.Contains(shader)) Shaders.Add(shader);
|
||||
|
||||
AppendTexturesList(material, shader);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendTexturesList([CanBeNull] Material material, [CanBeNull] Shader shader)
|
||||
{
|
||||
if (material == null || shader == null) return;
|
||||
for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
|
||||
{
|
||||
if (ShaderUtil.GetPropertyType(shader, i) != ShaderUtil.ShaderPropertyType.TexEnv) continue;
|
||||
var texture = material.GetTexture(ShaderUtil.GetPropertyName(shader, i));
|
||||
if (texture != null && !Textures.Contains(texture)) Textures.Add(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
11
Assets/RekornTools/Avatar/Components/TextureOptimizer/AssetManager.cs.meta
generated
Normal file
11
Assets/RekornTools/Avatar/Components/TextureOptimizer/AssetManager.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c2ae58bf68685849a8aea8c5e5de75e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27bc343648a6c9849b4f183357667f96
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,40 @@
|
|||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static RekornTools.Avatar.Editor.EditorGUILayoutExtensions;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomEditor(typeof(AssetManager))]
|
||||
public sealed class AssetManagerEditor : BaseEditor<AssetManager>
|
||||
{
|
||||
bool _renderersFoldout;
|
||||
bool _materialsFoldout;
|
||||
bool _shadersFoldout;
|
||||
bool _texturesFoldout;
|
||||
|
||||
protected override void Draw(AssetManager t)
|
||||
{
|
||||
t.Parent = ObjectField("Parent", t.Parent, true);
|
||||
|
||||
DrawLists(ref _renderersFoldout, nameof(AssetManager.Renderers), t.Renderers);
|
||||
DrawLists(ref _materialsFoldout, nameof(AssetManager.Materials), t.Materials);
|
||||
DrawLists(ref _shadersFoldout, nameof(AssetManager.Shaders), t.Shaders);
|
||||
DrawLists(ref _texturesFoldout, nameof(AssetManager.Textures), t.Textures);
|
||||
|
||||
if (GUILayout.Button("Refresh")) t.Refresh();
|
||||
}
|
||||
|
||||
void DrawLists<T>(ref bool foldout, [NotNull] string property, [NotNull] ObjectList<T> list) where T : Object
|
||||
{
|
||||
BeginHorizontal();
|
||||
{
|
||||
foldout = Foldout(foldout, typeof(T).Name);
|
||||
if (GUILayout.Button("Select All")) list.SelectComponents();
|
||||
}
|
||||
EndHorizontal();
|
||||
if (foldout) PropertyField(serializedObject.ResolveProperty(property), true);
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/AssetManagerEditor.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/AssetManagerEditor.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc4868172d3a484d88f546bd3ddda028
|
||||
timeCreated: 1651999120
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "GUID:3b7023146530b954c921ee55ab65dbff"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b97982f805b37b746876c09880c83db3
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(ShaderProperty))]
|
||||
public sealed class ShaderPropertyDrawer : BasePropertyDrawer
|
||||
{
|
||||
protected override void DrawProperty(Rect rect, SerializedProperty property, GUIContent _, int __)
|
||||
{
|
||||
var padding = EditorGUIUtility.standardVerticalSpacing;
|
||||
var fullWidth = rect.width;
|
||||
|
||||
rect.width = 0f;
|
||||
|
||||
DrawElement(0.15f, nameof(ShaderProperty.Shader));
|
||||
DrawElement(0.1f, nameof(ShaderProperty.Index));
|
||||
DrawElement(0.15f, nameof(ShaderProperty.Type));
|
||||
DrawElement(0.4f, nameof(ShaderProperty.Name));
|
||||
DrawElement(0.2f, nameof(ShaderProperty.TextureType), false);
|
||||
|
||||
void DrawElement(float weight, string propertyName, bool isReadOnly = true)
|
||||
{
|
||||
if (propertyName == null) return;
|
||||
|
||||
var prop = property.ResolveProperty(propertyName);
|
||||
rect.AppendWidth(fullWidth * weight - padding);
|
||||
prop.DisabledPropertyField(rect, isReadOnly);
|
||||
rect.AppendWidth(padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/ShaderPropertyDrawer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/ShaderPropertyDrawer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 24cd6b4b6ad94fcdb9b936ea07df86c4
|
||||
timeCreated: 1647777014
|
|
@ -0,0 +1,46 @@
|
|||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(TexturePropertiesByShader))]
|
||||
public sealed class TexturePropertiesByShaderDrawer : SerializedKeyValueDrawer
|
||||
{
|
||||
[NotNull] readonly ReorderableListHelper _listHelper = new ReorderableListHelper(SerializedList.FieldName, true);
|
||||
|
||||
protected override void DrawProperty(Rect rect, SerializedProperty property, GUIContent _, int indent)
|
||||
{
|
||||
var helper = Helper.Update(property);
|
||||
var key = helper.Key;
|
||||
var value = helper.Value;
|
||||
|
||||
rect.ApplyIndent(++indent);
|
||||
var foldoutWidth = 10f;
|
||||
var keyWidth = rect.width - foldoutWidth;
|
||||
|
||||
rect.width = 0f;
|
||||
rect.height = helper.KeyHeight;
|
||||
|
||||
rect.AppendWidth(foldoutWidth);
|
||||
property.isExpanded = EditorGUI.Foldout(rect, property.isExpanded, GUIContent.none);
|
||||
|
||||
rect.AppendWidth(keyWidth);
|
||||
key.DisabledPropertyField(rect);
|
||||
|
||||
if (property.isExpanded)
|
||||
{
|
||||
rect.AppendHeight(helper.ValueHeight);
|
||||
_listHelper.Update(value).Draw(rect);
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent _)
|
||||
{
|
||||
var helper = Helper.Update(property);
|
||||
return property?.isExpanded ?? false
|
||||
? helper.ValueHeight
|
||||
: helper.KeyHeight;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37bf0b354c0846e39f5f593c120db2fd
|
||||
timeCreated: 1651138620
|
|
@ -0,0 +1,31 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(TextureProperty))]
|
||||
public sealed class TexturePropertyDrawer : BasePropertyDrawer
|
||||
{
|
||||
protected override void DrawProperty(Rect rect, SerializedProperty property, GUIContent _, int __)
|
||||
{
|
||||
var padding = EditorGUIUtility.standardVerticalSpacing;
|
||||
var fullWidth = rect.width;
|
||||
|
||||
rect.width = 0f;
|
||||
|
||||
DrawElement(0.2f, nameof(ShaderProperty.Index));
|
||||
DrawElement(0.6f, nameof(ShaderProperty.Name));
|
||||
DrawElement(0.2f, nameof(ShaderProperty.TextureType), false);
|
||||
|
||||
void DrawElement(float weight, string propertyName, bool isReadOnly = true)
|
||||
{
|
||||
if (propertyName == null) return;
|
||||
|
||||
var prop = property.ResolveProperty(propertyName);
|
||||
rect.AppendWidth(fullWidth * weight - padding);
|
||||
prop.DisabledPropertyField(rect, isReadOnly);
|
||||
rect.AppendWidth(padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/TexturePropertyDrawer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/Editor/TexturePropertyDrawer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c89824b979c0494ca396f2f9255207d0
|
||||
timeCreated: 1651137470
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[Serializable]
|
||||
public class TextureProperty : ShaderProperty
|
||||
{
|
||||
public TextureProperty([NotNull] Shader shader, int index, ShaderPropertyType type, [NotNull] string name) : base(shader, index, type, name) { }
|
||||
|
||||
public TextureProperty([NotNull] Shader shader, int index) : base(shader, index) { }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderProperty
|
||||
{
|
||||
[field: SerializeField] public Shader Shader { get; private set; }
|
||||
[field: SerializeField] public int Index { get; private set; }
|
||||
[field: SerializeField] public ShaderPropertyType Type { get; private set; }
|
||||
[field: SerializeField] public string Name { get; private set; }
|
||||
[field: SerializeField] public TextureType TextureType { get; set; }
|
||||
|
||||
public ShaderProperty([NotNull] Shader shader, int index, ShaderPropertyType type, [NotNull] string name)
|
||||
{
|
||||
Shader = shader;
|
||||
Index = index;
|
||||
Type = type;
|
||||
Name = name;
|
||||
|
||||
if (type == ShaderPropertyType.Texture) TextureType = AssumeTextureType(name);
|
||||
}
|
||||
|
||||
public ShaderProperty([NotNull] Shader shader, int index) :
|
||||
this(shader, index, shader.GetPropertyType(index), shader.GetPropertyName(index) ?? "null") { }
|
||||
|
||||
static TextureType AssumeTextureType([NotNull] string name)
|
||||
{
|
||||
var token = name.ToLower();
|
||||
|
||||
if (token.Contains("normal")) return TextureType.Normal;
|
||||
if (token.Contains("emissi")) return TextureType.Emissive;
|
||||
if (token.Contains("sample") || token.Contains("pos")) return TextureType.Sampler;
|
||||
if (token.Contains("mask") || token.Contains("map")) return TextureType.Mask;
|
||||
|
||||
return TextureType.Default;
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/ShaderProperty.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/ShaderProperty.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6377e8b9104b46b8b6ce76e326d14812
|
||||
timeCreated: 1647783353
|
|
@ -0,0 +1,164 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public sealed class TextureOptimizer : MonoBehaviour
|
||||
{
|
||||
[Header("Target")]
|
||||
[SerializeField] Transform _parent;
|
||||
[SerializeField] [NotNull] Renderers _meshes = new Renderers();
|
||||
|
||||
[Header("Textures")]
|
||||
[SerializeField] TexturePropertiesTable _propertiesTable;
|
||||
[SerializeField] [ReadOnlyList] [ItemNotSpan] [NotNull] TexturesMapByType _texturesMap = new TexturesMapByType();
|
||||
|
||||
[Header("Optimizer")]
|
||||
[SerializeField] TextureOptimizerSettings _optimizerSettings;
|
||||
|
||||
[SerializeField] [HideInInspector] Transform _prevParent;
|
||||
[SerializeField] [HideInInspector] TexturePropertiesTable _prevTable;
|
||||
|
||||
void Awake() => _meshes.Initialize(_parent);
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (_prevParent != _parent || _prevTable != _propertiesTable) Refresh();
|
||||
}
|
||||
|
||||
[Button]
|
||||
void Refresh()
|
||||
{
|
||||
_prevParent = _parent;
|
||||
_meshes.Initialize(_parent);
|
||||
|
||||
_prevTable = _propertiesTable;
|
||||
ResetTexturesMap();
|
||||
AssignTexturesMap();
|
||||
}
|
||||
|
||||
void ResetTexturesMap()
|
||||
{
|
||||
_texturesMap.Clear();
|
||||
_texturesMap.MatchDictionaryKey(_ => new Textures());
|
||||
_texturesMap.Remove(TextureType.Ignore);
|
||||
}
|
||||
|
||||
void AssignTexturesMap()
|
||||
{
|
||||
if (_propertiesTable == null) return;
|
||||
foreach (var properties in _propertiesTable.TexturePropertiesMap.Values)
|
||||
{
|
||||
if (properties == null) continue;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (property == null) continue;
|
||||
AssignTextureProperty(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssignTextureProperty([NotNull] ShaderProperty property)
|
||||
{
|
||||
var type = property.TextureType;
|
||||
if (type == TextureType.Ignore) return;
|
||||
_texturesMap[type]?.AddRange(GetTextureList(property));
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
Textures GetTextureList([NotNull] ShaderProperty property)
|
||||
{
|
||||
var list = new Textures();
|
||||
foreach (var mesh in _meshes)
|
||||
{
|
||||
if (mesh == null) continue;
|
||||
AddTextures(mesh.sharedMaterials);
|
||||
}
|
||||
|
||||
return list.Count == 0 ? null : list;
|
||||
|
||||
void AddTextures(Material[] materials)
|
||||
{
|
||||
if (materials == null) return;
|
||||
foreach (var material in materials)
|
||||
if (TryFindMatchingTexture(material, property, out var texture))
|
||||
AppendTexture(texture);
|
||||
}
|
||||
|
||||
void AppendTexture(Texture texture)
|
||||
{
|
||||
if (!list.Contains(texture)) list.Add(texture);
|
||||
}
|
||||
}
|
||||
|
||||
bool TryFindMatchingTexture([CanBeNull] Material material, [NotNull] ShaderProperty property, [CanBeNull] out Texture texture)
|
||||
{
|
||||
texture = null;
|
||||
if (material == null) return false;
|
||||
if (material.shader != property.Shader) return false;
|
||||
if (!material.HasProperty(property.Name)) return false;
|
||||
|
||||
texture = material.GetTexture(property.Name);
|
||||
if (texture == null) return false;
|
||||
if (IsTextureExist(texture)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTextureExist([NotNull] Texture texture) =>
|
||||
_texturesMap.Any(x => x.Value?.Contains(texture) == true);
|
||||
|
||||
[Button]
|
||||
void Optimize()
|
||||
{
|
||||
if (_propertiesTable != null) _propertiesTable.UpdateTable();
|
||||
|
||||
if (_optimizerSettings == null)
|
||||
{
|
||||
this.ShowConfirmDialog("You need to set the optimizer settings first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EditorUtility.DisplayDialog("Warning"
|
||||
, "This operation will reimport all textures from list. "
|
||||
+ "This operation can't be undone, and takes huge amount of time."
|
||||
, "Proceed"
|
||||
, "Abort")) return;
|
||||
|
||||
ApplyPreset(_texturesMap, _optimizerSettings.PresetMap);
|
||||
}
|
||||
|
||||
void ApplyPreset([NotNull] TexturesMapByType texturesMapByType, [NotNull] TexturePresetMapByType presets)
|
||||
{
|
||||
var count = 0;
|
||||
var prevSize = new AssetSize(0L, 0L);
|
||||
var newSize = new AssetSize(0L, 0L);
|
||||
|
||||
foreach (var map in texturesMapByType)
|
||||
{
|
||||
if (!presets.TryGetValue(map.Key, out var preset)) continue;
|
||||
if (preset == null) continue;
|
||||
if (map.Value == null) continue;
|
||||
|
||||
foreach (var texture in map.Value)
|
||||
{
|
||||
count++;
|
||||
prevSize += AssetSize.GetAssetSize(texture);
|
||||
AssetHelper.ApplyPreset(texture, preset);
|
||||
newSize += AssetSize.GetAssetSize(texture);
|
||||
}
|
||||
}
|
||||
|
||||
var savedSize = newSize - prevSize;
|
||||
this.ShowConfirmDialog($"Total {count} textures optimized.\n"
|
||||
+ $"[Before] {prevSize.ToString()}\n"
|
||||
+ $"[After] {newSize.ToString()}\n"
|
||||
+ $"[Saved] {savedSize.ToString()}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureOptimizer.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureOptimizer.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b3cf9caebe14c7a815cfbf232f86cdd
|
||||
timeCreated: 1647707466
|
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Presets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Rekorn Tools/Texture Optimizer Settings")]
|
||||
public sealed class TextureOptimizerSettings : ScriptableObject, IValidate
|
||||
{
|
||||
[SerializeField] [ReadOnlyList] [NotNull]
|
||||
public TexturePresetMapByType PresetMap = new TexturePresetMapByType();
|
||||
|
||||
[NotNull] readonly IReadOnlyCollection<TextureType> _types = DictionaryExtensions.GetKeys<TextureType>();
|
||||
|
||||
public void OnEnable() => UpdateTable();
|
||||
|
||||
public void OnValidate() => ClearInvalidPreset();
|
||||
|
||||
void UpdateTable()
|
||||
{
|
||||
PresetMap.MatchDictionaryKey(_ => default);
|
||||
PresetMap.Remove(TextureType.Ignore);
|
||||
}
|
||||
|
||||
void ClearInvalidPreset()
|
||||
{
|
||||
foreach (var type in _types)
|
||||
{
|
||||
if (type == TextureType.Ignore) continue;
|
||||
if (!PresetMap.TryGetValue(type, out var preset)) continue;
|
||||
if (preset == null) continue;
|
||||
|
||||
if (!IsPresetTypeValid(preset, $"{nameof(UnityEditor)}.{nameof(TextureImporter)}"))
|
||||
PresetMap[type] = null;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPresetTypeValid([NotNull] Preset preset, [NotNull] string name) =>
|
||||
preset.GetPresetType().GetManagedTypeName()?.Equals(name) ?? false;
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureOptimizerSettings.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureOptimizerSettings.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a3f4698d610241f38d8d77c8b50f9520
|
||||
timeCreated: 1647969551
|
|
@ -0,0 +1,51 @@
|
|||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Rekorn Tools/Texture Properties Table")]
|
||||
public sealed class TexturePropertiesTable : ScriptableObject
|
||||
{
|
||||
[SerializeField] [ReadOnlyList] [ItemNotSpan] [NotNull]
|
||||
public TexturePropertiesMapByShader TexturePropertiesMap = new TexturePropertiesMapByShader();
|
||||
|
||||
public void OnEnable() => UpdateTable();
|
||||
|
||||
[Button]
|
||||
public void UpdateTable()
|
||||
{
|
||||
var shaders = ShaderPropertyExtensions.AllUserShadersInProject?.ToList();
|
||||
if (shaders == null) return;
|
||||
|
||||
TexturePropertiesMap.MatchDictionaryKey(shaders, GetTexturePropertyList);
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void ResetTable()
|
||||
{
|
||||
TexturePropertiesMap.Clear();
|
||||
UpdateTable();
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
public static TextureProperties GetTexturePropertyList([CanBeNull] Shader shader)
|
||||
{
|
||||
if (shader == null) return null;
|
||||
|
||||
var count = shader.GetPropertyCount();
|
||||
if (count == 0) return null;
|
||||
|
||||
var properties = new TextureProperties();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (shader.GetPropertyFlags(i) == ShaderPropertyFlags.HideInInspector) continue;
|
||||
if (shader.GetPropertyType(i) != ShaderPropertyType.Texture) continue;
|
||||
properties.Add(new TextureProperty(shader, i));
|
||||
}
|
||||
|
||||
return properties.Count == 0 ? null : properties;
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TexturePropertiesTable.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TexturePropertiesTable.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 89d3ec4237704783bffc3ea47b9602ba
|
||||
timeCreated: 1649259700
|
|
@ -0,0 +1,15 @@
|
|||
namespace RekornTools.Avatar
|
||||
{
|
||||
public enum TextureType
|
||||
{
|
||||
Ignore,
|
||||
Default,
|
||||
Opaque,
|
||||
Alpha,
|
||||
Emissive,
|
||||
Normal,
|
||||
Mask,
|
||||
Sampler,
|
||||
Icon,
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureType.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/TextureType.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a195c1996f2d4f5fa94918055616049d
|
||||
timeCreated: 1647969618
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using UnityEditor.Presets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
#region BasicList
|
||||
[Serializable] public sealed class Materials : ObjectList<Material> { }
|
||||
|
||||
[Serializable] public sealed class Shaders : ObjectList<Shader> { }
|
||||
|
||||
[Serializable] public sealed class Textures : ObjectList<Texture> { }
|
||||
#endregion // BasicList
|
||||
|
||||
#region List
|
||||
[Serializable] public sealed class Renderers : ComponentList<Renderer> { }
|
||||
|
||||
[Serializable] public sealed class TextureProperties : SerializedList<TextureProperty> { }
|
||||
|
||||
[Serializable] public sealed class ShaderProperties : SerializedList<ShaderProperty> { }
|
||||
#endregion // List
|
||||
|
||||
#region Dictionary
|
||||
[Serializable] public sealed class TexturePresetMapByType : SerializedDictionary<TexturePresetByType, TextureType, Preset> { }
|
||||
|
||||
[Serializable] public sealed class TexturesMapByType : SerializedDictionary<TexturesByType, TextureType, Textures> { }
|
||||
|
||||
[Serializable] public sealed class TexturePropertiesMapByShader : SerializedDictionary<TexturePropertiesByShader, Shader, TextureProperties> { }
|
||||
#endregion // Dictionary
|
||||
|
||||
#region KeyValue
|
||||
[Serializable] public sealed class TexturePresetByType : HorizontalKeyValue<TextureType, Preset> { }
|
||||
|
||||
[Serializable] public sealed class TexturesByType : HorizontalKeyValue<TextureType, Textures> { }
|
||||
|
||||
[Serializable] public sealed class TexturePropertiesByShader : SerializedKeyValue<Shader, TextureProperties> { }
|
||||
#endregion // KeyValue
|
||||
}
|
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/_Generic.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Components/TextureOptimizer/_Generic.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 23146dc6476945f5a8e68d26668fb2ab
|
||||
timeCreated: 1649590899
|
8
Assets/RekornTools/Avatar/Core.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Core.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29a60d6815d0ef34cb350ccb2de0dd06
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
3
Assets/RekornTools/Avatar/Core/Attribute.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Attribute.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f1d673a55cf464346bbd91d1668d7408
|
||||
timeCreated: 1647965920
|
|
@ -0,0 +1,7 @@
|
|||
using System;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class ButtonAttribute : Attribute { }
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Attribute/ButtonAttribute.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Attribute/ButtonAttribute.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b1f8999d73645448c2b92698a946981
|
||||
timeCreated: 1651836246
|
|
@ -0,0 +1,7 @@
|
|||
using System;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ItemNotSpanAttribute : Attribute { }
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Attribute/ItemNotSpanAttribute.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Attribute/ItemNotSpanAttribute.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f59b59bc60b84f45994be7a7a285252d
|
||||
timeCreated: 1647965702
|
|
@ -0,0 +1,7 @@
|
|||
using System;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ReadOnlyListAttribute : Attribute { }
|
||||
}
|
11
Assets/RekornTools/Avatar/Core/Attribute/ReadOnlyListAttribute.cs.meta
generated
Normal file
11
Assets/RekornTools/Avatar/Core/Attribute/ReadOnlyListAttribute.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 18838e3ca6514e5b8572f939b2f476d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
78
Assets/RekornTools/Avatar/Core/ComponentList.cs
Normal file
78
Assets/RekornTools/Avatar/Core/ComponentList.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[System.Serializable]
|
||||
public class ComponentList<T> : ObjectList<T> where T : Component
|
||||
{
|
||||
void ShowDialog([NotNull] string message)
|
||||
{
|
||||
var header = $"[{nameof(ObjectList<T>)}<{typeof(T).Name}>]";
|
||||
Debug.LogWarning($"{header} {message}");
|
||||
EditorUtility.DisplayDialog(header, message, "Confirm");
|
||||
}
|
||||
|
||||
#region UnityObject
|
||||
public void DestroyItems()
|
||||
{
|
||||
var destroyTarget = new List<T>();
|
||||
var destroyFailed = new StringBuilder();
|
||||
|
||||
foreach (var o in this)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
destroyTarget.Add(o);
|
||||
}
|
||||
else if (!IsObjectPrefab(o))
|
||||
{
|
||||
Undo.DestroyObjectImmediate(o.gameObject);
|
||||
destroyTarget.Add(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
destroyFailed.Append($"{o.name}, ");
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyFailed.Length > 0)
|
||||
{
|
||||
var objectsList = destroyFailed.ToString().TrimEnd(',', ' ');
|
||||
ShowDialog($"Failed to destroy following objects: {objectsList}\n" +
|
||||
"You might need to unpack prefabs before destroy them.");
|
||||
}
|
||||
|
||||
RemoveRange(destroyTarget);
|
||||
}
|
||||
|
||||
static bool IsObjectPrefab([NotNull] Object o) =>
|
||||
o && PrefabUtility.GetPrefabInstanceStatus(o) == PrefabInstanceStatus.Connected;
|
||||
|
||||
public void Initialize([CanBeNull] Transform parent, [CanBeNull] string keyword = null)
|
||||
{
|
||||
var objects = parent == null
|
||||
? GameObjectExtensions.GetAllGameObjectsInScene?.SelectMany(x => x == null ? null : x.GetComponents<T>())
|
||||
: parent.GetComponentsInChildren<T>(true);
|
||||
|
||||
if (keyword != null && !string.IsNullOrWhiteSpace(keyword))
|
||||
objects = objects?.Where(x => x != null && x.name.Contains(keyword));
|
||||
|
||||
Initialize(objects?.Where(x => x));
|
||||
}
|
||||
#endregion // UnityObject
|
||||
|
||||
#region Selection
|
||||
public override bool TryGetSelections(out Object[] selections)
|
||||
{
|
||||
selections = this.Select(x => x == null ? null : x.gameObject as Object).ToArray();
|
||||
return selections.Length != 0;
|
||||
}
|
||||
#endregion // Selection
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/ComponentList.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/ComponentList.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe3d0359e861479cb02ecd2c47cca64a
|
||||
timeCreated: 1647187947
|
8
Assets/RekornTools/Avatar/Core/Extensions.meta
generated
Normal file
8
Assets/RekornTools/Avatar/Core/Extensions.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e79b2b7c27306924aa13cd7dc57fbee0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
public static void MatchDictionaryKey<T, V>([NotNull] this Dictionary<T, V> target, [NotNull] Func<T, V> getValue) where T : Enum =>
|
||||
target.MatchDictionaryKey(GetKeys<T>(), getValue);
|
||||
|
||||
public static void MatchDictionaryKey<T, V>([NotNull] this Dictionary<T, V> target, [NotNull] IReadOnlyCollection<T> types, [NotNull] Func<T, V> getValue)
|
||||
{
|
||||
var keys = target.Keys.ToList();
|
||||
var toAdd = types.Except(keys).ToList();
|
||||
var toRemove = keys.Except(types).ToList();
|
||||
|
||||
foreach (var key in toAdd) target.Add(key, getValue.Invoke(key));
|
||||
foreach (var key in toRemove) target.Remove(key);
|
||||
}
|
||||
|
||||
[NotNull] public static IReadOnlyCollection<T> GetKeys<T>() where T : Enum =>
|
||||
Enum.GetValues(typeof(T)).Cast<T>().ToList();
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Extensions/DictionaryExtensions.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Extensions/DictionaryExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b07bd8be77b54b4cbe90d413eab28d03
|
||||
timeCreated: 1651155774
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class EditorExtensions
|
||||
{
|
||||
public static void ShowConfirmDialog<T>([NotNull] this T script, [NotNull] string message) where T : MonoBehaviour
|
||||
{
|
||||
var header = $"[{typeof(T)}({script.gameObject.name})]";
|
||||
Debug.LogWarning($"{header} {message}");
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.DisplayDialog(header, message, "Confirm");
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
|
||||
public static void UndoableAction([NotNull] this Object target, [NotNull] Action action) =>
|
||||
UndoableAction(target, target.name, action);
|
||||
|
||||
public static void UndoableAction([NotNull] this Object target, [NotNull] string actionName, [NotNull] Action action)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Undo.RegisterCompleteObjectUndo(target, actionName);
|
||||
action();
|
||||
Undo.FlushUndoRecordObjects();
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Extensions/EditorExtensions.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Extensions/EditorExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c24484bfde984bc184d95ce33634e57b
|
||||
timeCreated: 1649607325
|
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
public static void BackupGameObject([NotNull] this Object obj)
|
||||
{
|
||||
var backup = Object.Instantiate(obj);
|
||||
Undo.RegisterCreatedObjectUndo(backup, "Backup Cloth");
|
||||
}
|
||||
|
||||
public static void UnpackPrefab([CanBeNull] this GameObject prefab)
|
||||
{
|
||||
if (PrefabUtility.GetPrefabInstanceStatus(prefab) != PrefabInstanceStatus.Connected) return;
|
||||
var target = PrefabUtility.GetOutermostPrefabInstanceRoot(prefab);
|
||||
PrefabUtility.UnpackPrefabInstance(target, PrefabUnpackMode.OutermostRoot, InteractionMode.UserAction);
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
public static IEnumerable<GameObject> AllGameObjectsInProject =>
|
||||
Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[];
|
||||
|
||||
[CanBeNull]
|
||||
public static IEnumerable<GameObject> GetAllGameObjectsInScene =>
|
||||
AllGameObjectsInProject?.Where(IsEditableSceneObject);
|
||||
|
||||
static bool IsEditableSceneObject([CanBeNull] GameObject go)
|
||||
{
|
||||
if (go == null) return false;
|
||||
|
||||
var root = go.transform.root;
|
||||
if (root == null) root = go.transform;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var isStoredOnDisk = EditorUtility.IsPersistent(root);
|
||||
#else
|
||||
var isStoredOnDisk = false;
|
||||
#endif
|
||||
return !isStoredOnDisk && !(root.hideFlags == HideFlags.NotEditable || root.hideFlags == HideFlags.HideAndDontSave);
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Extensions/GameObjectExtensions.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Extensions/GameObjectExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c43c7164e54445c8b4ac98ae09d0b19
|
||||
timeCreated: 1647276663
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
#region Attribute
|
||||
public static readonly BindingFlags Everything = ~BindingFlags.Default;
|
||||
|
||||
public static (MemberInfo, Type) GetFieldOrProperty([CanBeNull] this Type type, [CanBeNull] string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return (null, type);
|
||||
|
||||
var fieldInfo = type?.GetField(name, Everything);
|
||||
if (fieldInfo != null) return (fieldInfo, fieldInfo.FieldType);
|
||||
|
||||
var propertyInfo = type?.GetProperty(name, Everything);
|
||||
if (propertyInfo != null) return (propertyInfo, propertyInfo.PropertyType);
|
||||
|
||||
return (null, type);
|
||||
}
|
||||
#endregion // Attribute
|
||||
|
||||
#region Property
|
||||
[NotNull] static readonly StringBuilder Sb = new StringBuilder();
|
||||
|
||||
[NotNull] const string AutoPropertyHeader = "<";
|
||||
[NotNull] const string AutoPropertyFooter = ">k__BackingField";
|
||||
|
||||
public static bool IsAutoProperty([NotNull] string name) =>
|
||||
name.StartsWith(AutoPropertyHeader, StringComparison.Ordinal)
|
||||
&& name.EndsWith(AutoPropertyFooter, StringComparison.Ordinal);
|
||||
|
||||
[NotNull]
|
||||
public static string ResolveDisplayName([NotNull] string name)
|
||||
{
|
||||
if (!IsAutoProperty(name)) return name;
|
||||
|
||||
name = name.Remove(0, AutoPropertyHeader.Length);
|
||||
name = name.Remove(name.Length - AutoPropertyFooter.Length, AutoPropertyFooter.Length);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
public static string ResolveFieldName([NotNull] string name)
|
||||
{
|
||||
if (IsAutoProperty(name)) return name;
|
||||
|
||||
Sb.Clear();
|
||||
Sb.Append(AutoPropertyHeader);
|
||||
Sb.Append(name);
|
||||
Sb.Append(AutoPropertyFooter);
|
||||
return Sb.ToString();
|
||||
}
|
||||
#endregion // Property
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Extensions/ReflectionExtensions.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Extensions/ReflectionExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b90ac13fa7054288ac26ae16cfe31ca9
|
||||
timeCreated: 1651847493
|
|
@ -0,0 +1,45 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class ShaderPropertyExtensions
|
||||
{
|
||||
[CanBeNull]
|
||||
public static IEnumerable<Shader> AllUserShadersInProject =>
|
||||
from material in AllMaterialsInProject
|
||||
let shader = material.shader
|
||||
where shader != null
|
||||
select shader;
|
||||
|
||||
[CanBeNull]
|
||||
public static IEnumerable<Material> AllMaterialsInProject =>
|
||||
#if UNITY_EDITOR
|
||||
from guid in AssetDatabase.FindAssets("t:Material")
|
||||
let path = AssetDatabase.GUIDToAssetPath(guid)
|
||||
let material = AssetDatabase.LoadAssetAtPath<Material>(path)
|
||||
where material != null
|
||||
select material;
|
||||
#else
|
||||
null;
|
||||
#endif
|
||||
|
||||
[CanBeNull]
|
||||
public static IEnumerable<Shader> AllShadersInProject =>
|
||||
#if UNITY_EDITOR
|
||||
from guid in AssetDatabase.FindAssets("t:Shader")
|
||||
let path = AssetDatabase.GUIDToAssetPath(guid)
|
||||
let shader = AssetDatabase.LoadAssetAtPath<Shader>(path)
|
||||
where shader != null
|
||||
select shader;
|
||||
#else
|
||||
null;
|
||||
#endif
|
||||
}
|
||||
}
|
11
Assets/RekornTools/Avatar/Core/Extensions/ShaderPropertyExtensions.cs.meta
generated
Normal file
11
Assets/RekornTools/Avatar/Core/Extensions/ShaderPropertyExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0007637d95157434a824ad5122f62b1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static void InvokeRecursive([NotNull] this Transform parent, [NotNull] Action<Transform> action)
|
||||
{
|
||||
action.Invoke(parent);
|
||||
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child == null) continue;
|
||||
child.InvokeRecursive(action);
|
||||
}
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
public static Transform FindRecursive([NotNull] this Transform parent, [NotNull] string name)
|
||||
{
|
||||
if (parent.name == name) return parent;
|
||||
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child == null) continue;
|
||||
if (child.name == name) return child;
|
||||
var result = child.FindRecursive(name);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Bounds TransformBounds([NotNull] this Transform transform, Bounds localBounds)
|
||||
{
|
||||
var center = transform.TransformPoint(localBounds.center);
|
||||
|
||||
var extents = localBounds.extents;
|
||||
var axisX = transform.TransformVector(extents.x, 0, 0);
|
||||
var axisY = transform.TransformVector(0, extents.y, 0);
|
||||
var axisZ = transform.TransformVector(0, 0, extents.z);
|
||||
|
||||
extents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x);
|
||||
extents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y);
|
||||
extents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z);
|
||||
|
||||
return new Bounds { center = center, extents = extents };
|
||||
}
|
||||
|
||||
public static Bounds InverseTransformBounds([NotNull] this Transform transform, Bounds localBounds)
|
||||
{
|
||||
var center = transform.InverseTransformPoint(localBounds.center);
|
||||
|
||||
var extents = localBounds.extents;
|
||||
var axisX = transform.InverseTransformVector(extents.x, 0, 0);
|
||||
var axisY = transform.InverseTransformVector(0, extents.y, 0);
|
||||
var axisZ = transform.InverseTransformVector(0, 0, extents.z);
|
||||
|
||||
extents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x);
|
||||
extents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y);
|
||||
extents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z);
|
||||
|
||||
return new Bounds { center = center, extents = extents };
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/Extensions/TransformExtensions.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/Extensions/TransformExtensions.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 926d769f1dfc4f66b1e105bfa2d67db2
|
||||
timeCreated: 1648135240
|
7
Assets/RekornTools/Avatar/Core/IValidate.cs
Normal file
7
Assets/RekornTools/Avatar/Core/IValidate.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace RekornTools.Avatar
|
||||
{
|
||||
public interface IValidate
|
||||
{
|
||||
void OnValidate();
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/IValidate.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/IValidate.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0811a75b3d044aebae9d2b3439ccd8a5
|
||||
timeCreated: 1651128798
|
22
Assets/RekornTools/Avatar/Core/ObjectList.cs
Normal file
22
Assets/RekornTools/Avatar/Core/ObjectList.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
[System.Serializable]
|
||||
public class ObjectList<T> : SerializedList<T> where T : Object
|
||||
{
|
||||
public void SelectComponents()
|
||||
{
|
||||
if (TryGetSelections(out var selections)) Selection.objects = selections;
|
||||
}
|
||||
|
||||
public virtual bool TryGetSelections([NotNull] out Object[] selections)
|
||||
{
|
||||
selections = this.Select(x => x == null ? null : x as Object).ToArray();
|
||||
return selections.Length != 0;
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/ObjectList.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/ObjectList.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 40d9a2e62dd3405f85ee17be3e922a54
|
||||
timeCreated: 1652002210
|
44
Assets/RekornTools/Avatar/Core/SerializedDictionary.cs
Normal file
44
Assets/RekornTools/Avatar/Core/SerializedDictionary.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public sealed class SerializedDictionary : SerializedDictionary<SerializedKeyValue, object, object>
|
||||
{
|
||||
[NotNull] public const string FieldName = nameof(Items);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SerializedDictionary<TKeyValue, TKey, TValue> :
|
||||
Dictionary<TKey, TValue>, ISerializationCallbackReceiver
|
||||
where TKeyValue : SerializedKeyValue<TKey, TValue>, new()
|
||||
{
|
||||
[SerializeField] [NotNull] protected List<TKeyValue> Items = new List<TKeyValue>();
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
Items.Clear();
|
||||
foreach (var item in this)
|
||||
{
|
||||
var pair = new TKeyValue
|
||||
{
|
||||
Key = item.Key,
|
||||
Value = item.Value,
|
||||
};
|
||||
|
||||
Items.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Clear();
|
||||
foreach (var item in Items)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/SerializedDictionary.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/SerializedDictionary.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 34bd198ed40c442bb43c305a864d753e
|
||||
timeCreated: 1651120815
|
24
Assets/RekornTools/Avatar/Core/SerializedKeyValue.cs
Normal file
24
Assets/RekornTools/Avatar/Core/SerializedKeyValue.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace RekornTools.Avatar
|
||||
{
|
||||
public sealed class SerializedKeyValue : SerializedKeyValue<object, object> { }
|
||||
|
||||
[Serializable] public class HorizontalKeyValue<K, V> : SerializedKeyValue<K, V> { }
|
||||
|
||||
[Serializable] public class SerializedKeyValue<K, V>
|
||||
{
|
||||
[field: SerializeField] [CanBeNull] public K Key { get; set; }
|
||||
[field: SerializeField] [CanBeNull] public V Value { get; set; }
|
||||
|
||||
protected SerializedKeyValue() { }
|
||||
|
||||
protected SerializedKeyValue([CanBeNull] K key, [CanBeNull] V value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/RekornTools/Avatar/Core/SerializedKeyValue.cs.meta
generated
Normal file
3
Assets/RekornTools/Avatar/Core/SerializedKeyValue.cs.meta
generated
Normal file
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bfc26b3e497a4d4a85759ca208c7f097
|
||||
timeCreated: 1647761218
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue