I'm having a problem updating a mesh after initial creation. I like to create a mesh in one step and update it's vertices afterwards, without completely recreating the mesh.
After several iterations I trimmed the code down to a minimum (below) and it doesn't seem to work... I'm setting a custom property ("update") to the active object and print "create", if the "update" property exists I print "update". The code does not seem to take the update-branch and the property does not seem to be set. If I de-select the object and execute the Operator again, the property is set.
What am I missing? Do I need to update the object? Is there another way of doing this?
def execute(self, context):
print(context.active_object.data.keys())
currentObject = context.active_object
if ('mesh_update' in currentObject.data.keys()):
print("update")
else:
currentObject.data["update"] = True
print("create")
return {'FINISHED'}
Additional information
The operator is called from "add object" menu (the one using shift + a). I also tried an operator property, a group property for objects in general and some other possibilities for days now. The operator property does not work because this setting has to be object (or mesh?) specific and this setting would be carried to the next object the operator gets applied to. Pre defined object properties have the same effect as the dynamic one in the example code above.
Update #1
I handled my problem in a different way now by using a local variable inside the operator to switch between the "create" and "update" stages. This eliminates some other issues and is easier to work with. I had my code working this way some iterations ago while searching for a solution. This leads to maybe the base problem I am trying so solve here: my active object changes between those stages! I explicitly set my newly created object to be the active one but as soon as hitting FINISHED and the operator's execute method starting over again (with the new stage "update"!) the active object changes.
updateMesh = False
def execute(self, context):
use_enter_edit_mode = context.preferences.edit.use_enter_edit_mode
context.preferences.edit.use_enter_edit_mode = False
if context.mode == "EDIT_MESH":
bpy.ops.object.mode_set(mode='OBJECT')
currentObject = context.active_object
# THE "OLD"/INITIALLY ACTIVE OBJECT IS PRINTED HERE ON THE VERY FIRST RUN
print(currentObject)
if self.updateMesh == True:
# ERROR
# THE "OLD"/INITIALLY ACTIVE OBJECT IS PRINTED HERE
# NOT THE NEW ONE FROM BELOW
print("update " + currentObject.name)
else:
self.updateMesh = True
# A NEW OBJECT IS CREATED HERE, LINKED TO VIEW LAYER
# AND SET AS ACTIVE VIEW LAYER OBJECT
# OUTPUTS THE NEW OBJECT
print("create " + currentObject.name)
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
Update #2
Here is a complete working example. Setup is like this:
- execute the script below
- Window > Toggle System Console
- create an object (Monkey/Suzanne for example)
- while object selected press SHIFT + A
- select Mesh > Object Update Test (car icon...)
- edit parameter (Dummy Property)
What it does:
- Suzanne is the active object
- an object "NewObject" is created and set active
- changing the property "Dummy Property" removes "NewObject" and sets Suzanne active again
What it SHOULD do:
- keep the object and show "update NewObject" when changing the property
import bpy
import bmesh
from bpy.utils import ( register_class, unregister_class )
from bpy.props import ( BoolProperty, FloatProperty )
from bpy.types import Operator
from mathutils import Vector
class ObjectUpdateOperator(Operator):
bl_idname = "mesh.objectupdatetest"
bl_label = "Object Update Test"
bl_options = {'REGISTER', 'UNDO'} #, 'PRESET'}
dummyProp : FloatProperty(
name = "Dummy Property",
default = 0.2,
min = 0.0001,
max = 10000.0,
precision = 4,
step = 4,
)
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="Basic Parameters")
box.prop(self, 'dummyProp')
@classmethod
def poll(cls, context):
return context.scene is not None
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}
updateMesh = False
def execute(self, context):
use_enter_edit_mode = context.preferences.edit.use_enter_edit_mode
context.preferences.edit.use_enter_edit_mode = False
if context.mode == "EDIT_MESH":
bpy.ops.object.mode_set(mode='OBJECT')
currentObject = context.active_object
# problematic part...
if self.updateMesh == True: # update object/mesh
print("update " + currentObject.name)
else: # create object/mesh
print("create")
print("active object: " + currentObject.name)
new_mesh = bpy.data.meshes.new("NewMesh")
new_verts = [
Vector((-1, 1, 0)),
Vector((1, 1, 0)),
Vector((1, -1, 0)),
Vector((-1, -1, 0)),
]
new_faces = [[0, 1, 2, 3]]
new_mesh.from_pydata(new_verts, [], new_faces)
new_object = bpy.data.objects.new(name="NewObject", object_data=new_mesh)
view_layer = context.view_layer
view_layer.active_layer_collection.collection.objects.link(new_object)
view_layer.objects.active = None
new_object.select_set(True)
view_layer.objects.active = new_object
print("NEW active object: " + context.active_object.name)
self.updateMesh = True
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def menu_item(self, context):
# we need at minimum one object selected!
if len(context.selected_objects) == 0:
return
layout = self.layout
layout.operator("mesh.objectupdatetest", text="Object Update Test", icon="AUTO")
register and unregister
classes = (
ObjectUpdateOperator,
)
def register():
for cls in classes:
register_class(cls)
# add menu to shift-a add object menu
bpy.types.VIEW3D_MT_mesh_add.append(menu_item)
def unregister():
for cls in reversed(classes):
unregister_class(cls)
# remove menu item
bpy.types.VIEW3D_MT_mesh_add.remove(menu_item)
if name == "main":
register()
```