Created
May 5, 2020 12:29
-
-
Save geowarin/2542fdabe48674a8518bc0d0da0b4c1e to your computer and use it in GitHub Desktop.
Editor Window UIElements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using UnityEditor; | |
using UnityEditor.Animations; | |
using UnityEditor.ShortcutManagement; | |
using UnityEditor.UIElements; | |
using UnityEngine; | |
using UnityEngine.Animations; | |
using UnityEngine.Playables; | |
using UnityEngine.UIElements; | |
using Object = UnityEngine.Object; | |
// Assets/MixamoImport/Editor/MixamoImportTool.cs | |
namespace MixamoImport.Editor | |
{ | |
public class MixamoImportTool : EditorWindow | |
{ | |
private VisualElement _root; | |
private static MixamoImportTool _window; | |
private Avatar _selectedAvatar; | |
public void OnEnable() | |
{ | |
_root = rootVisualElement; | |
var visualTree = Resources.Load<VisualTreeAsset>("MixamoImportTool"); | |
visualTree.CloneTree(_root); | |
_root.styleSheets.Add(Resources.Load<StyleSheet>("MixamoImportTool")); | |
_root.Q<Button>("modifyButton").clicked += ProcessSelectedAssets; | |
_root.Q<Button>("testButton").clicked += AddAnimationToController; | |
_root.Q<ObjectField>("avatar").RegisterValueChangedCallback(SelectAvatar); | |
} | |
private void AddAnimationToController() | |
{ | |
var selectedAssetsInfo = GetSelectedAssetsInfo(); | |
var directoryName = Path.GetDirectoryName(selectedAssetsInfo.First().assetPath) ?? | |
throw new InvalidOperationException(); | |
var controllersDirectoryPath = Path.Combine(directoryName, "Controllers"); | |
Directory.CreateDirectory(controllersDirectoryPath); | |
var mixamoControllerPath = controllersDirectoryPath + "/Mixamo.controller"; | |
var controller = | |
AssetDatabase.LoadAssetAtPath<AnimatorController>(mixamoControllerPath) ?? | |
AnimatorController.CreateAnimatorControllerAtPath(mixamoControllerPath); | |
var rootStateMachine = controller.layers[0].stateMachine; | |
var stateNames = rootStateMachine.states.Select(state => state.state.name).ToList(); | |
foreach (var assetInfo in selectedAssetsInfo) | |
{ | |
var animationClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(assetInfo.assetPath); | |
if (stateNames.Contains(animationClip.name)) | |
continue; | |
var animatorState = controller.AddMotion(animationClip); | |
} | |
} | |
private void SelectAvatar(ChangeEvent<Object> evt) => _selectedAvatar = (Avatar) evt.newValue; | |
private void OnDisable() | |
{ | |
_root.Q<Button>("modifyButton").clicked -= ProcessSelectedAssets; | |
_root.Q<Button>("testButton").clicked -= AddAnimationToController; | |
_root.Q<ObjectField>("avatar").UnregisterValueChangedCallback(SelectAvatar); | |
} | |
private void OnGUI() | |
{ | |
var selectedAssetsInfo = GetSelectedAssetsInfo(); | |
_root.Q<Label>("numObjects").text = $"{selectedAssetsInfo.Count} objects selected"; | |
_root.Q<Label>("avatarName").text = _selectedAvatar != null | |
? $"{_selectedAvatar.name} selected" | |
: "One avatar will be created for each animation"; | |
} | |
private void ProcessSelectedAssets() | |
{ | |
var selectedAssets = GetSelectedAssetsInfo(); | |
var count = selectedAssets.Count; | |
for (var i = 0; i < count; i++) | |
{ | |
var assetInfo = selectedAssets[i]; | |
var assetName = assetInfo.gameObject.name; | |
EditorUtility.DisplayProgressBar($"Processing {count} files", assetName, (1f / count) * i); | |
ModifyModelImporter(assetInfo); | |
} | |
EditorUtility.ClearProgressBar(); | |
} | |
private void ModifyModelImporter(AssetInfo assetInfo) | |
{ | |
var assetName = assetInfo.gameObject.name; | |
var modelImporter = assetInfo.modelImporter; | |
ModelImporterClipAnimation[] clipAnimations = modelImporter.defaultClipAnimations; | |
// Rig => Humanoid | |
modelImporter.animationType = ModelImporterAnimationType.Human; | |
// Change avatar | |
if (_selectedAvatar != null) | |
{ | |
modelImporter.sourceAvatar = _selectedAvatar; | |
} | |
else | |
{ | |
modelImporter.avatarSetup = ModelImporterAvatarSetup.CreateFromThisModel; | |
} | |
var parentDirectory = Path.GetDirectoryName(assetInfo.assetPath) ?? throw new InvalidOperationException(); | |
if (assetInfo.isModel) | |
{ | |
var textureDirectoryPath = Path.Combine(parentDirectory, "Textures"); | |
Directory.CreateDirectory(textureDirectoryPath); | |
modelImporter.ExtractTextures(textureDirectoryPath); | |
} | |
assetName = assetName.Replace(' ', '_').ToLower(); | |
// change names | |
foreach (var clip in clipAnimations) | |
{ | |
clip.name = assetName; | |
} | |
modelImporter.clipAnimations = clipAnimations; | |
modelImporter.SaveAndReimport(); | |
var animationClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(assetInfo.assetPath); | |
ExtractAnimationClip(animationClip, parentDirectory + "/" + assetName + ".anim"); | |
} | |
private static void ExtractAnimationClip(AnimationClip orgClip, string destinationPath) | |
{ | |
AnimationClip placeClip = new AnimationClip(); | |
if (!Resources.Load(destinationPath)) | |
{ | |
EditorUtility.CopySerialized(orgClip, placeClip); | |
AssetDatabase.CreateAsset(placeClip, destinationPath); | |
AssetDatabase.Refresh(); | |
} | |
} | |
struct AssetInfo | |
{ | |
public string assetPath; | |
public bool isModel; | |
public GameObject gameObject; | |
public ModelImporter modelImporter; | |
} | |
private static List<AssetInfo> GetSelectedAssetsInfo() | |
{ | |
var selectedUniqueAssetPaths = Selection.gameObjects | |
.GroupBy(AssetDatabase.GetAssetPath) | |
.Select(g => g.Key) | |
.ToList(); | |
var assets = selectedUniqueAssetPaths | |
.Select(assetPath => new AssetInfo | |
{ | |
assetPath = assetPath, | |
isModel = AssetDatabase.LoadAssetAtPath<Mesh>(assetPath) != null, | |
gameObject = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath), | |
modelImporter = (ModelImporter) AssetImporter.GetAtPath(assetPath) | |
}) | |
// Models first | |
.OrderByDescending(a => a.isModel) | |
.ToList(); | |
return assets; | |
} | |
#region Show Window | |
// [MenuItem("My Tools/Mixamo import")] | |
// [Shortcut("Refresh Window", KeyCode.F9)] | |
// public static void ShowExample() | |
// { | |
// if (_window) _window.Close(); | |
// | |
// _window = GetWindow<MixamoImportTool>(); | |
// _window.minSize = new Vector2(350, 200); | |
// _window.titleContent = new GUIContent("Mixamo import"); | |
// } | |
#endregion | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Assets/MixamoImport/Resources/MixamoImportTool.uss | |
.row { | |
display: flex; | |
flex-direction: row; | |
justify-content: center; | |
} | |
.header { | |
font-size: 24px; | |
color: #11ff00; | |
} | |
Label { | |
font-size: 12px; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Assets/MixamoImport/Resources/MixamoImportTool.uxml --> | |
<UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements"> | |
<ui:VisualElement class ="row"> | |
<ui:Label text="Import Animations" class="header"/> | |
</ui:VisualElement> | |
<ui:Label name="numObjects" /> | |
<uie:ObjectField name="avatar" label="Avatar" type="UnityEngine.Avatar, UnityEngine" binding-path="avatar" allow-scene-objects="false"/> | |
<ui:Label name="avatarName" /> | |
<ui:Button text="Modify animations" name="modifyButton" /> | |
<ui:Button text="Test" name="testButton" /> | |
</UXML> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment