Skip to content

Instantly share code, notes, and snippets.

@Guendeli
Forked from strich/AnimatedUnitCompressor.cs
Created November 27, 2018 12:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Guendeli/42967e6fb7acc2e77d47b0d0132306ad to your computer and use it in GitHub Desktop.
Save Guendeli/42967e6fb7acc2e77d47b0d0132306ad to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
public class AnimatedUnitCompressor : EditorWindow {
Vector2 scrollPos = Vector2.zero;
public List<GameObject> UnitPrefabs;
public static string BonePrefix;
private SerializedObject _serializedObject;
private int _prefabsToTouch;
private int _remainingPrefabsToTouch;
[MenuItem("Bulk Asset Modifier/Animated Unit Compressor")]
public static void ShowWindow() {
GetWindow(typeof(AnimatedUnitCompressor));
}
public void OnEnable() {
_serializedObject = new SerializedObject(this);
}
public void OnGUI()
{
_serializedObject.Update();
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
GUILayout.Label("Base Settings", EditorStyles.boldLabel);
GUILayout.Label("Specify the string prefix for all the bones. ", EditorStyles.helpBox);
//EditorGUILayout.PropertyField(_serializedObject.FindProperty("BonePrefix"), true);
BonePrefix = EditorGUILayout.TextField("rigged_");
GUILayout.Label("Drag and drop all the prefabs you wish to have compressed. " +
"This will cause all unnecessary bone GameObjects to be lost.", EditorStyles.helpBox);
EditorGUILayout.PropertyField(_serializedObject.FindProperty("UnitPrefabs"), true);
if (GUILayout.Button("Start"))
{
_remainingPrefabsToTouch = 0;
_prefabsToTouch = UnitPrefabs.Count;
CompressModelBones();
}
EditorGUILayout.EndScrollView();
_serializedObject.ApplyModifiedProperties();
}
void CompressModelBones () {
foreach (var unitPrefab in UnitPrefabs)
{
_remainingPrefabsToTouch++;
if (unitPrefab == null) continue;
UpdateProgressBar(unitPrefab.name);
var prefabInstance = Instantiate(unitPrefab, new Vector3(), new Quaternion()) as GameObject;
if (prefabInstance == null) continue;
var exposedBones = new List<string>();
FindBonesRequiredToExpose(exposedBones, prefabInstance.transform);
OptimizeAnimatorHierarchyPrefab(prefabInstance, exposedBones);
OptimizeAnimatorHierarchyMesh(prefabInstance, exposedBones);
//UpdatePrefabWithNewModel(prefabInstance);
// Save the prefab to disk and remove from scene:
PrefabUtility.ReplacePrefab(prefabInstance, unitPrefab, ReplacePrefabOptions.ReplaceNameBased);
DestroyImmediate(prefabInstance);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
void UpdateProgressBar(string updateText) {
if (_remainingPrefabsToTouch < _prefabsToTouch)
{
EditorUtility.DisplayProgressBar("Compressing Model Bones", updateText, (float)_remainingPrefabsToTouch / _prefabsToTouch);
}
else
{
EditorUtility.ClearProgressBar();
}
}
static void OptimizeAnimatorHierarchyPrefab(GameObject prefabInstance, List<string> exposedBones)
{
var animatorGO = prefabInstance.GetComponentInChildren<Animator>().gameObject;
List<string> customBones = new List<string>();
customBones.AddRange(exposedBones);
var gameObjectParentPairDict = new Dictionary<GameObject, string>();
FindEmptyGameObjects(prefabInstance, gameObjectParentPairDict);
foreach (var customObject in gameObjectParentPairDict)
{
customBones.Add(customObject.Value);
customObject.Key.transform.parent = null;
}
// Optimise the bones:
AnimatorUtility.OptimizeTransformHierarchy(
animatorGO, customBones.ToArray());
foreach (var customObject in gameObjectParentPairDict)
{
customObject.Key.transform.parent = prefabInstance.transform.FindInChildren(customObject.Value);
}
}
static void FindEmptyGameObjects(GameObject gameObject, Dictionary<GameObject, string> gameObjectParentPairDict) {
List<string> objectToCache = new List<string>() {
"boneShield", "cameraBone", "VFXpoint_top",
"VFXpoint_mid", "VFXpoint_bottom", "VFXpoint_ground"
};
foreach (var obj in objectToCache)
{
var objGO = gameObject.transform.FindInChildren(obj);
if(objGO != null)
gameObjectParentPairDict.Add(objGO.gameObject, objGO.parent.name);
}
}
static void OptimizeAnimatorHierarchyMesh(GameObject prefabInstance, List<string> exposedBones) {
var mesh = GetSourceMesh(prefabInstance);
var meshImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(mesh)) as ModelImporter;
if (meshImporter == null) return;
meshImporter.optimizeGameObjects = true;
meshImporter.extraExposedTransformPaths = exposedBones.ToArray();
AssetDatabase.WriteImportSettingsIfDirty(AssetDatabase.GetAssetPath(mesh));
AssetDatabase.Refresh();
}
static Mesh GetSourceMesh(GameObject prefab) {
var meshRenderer = prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true).First();
if (meshRenderer == null) return null;
return meshRenderer.sharedMesh;
}
static void FindBonesRequiredToExpose(List<string> exposedBones, Transform transform)
{
// TODO: Do a sanity check for any components attached to rig GOs
var isRigging = transform.name.StartsWith(BonePrefix);
var isChildOfRigging =
transform.parent != null && transform.parent.name.StartsWith(BonePrefix);
if (!isRigging && isChildOfRigging)
exposedBones.Add(transform.parent.name);
for (int x = 0; x < transform.childCount; x++)
{
if (isRigging || transform.parent == null)
FindBonesRequiredToExpose(exposedBones, transform.GetChild(x));
}
}
public void OnInspectorUpdate()
{
Repaint();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment