diff --git a/leenkx/blender/lnx/handlers.py b/leenkx/blender/lnx/handlers.py index 89c6f2fe..ca0bc2e8 100644 --- a/leenkx/blender/lnx/handlers.py +++ b/leenkx/blender/lnx/handlers.py @@ -68,9 +68,10 @@ def on_depsgraph_update_post(self): # Send last operator to Krom wrd = bpy.data.worlds['Lnx'] if state.proc_play is not None and state.target == 'krom' and wrd.lnx_live_patch: - ops = bpy.context.window_manager.operators - if len(ops) > 0 and ops[-1] is not None: - live_patch.on_operator(ops[-1].bl_idname) + if not live_patch.on_depsgraph_update(depsgraph.updates): + ops = bpy.context.window_manager.operators + if len(ops) > 0 and ops[-1] is not None: + live_patch.on_operator(ops[-1].bl_idname) # Hacky solution to update leenkx props after operator executions. # bpy.context.active_operator doesn't always exist, in some cases diff --git a/leenkx/blender/lnx/live_patch.py b/leenkx/blender/lnx/live_patch.py index 7ca57ff3..3e32f33d 100644 --- a/leenkx/blender/lnx/live_patch.py +++ b/leenkx/blender/lnx/live_patch.py @@ -32,6 +32,9 @@ else: __running = False """Whether live patch is currently active""" + scene_cache = {} + """Keys: (event_id, object_name) -> cached value tuple""" + # Any object can act as a message bus owner msgbus_owner = object() @@ -65,6 +68,62 @@ def stop(): bpy.msgbus.clear_by_owner(msgbus_owner) +def get_obj_transforms(obj): + loc = (round(obj.location.x, 6), round(obj.location.y, 6), round(obj.location.z, 6)) + rot = obj.rotation_euler.to_quaternion() + rot = (round(rot.x, 6), round(rot.y, 6), round(rot.z, 6), round(rot.w, 6)) + scale = (round(obj.scale.x, 6), round(obj.scale.y, 6), round(obj.scale.z, 6)) + return loc, rot, scale + + +def get_light_props(obj): + light = obj.data + color = (round(light.color[0], 6), round(light.color[1], 6), round(light.color[2], 6)) + energy = round(light.energy, 6) + return color, energy + +DEPSGRAPH_HANDLERS = { + 'is_updated_transform': [ + ('obj_location', lambda obj: get_obj_transforms(obj)[0]), + ('obj_rotation', lambda obj: get_obj_transforms(obj)[1]), + ('obj_scale', lambda obj: get_obj_transforms(obj)[2]), + ], +} + + +def on_depsgraph_update(updates): + if not __running: + return False + + changed = False + + for update in updates: + uid = update.id + + if isinstance(uid, bpy.types.Object): + name = uid.name + + if update.is_updated_transform: + for event_id, extractor in DEPSGRAPH_HANDLERS['is_updated_transform']: + current = extractor(uid) + cache_key = (event_id, name) + if current != scene_cache.get(cache_key): + scene_cache[cache_key] = current + send_event(event_id) + changed = True + + if update.is_updated_shading and isinstance(uid.data, bpy.types.Light): + color, energy = get_light_props(uid) + for event_id, current in (('light_color', color), ('light_energy', energy)): + cache_key = (event_id, uid.data.name) + if current != scene_cache.get(cache_key): + scene_cache[cache_key] = current + send_event(event_id) + changed = True + + return changed + + def patch_export(): """Re-export the current scene and update the game accordingly.""" if not __running or state.proc_build is not None: