I have created a simple addon that creates a series of metaballs. Sometimes when the addon executes, the metaballs are rendered invisible (as only outlines, no surface). Usually the metaballs seem to be created correctly (ie, visible) -- sometimes it can be a little bit hard to reproduce the problem.
When I encounter the problem, I can re-execute the script with the same parameters (click to edit a field and then hit enter) and the metaballs/surface will often then suddenly appear. The inconsistency here makes me wonder if this is really a bug in my script or if it might be a bug in Blender itself.
Any ideas what could be causing the issue?
Edit: bpy.context.scene.update() has been no help.
bl_info = {
"name": "Metaball Thing",
"category": "Object",
}
import logging
import random
import math
import mathutils
from mathutils import Vector
import string
import abc
import bpy
log = logging.getLogger(__name__)
class ObjectMetaballThing(bpy.types.Operator):
"""Object Metaball Thing"""
bl_idname = "object.metaball_thing"
bl_label = "Metaball Thing"
bl_options = {'REGISTER', 'UNDO'}
# ========= Properties =========
primaryRadius = bpy.props.FloatProperty(name="Primary Radius", default=9, min=0, max=10000)
seriesRatio = bpy.props.FloatProperty(name="Series Ratio", default=0.333, min=0, max=1)
scale = bpy.props.FloatProperty(name="Scale", default=1.0, min=0, max=100)
numBubbles = bpy.props.IntProperty(name="Num Bubbles", default=3, min=1, max=10)
resFactor = bpy.props.FloatProperty(name="Res Factor", default=0.01, min=0, max=100)
def execute(self, context):
# add metaball object
disambiguator = '-' + ''.join(random.choice(string.ascii_uppercase + string.digits) for c in range(8))
mball = bpy.data.metaballs.new("SurfaceBall" + disambiguator)
obj = bpy.data.objects.new("SurfaceBallObject" + disambiguator, mball)
context.scene.objects.link(obj)
# resolution is based on radius
mball.resolution = self.primaryRadius * self.resFactor
mball.render_resolution = mball.resolution * 0.25
# create the initial ball
centerball = mball.elements.new()
centerball.co = (0, 0, 0)
centerball.radius = self.primaryRadius
# build a series of smaller metaelements
dist = 0
radius = self.primaryRadius
postElements = []
for p in range(self.numBubbles):
dist += radius * 0.5
radius *= self.seriesRatio;
dist += radius * 0.5
center = Vector([0, 0, 1])
center *= dist * self.scale
element = mball.elements.new()
element.co = center
element.radius = radius
log.warning("element at " + str(center.z) + ", radius " + str(radius))
postElements.append(element)
obj.select = True
context.scene.objects.active = obj
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(ObjectMetaballThing.bl_idname)
# store keymaps here to access after registration
addon_keymaps = []
def register():
bpy.utils.register_class(ObjectMetaballThing)
bpy.types.VIEW3D_MT_object.append(menu_func)
# handle the keymap
wm = bpy.context.window_manager
# Note that in background mode (no GUI available), keyconfigs are not available either, so we have to check this
# to avoid nasty errors in background case.
kc = wm.keyconfigs.addon
if kc:
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new(ObjectMetaballThing.bl_idname, 'SPACE', 'PRESS', ctrl=True, shift=True)
addon_keymaps.append((km, kmi))
def unregister():
# Note: when unregistering, it's usually good practice to do it in reverse order you registered.
# Can avoid strange issues like keymap still referring to operators already unregistered...
# handle the keymap
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
bpy.utils.unregister_class(ObjectMetaballThing)
bpy.types.VIEW3D_MT_object.remove(menu_func)
if __name__ == "__main__":
register()