5
$\begingroup$

We want to see the three values of a normal of a face when we select it in edit mode.
How to do so?

Is there any add-on showing these sort of info realtime?

Update:
It seems the accepted answer does not result correct normal vector!! In the following figure the normal vector of the selected face obviously is not (0,0,-1)!

error

$\endgroup$
10
$\begingroup$

Here's a little hack to add the normal vector to the Data tab in Properties Editor. Copy, paste and run from the Text Editor (will stay until you restart Blender):

Active Polygon Normal in Panel

import bpy
import bmesh
from mathutils import Vector

def draw_func(self, context):
    layout = self.layout

    me = context.object.data

    if me.is_editmode:
        bm = bmesh.from_edit_mesh(me)
        if bm.faces.active is not None:
            row = layout.row()
            col = row.column()
            col.prop(context.window_manager, "active_face_normal", text="", expand=True)
            row.prop(context.window_manager, "active_face_normal", text="")

            if context.window_manager.active_face_normal != bm.faces.active.normal:
                context.window_manager.active_face_normal = bm.faces.active.normal

    else:
        p = me.polygons
        if p.active is not None:
            row = layout.row()
            col = row.column()
            col.prop(p[p.active], "normal", text="", expand=True)
            row.prop(p[p.active], "normal", text="")


def upd_normal(self, context):
    bm = bmesh.from_edit_mesh(context.object.data)
    bm.faces.active.normal = self.active_face_normal.normalized()

def register():
    bpy.types.DATA_PT_normals.prepend(draw_func)
    bpy.types.WindowManager.active_face_normal = bpy.props.FloatVectorProperty(subtype='DIRECTION', precision=3, update=upd_normal)


def unregister():
    bpy.types.DATA_PT_normals.remove(draw_func)
    del bpy.types.WindowManager.active_face_normal



if __name__ == "__main__":
    register()

The normals are actually read-only, but I made it so you can edit the normals in edit mode. Note that they will reset as you leave the mode to e.g. object mode!

If I'm not mistaken, there is a Normal edit addon, which should be similar to what I created. But AFAIK it remembers the normals per face, only used for exports however.

$\endgroup$
  • $\begingroup$ Nice Job. It works well for showing the normal tuple (x,y,z) which was our goal. A side comment on editing normal it does not affect the associated face so it will not be a legitimate normal if edited. Three more things: how to update face to follow edited normal? How to make it readonly (check-box)? If we put the code in addon directory it should be available always, right? $\endgroup$ – Developer Jan 27 '14 at 11:31
  • $\begingroup$ It affects the active face's normal, not the face. Do you actually want to rotate the face? You can use the standard R rotation, or RR for trackball rotation. Note that face normals are used for shading in Blender, they don't define the plane like they would in math / geometry. Not sure about read-only, do you want some sort of protection in the interface (only editable if checkbox ticked), or have the normal property displayed but never editable? Yes, it could be turned into an addon easily. $\endgroup$ – CoDEmanX Jan 27 '14 at 11:37
  • $\begingroup$ Sometimes it is useful to push a face to follow a specified direction i.e., a normal vector. There are may be other ways. For read-only exactly, to protect unwanted editing. Can you make it as add-on, do us a favor :) Anyway, we accept your answer as it does what we were looking for. $\endgroup$ – Developer Jan 27 '14 at 11:58
  • $\begingroup$ Do you want to orient the active face to some reference face (in the same mesh, or other object surface)? Or isn't there a reference, but you want to type values from mind to achieve a certain orientation (e.g. 45° up angle)? Posted new code as second answer: blender.stackexchange.com/a/6566/1363 $\endgroup$ – CoDEmanX Jan 27 '14 at 13:02
  • $\begingroup$ +1 for so great solution. And nice example showing UI access by the way. $\endgroup$ – gabrign Jan 27 '14 at 14:19
2
$\begingroup$

Modified code based on feedback, to support locking (UI only), made it a proper addon and added "Normalize / Update" checkbox to disable realtime updates or live normalization respectively:

bl_info = {
    "name": "Face Normal Display",
    "author": "CoDEmanX",
    "version": (1, 0),
    "blender": (2, 68, 0),
    "location": "Properties Editor > Data > Normals",
    "description": "Show normal property for active mesh face",
    "warning": "Edited normal is discarded on mode change!",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Mesh"}

import bpy
import bmesh
from mathutils import Vector


def draw_func(self, context):
    layout = self.layout

    wm = context.window_manager
    me = context.object.data

    row = layout.row()
    row.active = me.is_editmode

    col = row.column()
    col.scale_y = 3
    col.scale_x = 2
    col.prop(wm, "face_normal_lock", icon='LOCKED' if wm.face_normal_lock else 'UNLOCKED', text="")

    sub = row.row()
    sub.enabled = not wm.face_normal_lock

    col = sub.column()

    if me.is_editmode:
        bm = bmesh.from_edit_mesh(me)
        if bm.faces.active is not None:

            col.prop(wm, "face_normal_active", text="", expand=True)
            sub.prop(wm, "face_normal_active", text="")

            if wm.face_normal_normalize and wm.face_normal_active != bm.faces.active.normal:
                wm.face_normal_active = bm.faces.active.normal
        else:
            col.label("No active face!")

    else:
        p = me.polygons
        if p.active is not None:
            col.prop(p[p.active], "normal", text="", expand=True)
            row.prop(p[p.active], "normal", text="")
        else:
            col.label("No active face!")

    layout.column().prop(wm, "face_normal_normalize")


def upd_normal(self, context):
    bm = bmesh.from_edit_mesh(context.object.data)
    if context.window_manager.face_normal_normalize:
        bm.faces.active.normal = self.face_normal_active.normalized()
    else:
        bm.faces.active.normal = self.face_normal_active

def upd_normalize(self, context):
    if self.face_normal_normalize:
        upd_normal(self, context)

def register():
    bpy.types.DATA_PT_normals.prepend(draw_func)
    bpy.types.WindowManager.face_normal_active = bpy.props.FloatVectorProperty(subtype='DIRECTION', min=-1.0, max=1.0, precision=3, update=upd_normal)
    bpy.types.WindowManager.face_normal_lock = bpy.props.BoolProperty(name="Lock", description="Disable normal editing above")
    bpy.types.WindowManager.face_normal_normalize = bpy.props.BoolProperty(name="Normalize / Update on selection changes", description="Disable for manual input, then re-enable", update=upd_normalize)


def unregister():
    bpy.types.DATA_PT_normals.remove(draw_func)
    del bpy.types.WindowManager.face_normal_active
    del bpy.types.WindowManager.face_normal_lock
    del bpy.types.WindowManager.face_normal_normalize



if __name__ == "__main__":
    register()
$\endgroup$
  • $\begingroup$ Check the update, it seems that the resulting normal vector is not correct! $\endgroup$ – Developer Jan 28 '14 at 1:32
  • $\begingroup$ Did you rotate the object or the mesh data? The normal is in object-space, not world space. In object mode, do Object > Apply > Rotation & Scale. $\endgroup$ – CoDEmanX Jan 28 '14 at 8:49
  • $\begingroup$ It corrected but what does mean that and why it should be like that!? It does not make any sense! $\endgroup$ – Developer Jan 28 '14 at 11:21
  • $\begingroup$ It makes a lot of sense, every object has its own local space to position the geometry or whatever in. Objects can be freely positioned in world space, and their data will not change (still in object space). Instead, the entire data is transformed to worldspace for viewport output / rendering. How object and world space relate, is stored in a model's .matrix_world property. See OpenGL tutorial - The Model Matrix $\endgroup$ – CoDEmanX Jan 28 '14 at 13:26
  • $\begingroup$ Does this still work in the 2.7x codebase? In 2.7.3 I'm finding it only showing 0,0,0 for any face, until I drag left/right in any of the X,Y,Z field; doing so does visually affect the normal of the selected face, but the values remain the same regardless of selected face after that. I am only looking to retrieve the values of the normal vector for each face in my model, not edit them in this way. $\endgroup$ – tobinjim Feb 24 '15 at 23:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.