← Blog

Godot does not store your normals exactly, and your tests should know it

You export a mesh to Godot, you compare the normals with the source, and everything fails. The asset is fine. Your comparison is not.

What we measured

A 2 m cube with sharp edges and two materials, exported from Blender 5.2 as glTF and imported by the real Godot 4.7 import pipeline. Reading the mesh back gives 2 surfaces, 12 render vertices each, 24 in total, exactly what Blender predicted. Normals, tangents and UVs are all present and the materials keep their names.

And a normal that should be exactly (-1, 0, 0) reads as (-1.0, 0.00007, -0.00006).

Why it happens

Engines store vertex attributes in compact formats. A normal does not need 32 bits per axis to look right on screen, so it gets packed, and packing is lossy. The deviation above is around four thousandths of a degree. No human will ever see it. An exact comparison will see nothing else.

The consequence for testing

Any check of the form "the imported normal equals the source normal" fails on every asset in Godot. The useful check is angular and states its threshold: the angle between the source normal and the imported normal stays under X degrees, on at least Y percent of the surface.

That formulation has a property the equality does not: it keeps working when the engine changes its compression, and it still catches a real problem such as normals being recomputed, which moves normals by tens of degrees, not by thousandths.

How FixMyMesh handles it

Tolerances are per check and per engine, and they exist because the engines were measured, not because a number felt safe. A result that sits within engine noise is a PASS and says nothing more. A result outside it is reported with the measured deviation and the share of the surface affected.

This is also why the report distinguishes WARNING from ERROR: something can change measurably and still be harmless, and you are the one who decides, with the number in front of you.

What to do about it

Nothing, in the Godot case: the compression is expected and your asset is fine. What matters is that your delivery test knows the difference between engine noise and a real change. If your current process cannot tell them apart, it is either failing assets that are correct, or passing assets that are broken.

Test it on your own asset

FixMyMesh runs the same measurement on your files, in the engine installed on your machine. 3 full tests, free.