Skip to content

Instantly share code, notes, and snippets.

@mandarinx
Last active January 12, 2024 23:08
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save mandarinx/ed733369fbb2eea6c7fa9e3da65a0e17 to your computer and use it in GitHub Desktop.
Save mandarinx/ed733369fbb2eea6c7fa9e3da65a0e17 to your computer and use it in GitHub Desktop.
Visualize mesh normals in Unity3D

Visualize the normals of a mesh

It's meant to be simple and easy to use, and therefore it has been made as a custom editor of MeshFilter. It needs no preparations.

How to use

Drop the file in an Editor folder.

Updates

  • store the normals length i editor prefs
  • adjust the normals length in the mesh filter inspector
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MeshFilter))]
public class NormalsVisualizer : Editor {
private const string EDITOR_PREF_KEY = "_normals_length";
private Mesh mesh;
private MeshFilter mf;
private Vector3[] verts;
private Vector3[] normals;
private float normalsLength = 1f;
private void OnEnable() {
mf = target as MeshFilter;
if (mf != null) {
mesh = mf.sharedMesh;
}
normalsLength = EditorPrefs.GetFloat(EDITOR_PREF_KEY);
}
private void OnSceneGUI() {
if (mesh == null) {
return;
}
Handles.matrix = mf.transform.localToWorldMatrix;
Handles.color = Color.yellow;
verts = mesh.vertices;
normals = mesh.normals;
int len = mesh.vertexCount;
for (int i = 0; i < len; i++) {
Handles.DrawLine(verts[i], verts[i] + normals[i] * normalsLength);
}
}
public override void OnInspectorGUI() {
base.OnInspectorGUI();
EditorGUI.BeginChangeCheck();
normalsLength = EditorGUILayout.FloatField("Normals length", normalsLength);
if (EditorGUI.EndChangeCheck()) {
EditorPrefs.SetFloat(EDITOR_PREF_KEY, normalsLength);
}
}
}
@JocelynSachs
Copy link

You've probably noticed this script creates massive slowdown on complex objects. This is for two reasons:

  1. You only need to set the Handles matrix and colour once.
  2. (much more serious): accessing mesh.vertices creates a copy of the entire vertex array. Same for accessing mesh.normals. Since you are accessing mesh.vertices twice and mesh.normals once for every vertex, you're forcing Unity to allocate and free up hundreds or thousands (or hundreds OF thousands) of vertex buffers every frame.

All you need to do is move the handles.matrix and colour lines outside the loop, and cache mesh.vertices and mesh.normals in local arrays, too:

Vector3 verts = mesh.vertices;
Vector3 norms = mesh.normals;

for (int.....)
{
Handles.DrawLine(verts[i],verts[i]+norms[i])
}

@mandarinx
Copy link
Author

Thanks for pointing out the performance issues! One of the things I work with is performance optimizations, so it was rather embarrassing seeing this script again. I fully agree with you, and will update the gist. Thanks!

@fakkoweb
Copy link

fakkoweb commented Feb 8, 2022

I just dropped the script in an Editor folder and I cannot see anything. Dropped a Cube in the Scene, select it or not, what should I see? In the inspector I see the "MeshFilter" with the Mesh field, and nothing else. I am on 2020.3

@mandarinx
Copy link
Author

mandarinx commented Feb 8, 2022

Hi @fakkoweb. I just tried the script in 2021.2.10 and it worked like expected. I tested with the default cube. I'm not quite sure why it doesn't work on your end. You should see yellow lines drawn along the normals of the mesh, in the scene view.

@Foxician
Copy link

@mandarinx Thanks for this handy tool!
@fakkoweb I just used the script in 2019.4.14f1 and it workes for me. In the Mesh Filter component there's a new attribute called "Normals length", which is set to 0 by default. When I increase it, yellow lines appear, like mandarinx described.
It doesn't work when multiple meshes are selected.

@DiVoidation
Copy link

DiVoidation commented Mar 24, 2022

@mandarinx works great! I don't have anough time to fork this, but i think you should add a boolean property like "Show Normals", so you can avoid computations in OnSceneGUI and just return from it. Without it unity starts lagging heavily when large mesh is selected even when you don't need to see normals and set Normal Length to 0.
Or maybe just return from OnSceneGUI when NormalLength == 0.
But i think my option is more logical, so that you don't need to fiddle with normals lenghts just to show it or hide.
Something like this should work

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MeshFilter))]
public class NormalsVisualizer : Editor {

