Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save FleshMobProductions/a5497f2e705a22b2ce4e4a0f0bcf612a to your computer and use it in GitHub Desktop.
Save FleshMobProductions/a5497f2e705a22b2ce4e4a0f0bcf612a to your computer and use it in GitHub Desktop.
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
namespace TF2018.UI
{
/// <summary>
/// Will copy the import data from a reference model to all animations in a specific folder
///- files have to be of type fbx
///- The importer will iterate over every animation that has the name "SmashAnimations" in the path, so it is recommended to have a separate folder for that.
///- The importer will search a reference model in the same folder as the currently imported animation asset.The reference model has to have "MainAvatar" in the name
///- The referenced avatar definition has to be in a model file with the name "ModelReference" in it. this model is excluded from the import processing.
///- In your "MainAvatar" animation/model, set the rig to type "Humanoid", check "Copy from other Avatar" and select as source the avatar from the "ModelReference" model.
///- all relevant properties of the reference model importer are copied to the importer of the animation
///- some animation clip settings from the first animation clip in the reference model are copied to all animation clips of the other animations
///
/// Problems:
/// Script execution throws errors in the Editor: Assertion failed: TLS Allocator ALLOC_TEMP_THREAD, underlying allocator ALLOC_TEMP_THREAD has unfreed allocations, size 454
/// changing the name of the importer probably has no effect, so in future versions I'd need another way to check if an animation was already processed
/// </summary>
public class UnityAnimationSettingCopyPostprocessor : AssetPostprocessor
{
private static readonly string requiredAssetPathContent = "smashanimations";
private static readonly string mainAvatarContent = "mainavatar";
private static readonly string excludedModelname = "modelreference";
private static readonly string doneAnimationSubfix = "_done";
void OnPreprocessModel()
{
//OnPreprocesModelOrAnimation();
}
/// <summary>
/// Apply settings to the asset importer here!
/// </summary>
void OnPreprocessAnimation()
{
OnPreprocesModelOrAnimation();
}
/// <summary>
/// Processes fbx files, checks if they are in a path whose lower case conversion contains "smashanimations"
/// and edits all ModelImporters for them, except for files with the name "modelreference" or "mainavatar" in their lower case paths
/// </summary>
private void OnPreprocesModelOrAnimation()
{
string animationPath = assetPath;
try
{
string animationPathLower = animationPath.ToLower();
Debug.Log("Process animation " + animationPath);
//don't include the reference model
if (animationPathLower.Contains(".fbx") && animationPathLower.Contains(requiredAssetPathContent) && !IsModelPathMainAvatar(animationPath) && !animationPathLower.Contains(excludedModelname))
{
ModelImporter importer = (ModelImporter)assetImporter;
if (importer.name.EndsWith(doneAnimationSubfix))
{
return;
}
string sourceModelPath = GetMainAvatarModelPathForFile(animationPath);
if (string.IsNullOrEmpty(sourceModelPath))
{
Debug.LogError(string.Format("SmashFbxAnimationImporter: no reference avatar fbx found for animation at path {0}", animationPath));
return;
}
ModelImporter referenceImporter = AssetImporter.GetAtPath(sourceModelPath) as ModelImporter;
if (referenceImporter == null)
{
Debug.LogError(string.Format("SmashFbxAnimationImporter: reference model importer for path {0} is null", animationPath));
return;
}
CopyModelImporterSettings(importer, referenceImporter);
//after everything is done, we can mark our importer in a way:
importer.name = importer.name + doneAnimationSubfix;
//importer.SaveAndReimport(); //this would cause an endless loop, importer is saved automatically
AssetDatabase.ImportAsset(importer.assetPath, ImportAssetOptions.ForceUpdate);
}
}
catch (Exception e)
{
Debug.LogError("SmashAnimationPostprocessor: something went wrong while processing animation " + animationPath + ". Error: " + e.ToString());
}
}
/// <summary>
/// Copies important settings from the referenceImporter to the importer
/// Animation clip settings are copied from the first clip in the referenceImporter and applied to all clips in the importer
/// </summary>
/// <param name="importer">ModelImporter attributes are copied to</param>
/// <param name="referenceImporter">ModelImporter attributes are copied from</param>
private void CopyModelImporterSettings(ModelImporter importer, ModelImporter referenceImporter)
{
//Model tab
importer.globalScale = referenceImporter.globalScale;
importer.useFileScale = referenceImporter.useFileScale;
importer.meshCompression = referenceImporter.meshCompression;
importer.isReadable = referenceImporter.isReadable;
importer.optimizeMesh = referenceImporter.optimizeMesh;
importer.importBlendShapes = referenceImporter.importBlendShapes;
importer.keepQuads = referenceImporter.keepQuads;
importer.indexFormat = referenceImporter.indexFormat;
importer.weldVertices = referenceImporter.weldVertices;
importer.importVisibility = referenceImporter.importVisibility;
importer.importCameras = referenceImporter.importCameras;
importer.importLights = referenceImporter.importLights;
importer.preserveHierarchy = referenceImporter.preserveHierarchy;
importer.swapUVChannels = referenceImporter.swapUVChannels;
importer.generateSecondaryUV = referenceImporter.generateSecondaryUV;
importer.importNormals = referenceImporter.importNormals;
importer.normalCalculationMode = referenceImporter.normalCalculationMode;
importer.normalSmoothingAngle = referenceImporter.normalSmoothingAngle;
importer.importTangents = referenceImporter.importTangents;
//rig tab
importer.animationType = referenceImporter.animationType;
importer.sourceAvatar = referenceImporter.sourceAvatar;
importer.optimizeGameObjects = referenceImporter.optimizeGameObjects;
//importer.SaveAndReimport(); //endless loop? Yes, endless loop
//materials tab
importer.importMaterials = referenceImporter.importMaterials;
importer.materialLocation = referenceImporter.materialLocation;
importer.materialName = referenceImporter.materialName;
importer.materialLocation = referenceImporter.materialLocation;
//animation tab
importer.importConstraints = referenceImporter.importConstraints;
importer.importAnimation = referenceImporter.importAnimation;
importer.animationCompression = referenceImporter.animationCompression;
importer.animationRotationError = referenceImporter.animationRotationError;
importer.animationPositionError = referenceImporter.animationPositionError;
importer.animationScaleError = referenceImporter.animationScaleError;
importer.importAnimatedCustomProperties = referenceImporter.importAnimatedCustomProperties;
var referenceClipAnimations = referenceImporter.clipAnimations;
Debug.Log(string.Format("referenceImporter.clipAnimations.Length {0}, is null: {1}", referenceClipAnimations != null ? referenceClipAnimations.Length : 0, referenceClipAnimations == null));
if (referenceClipAnimations != null && referenceClipAnimations.Length > 0)
{
var referenceClip = referenceClipAnimations[0];
//https://answers.unity.com/questions/724415/change-animationclip-settings-during-import.html
//changing the clips can be done by editing the default animations and setting it back to the clipAnimations
var defaultClipAnimations = importer.defaultClipAnimations;
foreach (var clipAnimation in defaultClipAnimations)
{
clipAnimation.hasAdditiveReferencePose = referenceClip.hasAdditiveReferencePose;
if (referenceClip.hasAdditiveReferencePose)
{
clipAnimation.additiveReferencePoseFrame = referenceClip.additiveReferencePoseFrame;
}
clipAnimation.maskType = referenceClip.maskType;
clipAnimation.maskSource = referenceClip.maskSource;
clipAnimation.keepOriginalOrientation = referenceClip.keepOriginalOrientation;
clipAnimation.keepOriginalPositionXZ = referenceClip.keepOriginalPositionXZ;
clipAnimation.keepOriginalPositionY = referenceClip.keepOriginalPositionY;
clipAnimation.lockRootRotation = referenceClip.lockRootRotation;
clipAnimation.lockRootPositionXZ = referenceClip.lockRootPositionXZ;
clipAnimation.lockRootHeightY = referenceClip.lockRootHeightY;
clipAnimation.mirror = referenceClip.mirror;
clipAnimation.wrapMode = referenceClip.wrapMode;
}
importer.clipAnimations = defaultClipAnimations;
}
}
private string GetMainAvatarModelPathForFile(string assetPath)
{
string directoryPath = Path.GetDirectoryName(assetPath); //Get the directory path, excluding the file
string[] filePaths = Directory.GetFiles(directoryPath, "*.fbx", SearchOption.AllDirectories);
foreach (string path in filePaths)
{
string fileName = Path.GetFileName(path);
if (IsModelPathMainAvatar(fileName))
{
//main avatar found.
return path;
}
}
return null;
}
private bool IsModelPathMainAvatar(string fileName)
{
return fileName.ToLower().Replace("_", "").Contains(mainAvatarContent);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment