using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace Luneraria.EditorTools { /// /// Import rules for every FBX of the Blender pipeline under Assets/Luneraria/Art (U2A). /// Metres stay metres, Blender's Z-up axes are converted by baking (no root rotation), /// normals and tangents come from the file, rigs are generic with the Blender bone names, /// and clips get their Blender action names, loop flags and contact events. /// public sealed class LuneModelPostprocessor : AssetPostprocessor { public const string ArtRoot = "Assets/Luneraria/Art/"; /// Raised on the figure at the hit frame of attack and ability clips. public const string ContactEvent = "OnLuneContact"; public override uint GetVersion() { return 3; } void OnPreprocessModel() { if (!assetPath.StartsWith(ArtRoot) || !assetPath.EndsWith(".fbx")) return; ExportReport report = ExportReport.ForModel(assetPath); var importer = (ModelImporter)assetImporter; importer.globalScale = 1f; importer.useFileScale = true; importer.bakeAxisConversion = true; importer.importCameras = false; importer.importLights = false; importer.importVisibility = false; importer.importBlendShapes = false; importer.importNormals = ModelImporterNormals.Import; importer.importTangents = ModelImporterTangents.Import; importer.meshCompression = ModelImporterMeshCompression.Off; importer.isReadable = true; importer.materialImportMode = ModelImporterMaterialImportMode.ImportViaMaterialDescription; importer.materialLocation = ModelImporterMaterialLocation.InPrefab; bool skinned = report != null && report.Skinned; importer.animationType = skinned ? ModelImporterAnimationType.Generic : ModelImporterAnimationType.None; importer.avatarSetup = skinned ? ModelImporterAvatarSetup.CreateFromThisModel : ModelImporterAvatarSetup.NoAvatar; importer.importAnimation = skinned; importer.optimizeGameObjects = false; importer.animationCompression = ModelImporterAnimationCompression.Off; importer.resampleCurves = false; } void OnPreprocessAnimation() { if (!assetPath.StartsWith(ArtRoot) || !assetPath.EndsWith(".fbx")) return; ExportReport report = ExportReport.ForModel(assetPath); if (report == null || !report.Skinned) return; var importer = (ModelImporter)assetImporter; var blender = report.Clips.ToDictionary(c => (string)c["name"], c => c); var clips = new List(); foreach (ModelImporterClipAnimation clip in importer.defaultClipAnimations) { // Blender names takes "|"; Unity keeps the action name. string name = clip.takeName.Contains("|") ? clip.takeName.Substring(clip.takeName.LastIndexOf('|') + 1) : clip.takeName; clip.name = name; if (blender.TryGetValue(name, out var facts)) { clip.loopTime = (bool)facts["loop"]; double contact = (double)facts["contact_seconds"]; double seconds = (double)facts["seconds"]; if (contact > 0.0 && seconds > 0.0) clip.events = new[] { new AnimationEvent { functionName = ContactEvent, time = (float)(contact / seconds) } }; } clips.Add(clip); } importer.clipAnimations = clips.ToArray(); } } }