Skip to content

Instantly share code, notes, and snippets.

@thebeardphantom
Last active November 25, 2025 11:53
Show Gist options
  • Select an option

  • Save thebeardphantom/04abb46fd449087a97a24a5d940a29a7 to your computer and use it in GitHub Desktop.

Select an option

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!
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