    private const string     EDITOR_PREF_KEY = "_normals_length";
    private const string     EDITOR_PREF_BOOL = "_show_normals";
    private       Mesh       mesh;
    private       MeshFilter mf;
    private       Vector3[]  verts;
    private       Vector3[]  normals;
    private       float      normalsLength = 1f;
    private       bool       showNormals = false;

    private void OnEnable() {
        mf   = target as MeshFilter;
        if (mf != null) {
            mesh = mf.sharedMesh;
        }
        normalsLength = EditorPrefs.GetFloat(EDITOR_PREF_KEY);
        showNormals = EditorPrefs.GetBool(EDITOR_PREF_BOOL);
    }

    private void OnSceneGUI() {
        if (mesh == null || !showNormals) {
            return;
        }

        Handles.matrix = mf.transform.localToWorldMatrix;
        Handles.color = Color.yellow;
        verts = mesh.vertices;
        normals = mesh.normals;
        int len = mesh.vertexCount;
        
        for (int i = 0; i < len; i++) {
            Handles.DrawLine(verts[i], verts[i] + normals[i] * normalsLength);
        }
    }

    public override void OnInspectorGUI() {
        base.OnInspectorGUI();
        EditorGUI.BeginChangeCheck();
        showNormals = EditorGUILayout.Toggle("Show normals", showNormals);
        normalsLength = EditorGUILayout.FloatField("Normals length", normalsLength);
        if (EditorGUI.EndChangeCheck()) {
            EditorPrefs.SetBool(EDITOR_PREF_BOOL, showNormals);
            EditorPrefs.SetFloat(EDITOR_PREF_KEY, normalsLength);
        }
    }
}

@giantkiller
Copy link

giantkiller commented Jun 10, 2022

I am on 2020.3.3f1 LTS. I have dropped this into an editor folder and I see nothing.
This is really a very important addon since this is missing in Unity. Excuse me?
I have been dropping tech upgrade bombs on Unity for years.
The last one was the suggestion for 'Local' in the objects transform area of the inspector window.
They finally did it in 2020.3.
This normals should be in Unity by default too.
Please help me get this to work. Thank you.
in assets/projectwork/editor folder

@Tunied
Copy link

Tunied commented Aug 18, 2022

using UnityEditor;
using UnityEngine;

public class DebugTool_ShowNormal : MonoBehaviour
{
    public bool isShowNormal;
    public Color color = Color.yellow;
    public float normalsLength = 1f;

    private void OnDrawGizmosSelected()
    {
        if (!isShowNormal) return;

        if (!TryGetComponent<MeshFilter>(out var meshFilter)) return;

        var mesh = meshFilter.mesh;
        if (mesh == null) return;

        var defaultColor = Handles.color;
        Handles.matrix = transform.localToWorldMatrix;
        Handles.color = color;
        var verts = mesh.vertices;
        var normals = mesh.normals;
        int len = mesh.vertexCount;

        for (int i = 0; i < len; i++)
        {
            Handles.DrawLine(verts[i], verts[i] + normals[i] * normalsLength);
        }

        Handles.color = defaultColor;
    }
}

Thanks for sharing,
If someone is like me, just need to debug the Normal information in the short term,can use my code.
The principle is the same

@PooperPig
Copy link

Thanks for sharing - this was just what I needed to sort out a niggly bug!

@madtowngaming
Copy link

madtowngaming commented Jan 11, 2024

Warning ... code can pose problems on large meshes ...

Sorry ... the code works but .. I used it on a large mesh and it crashed Unity. I lost about a day's worth of coding.  The problem however is not in code per se but in Unity, or rather both.  It will not work on large meshes. I am not completely sure why Unity crashed but I am working on a custom mesh with a lot of vertices, about 350,000.  As someone pointed out above there should be some way to limit the updating for large meshes.  Also, if the length of the normal in the inspector is set to zero, to hide the normal, then when it is set back to a non zero value the code will not work.  This has to do with some weird Unity editor updating behavior.  In addition there should be a way to turn the method off and on in an easy manner.

@Tunied
Copy link

Tunied commented Jan 11, 2024

Does not work .. garbage code ...

first : it's working.

second: it's not garbage. even if it's not working.

third: maybe something else is garbage?

@madtowngaming
Copy link

madtowngaming commented Jan 12, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment