Last active
November 25, 2025 11:53
-
-
Save thebeardphantom/04abb46fd449087a97a24a5d940a29a7 to your computer and use it in GitHub Desktop.
An AssetPostprocessor that fixes the import rotations for .blend files, and FBX files exported by blender using default settings. If using this processor use the DEFAULT export settings in Blender!
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 UnityEditor; | |
| using UnityEngine; | |
| using UnityEngine.Pool; | |
| public class BlenderImportFixesProcessor : AssetPostprocessor | |
| { | |
| private const float Epsilon = 3E-07f; | |
| private const float FBXInverseScale = 1f / 100f; | |
| private static readonly Quaternion s_tformRotationDefault = new(0.7071068f, 0f, 0f, 0.7071067f); | |
| private Quaternion _tformRotation; | |
| private bool _isFBX; | |
| private static void CalculateWorldSpaceState( | |
| List<MeshFilter> meshFilters, | |
| List<Vector3> meshFilterWorldSpacePositions, | |
| List<Vector3[]> meshToWorldSpaceVerts) | |
| { | |
| foreach (MeshFilter meshFilter in meshFilters) | |
| { | |
| Vector3[] worldSpaceVerts = meshFilter.sharedMesh.vertices; | |
| // Store transform world space position to recalculate new local space position later | |
| Transform meshFilterTransform = meshFilter.transform; | |
| meshFilterWorldSpacePositions.Add(meshFilterTransform.position); | |
| // Store mesh verts world space positions to recalculate model space verts later | |
| meshFilterTransform.TransformPoints(worldSpaceVerts); | |
| meshToWorldSpaceVerts.Add(worldSpaceVerts); | |
| } | |
| } | |
| private static void RestoreLocalSpaceState( | |
| List<MeshFilter> meshFilters, | |
| List<Vector3> meshFilterToWorldSpacePosition, | |
| List<Vector3[]> meshToWorldSpaceVerts) | |
| { | |
| for (var i = 0; i < meshFilters.Count; i++) | |
| { | |
| MeshFilter meshFilter = meshFilters[i]; | |
| Transform meshFilterTransform = meshFilter.transform; | |
| // Restore transform world position, cleaning up transformed positions with epsilon check | |
| Vector3 newLocalPosition = meshFilterTransform.parent.InverseTransformPoint(meshFilterToWorldSpacePosition[i]); | |
| for (var j = 0; j < 3; j++) | |
| { | |
| if (Mathf.Abs(newLocalPosition[j]) <= Epsilon) | |
| { | |
| newLocalPosition[j] = 0f; | |
| } | |
| } | |
| meshFilterTransform.localPosition = newLocalPosition; | |
| // Calculate and apply new local vert positions based on new transform values | |
| Vector3[] worldSpaceVerts = meshToWorldSpaceVerts[i]; | |
| Mesh mesh = meshFilter.sharedMesh; | |
| meshFilterTransform.InverseTransformPoints(worldSpaceVerts); | |
| mesh.SetVertices(worldSpaceVerts); | |
| mesh.RecalculateNormals(); | |
| mesh.RecalculateTangents(); | |
| mesh.RecalculateBounds(); | |
| mesh.UploadMeshData(false); | |
| } | |
| } | |
| public override int GetPostprocessOrder() | |
| { | |
| return int.MinValue; | |
| } | |
| private void OnPreprocessModel() | |
| { | |
| var modelImporter = (ModelImporter)assetImporter; | |
| string fileExtension = Path.GetExtension(modelImporter.assetPath); | |
| _isFBX = string.Equals(".fbx", fileExtension, StringComparison.OrdinalIgnoreCase); | |
| _tformRotation = modelImporter.bakeAxisConversion | |
| ? Quaternion.Inverse(s_tformRotationDefault) | |
| : s_tformRotationDefault; | |
| } | |
| private void OnPostprocessModel(GameObject root) | |
| { | |
| using PooledObject<List<MeshFilter>> _ = ListPool<MeshFilter>.Get(out List<MeshFilter> meshFilters); | |
| using PooledObject<List<Vector3>> __ = ListPool<Vector3>.Get(out List<Vector3> meshFilterWorldSpacePositions); | |
| using PooledObject<List<Vector3[]>> ___ = ListPool<Vector3[]>.Get(out List<Vector3[]> meshFilterWorldSpaceVerts); | |
| root.GetComponentsInChildren(true, meshFilters); | |
| CalculateWorldSpaceState(meshFilters, meshFilterWorldSpacePositions, meshFilterWorldSpaceVerts); | |
| ApplyFirstLevelChildTransformChanges(root); | |
| RestoreLocalSpaceState(meshFilters, meshFilterWorldSpacePositions, meshFilterWorldSpaceVerts); | |
| } | |
| private void ApplyFirstLevelChildTransformChanges(GameObject root) | |
| { | |
| Transform rootTransform = root.transform; | |
| int childCount = rootTransform.childCount; | |
| for (var i = 0; i < childCount; i++) | |
| { | |
| Transform firstLevelChild = rootTransform.GetChild(i); | |
| firstLevelChild.localRotation *= _tformRotation; | |
| if (_isFBX) | |
| { | |
| firstLevelChild.localScale *= FBXInverseScale; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment