1
$\begingroup$

With the bmesh module, I added a custom data layer to a mesh, holding information for each face.

bm = bmesh.new()
collision_flag_layer = bm.faces.layers.int.new("collision_flags")
for triangle in model.triangles:
    vertices = model.get_triangle_vertices(triangle)
    vert1 = bm.verts.new(vertices[0])
    vert2 = bm.verts.new(vertices[1])
    vert3 = bm.verts.new(vertices[2])
    face = bm.faces.new((vert1, vert2, vert3))
    face[collision_flag_layer] = triangle.collision_flags

Now, in Edit Mode, I created a panel to display this property in labels:

class MarioKart8EditPanel(bpy.types.Panel):
    bl_label = "Mario Kart 8"
    bl_region_type = "UI"
    bl_space_type = "VIEW_3D"

    @classmethod
    def poll(cls, context):
        # Only allow in edit mode for a selected mesh.
        return context.mode == "EDIT_MESH" and context.object is not None and context.object.type == "MESH"

    def draw(self, context):
        obj = context.object
        bm = bmesh.from_edit_mesh(obj.data)
        face = bm.faces.active
        collision_flag_layer = bm.faces.layers.int.get("collision_flags")
        if collision_flag_layer is None or face is None:
            self.layout.row().label("No collision face selected.")
        else:
            #self.layout.prop(face, '["collision_flags"]')
            row = self.layout.row()
            row.label("Collision Flags")
            row.label(str(face[collision_flag_layer]))

That's pretty awesome already, displaying the custom data like this: editpanel

However, since this is edit mode, I want the user to be able to edit this value of course. As you might have noticed from my code already, I commented out a line where I failed to create a number picker for this with the layout.prop method.

How can I implement an edit control for this custom data?

$\endgroup$
1
$\begingroup$

Set up an approach similar to https://blender.stackexchange.com/a/40264/15543. One instance of an edit bmesh is added to a global dictionary on editmode toggle via a scene update handler.

A dummy int property is added to the window manager and is used to set the face layer int via an update method.

The dic bmesh instance can also be used in your panel.

import bpy 
import bmesh
from bpy.app.handlers import persistent
from bpy.props import IntProperty

# global to hold the bmesh.
dic = {}
# update method
def setlayerint(self, context):
    eo = context.edit_object
    bm = dic.setdefault(eo.name, bmesh.from_edit_mesh(eo.data))
    face = bm.faces.active
    cfl = bm.faces.layers.int.get("collision_flags")
    if face and cfl:
        face[cfl] = self.layer_int_value
    return None
#layer_int_value is used to set the value    
bpy.types.WindowManager.layer_int_value = IntProperty(name="Collision Flag",
                                                      update=setlayerint)
#keep wm.layer_int_value updated to active face
def set_collision_flag(bm):
    face = bm.faces.active
    if face is not None:
        #print("BFace", face, face.index)
        cfl = bm.faces.layers.int.get("collision_flags")
        if cfl:
            bpy.context.window_manager.layer_int_value = face[cfl]
    return None
#scene update handler
@persistent
def edit_object_change_handler(scene):
    obj = scene.objects.active
    if obj is None:
        return None
    # add one instance of edit bmesh to global dic
    if obj.mode == 'EDIT' and obj.type == 'MESH':
        bm = dic.setdefault(obj.name, bmesh.from_edit_mesh(obj.data))
        set_collision_flag(bm)
        return None

    dic.clear()
    return None

class MarioKart8EditPanel(bpy.types.Panel):
    bl_label = "Mario Kart 8"
    bl_region_type = "UI"
    bl_space_type = "VIEW_3D"

    @classmethod
    def poll(cls, context):
        # Only allow in edit mode for a selected mesh.
        return context.mode == "EDIT_MESH" and context.object is not None and context.object.type == "MESH"

    def draw(self, context):
        obj = context.object
        bm = dic.setdefault(obj.name, bmesh.from_edit_mesh(obj.data))
        face = bm.faces.active
        collision_flag_layer = bm.faces.layers.int.get("collision_flags")
        if collision_flag_layer is None:
            self.layout.row().label("No collision layer.")
        elif face is None:
            self.layout.row().label("No collision face selected.")
        else:
            wm = context.window_manager
            self.layout.prop(wm, "layer_int_value")
            #self.layout.prop(face, '["collision_flags"]')
            row = self.layout.row()
            row.label("Collision Flags")
            row.label(str(face[collision_flag_layer]))

def register():
    bpy.utils.register_class(MarioKart8EditPanel)
    # clear the handler for testing purposes.
    bpy.app.handlers.scene_update_post.clear()
    bpy.app.handlers.scene_update_post.append(edit_object_change_handler)
register()

Note could add something like the active_face_index to the dic and check on change, instead of constantly setting the face layer value.

$\endgroup$
  • $\begingroup$ Thanks for the answer; I'll try it as soon as possible I get back to the above project! =) $\endgroup$ – Ray Jun 22 '16 at 17:59
  • $\begingroup$ How would I actually write the bmesh back to the mesh? Right now, if someone edits a face property, and then exports, it has not been written back to mesh. Would I have to update the mesh everytime I do an edit? Otherwise the export hasn't changed at all. $\endgroup$ – Ray Jun 27 '16 at 22:19

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.