armory/blender/arm/handlers.py

263 lines
9.7 KiB
Python
Raw Normal View History

2016-10-19 13:28:06 +02:00
import bpy
import time
2017-02-09 22:52:47 +01:00
import os
import sys
2016-10-31 19:29:03 +01:00
from bpy.app.handlers import persistent
2017-03-15 12:30:14 +01:00
import arm.utils
import arm.bridge as bridge
import arm.log as log
import arm.props as props
import arm.make as make
import arm.make_state as state
import arm.space_armory as space_armory
import arm.make_renderer as make_renderer
2017-05-24 23:04:24 +02:00
import arm.assets as assets
2016-10-19 13:28:06 +02:00
try:
import barmory
except ImportError:
pass
last_time = time.time()
2016-11-25 11:29:26 +01:00
# last_update_time = time.time()
2016-10-19 13:28:06 +02:00
last_operator = None
2016-11-23 15:34:59 +01:00
redraw_ui = False
redraw_progress = False
2017-04-01 21:25:57 +02:00
first_update = True
2016-10-19 13:28:06 +02:00
2016-10-31 19:29:03 +01:00
@persistent
2016-10-19 13:28:06 +02:00
def on_scene_update_post(context):
global last_time
2016-11-25 11:29:26 +01:00
# global last_update_time
2016-10-19 13:28:06 +02:00
global last_operator
2016-11-23 15:34:59 +01:00
global redraw_ui
global redraw_progress
2017-04-01 21:25:57 +02:00
global first_update
if first_update == True: # Skip first one, object reports is_update_data
first_update = False
return
2016-10-19 13:28:06 +02:00
2016-11-23 15:34:59 +01:00
# Redraw at the start of 'next' frame
if redraw_ui and bpy.context.screen != None:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
area.tag_redraw()
redraw_ui = False
if redraw_progress and bpy.context.screen != None:
for area in bpy.context.screen.areas:
if area.type == 'INFO':
area.tag_redraw()
break
2016-11-25 11:29:26 +01:00
redraw_progress = False
2016-11-23 15:34:59 +01:00
# New operator
2016-11-07 00:31:48 +01:00
ops = bpy.context.window_manager.operators
operators_changed = False
if len(ops) > 0 and last_operator != ops[-1]:
last_operator = ops[-1]
operators_changed = True
2017-02-15 16:55:46 +01:00
# Undo was performed - Blender clears the complete operator stack, undo last known operator atleast
2017-02-17 20:06:57 +01:00
# if len(ops) == 0 and last_operator != None:
# if hasattr(bpy.context, 'object'):
# op_changed(last_operator, bpy.context.object)
# last_operator = None
2016-11-07 00:31:48 +01:00
2017-06-26 15:37:10 +02:00
if state.is_render:
2017-07-01 13:55:35 +02:00
import numpy
2017-07-31 13:20:25 +02:00
# fp = arm.utils.get_fp_build()
krom_location, krom_path = arm.utils.krom_paths()
fp = krom_location
2017-06-26 15:37:10 +02:00
resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene())
2017-08-23 23:37:55 +02:00
wrd = bpy.data.worlds['Arm']
cformat = wrd.rp_rendercapture_format
2017-07-01 13:55:35 +02:00
if cformat == '8bit':
cbits = 4
ctype = numpy.uint8
elif cformat == '16bit':
cbits = 8
ctype = numpy.float16
elif cformat == '32bit':
cbits = 16
ctype = numpy.float32
if os.path.isfile(fp + '/render.bin') and os.path.getsize(fp + '/render.bin') == resx * resy * cbits:
data = numpy.fromfile(fp + '/render.bin', dtype=ctype)
data = data.astype(float)
if cformat == '8bit':
data = numpy.divide(data, 255)
2017-06-26 15:37:10 +02:00
n = "Render Result"
if n in bpy.data.images and bpy.data.images[n].size[0] == resx and bpy.data.images[n].size[1] == resy:
bpy.data.images[n].pixels = data
else:
2017-07-31 13:20:25 +02:00
float_buffer = cformat != '8bit'
image = bpy.data.images.new("Render Result", width=resx, height=resy, float_buffer=float_buffer)
2017-06-26 15:37:10 +02:00
image.pixels = data
state.is_render = False
os.remove(fp + '/render.bin')
2017-06-26 17:18:34 +02:00
print('Output image captured into Blender - UV/Image Editor - Render Result')
2017-06-26 15:37:10 +02:00
2016-11-23 15:34:59 +01:00
# Player running
state.krom_running = False
if not state.is_paused and bpy.context.screen != None:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_ARMORY':
state.krom_running = True
break
2016-10-19 13:28:06 +02:00
2016-11-23 15:34:59 +01:00
# Auto patch on every operator change
2017-09-01 15:24:46 +02:00
if not 'Arm' in bpy.data.worlds:
props.create_wrd()
2017-01-18 14:52:51 +01:00
wrd = bpy.data.worlds['Arm']
2016-11-23 15:34:59 +01:00
if state.krom_running and \
2017-01-18 14:52:51 +01:00
wrd.arm_play_live_patch and \
wrd.arm_play_auto_build and \
2016-11-23 15:34:59 +01:00
operators_changed:
# Otherwise rebuild scene
if bridge.send_operator(last_operator) == False:
2017-07-03 15:16:15 +02:00
if state.compileproc == None:
# state.is_paused = True # Speeds up recompile
assets.invalidate_enabled = False
make.play_project(in_viewport=True)
assets.invalidate_enabled = True
2016-10-19 13:28:06 +02:00
2016-11-23 15:34:59 +01:00
# Use frame rate for update frequency for now
2017-05-19 11:28:09 +02:00
fps_mult = 2.0 if (state.krom_running and arm.utils.get_os() == 'win') else 1.0 # Handlers called less frequently on Windows?
2017-08-29 17:37:41 +02:00
fps = 60
if time.time() - last_time >= (1 / (fps * fps_mult)):
2016-11-23 15:34:59 +01:00
last_time = time.time()
2016-10-19 13:28:06 +02:00
2016-11-23 15:34:59 +01:00
if state.krom_running:
# Read krom console
if barmory.get_console_updated() == 1:
log.print_player(barmory.get_console())
# Read operator console
if barmory.get_operator_updated() == 1:
bridge.parse_operator(barmory.get_operator())
# Tag redraw
if bpy.context.screen != None:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_ARMORY':
2016-10-19 13:28:06 +02:00
area.tag_redraw()
2016-11-23 15:34:59 +01:00
break
2016-10-19 13:28:06 +02:00
# New output has been logged
2016-11-13 11:46:54 +01:00
if log.tag_redraw and bpy.context.screen != None:
2016-10-19 13:28:06 +02:00
log.tag_redraw = False
2016-11-23 15:34:59 +01:00
redraw_progress = True
2016-10-19 13:28:06 +02:00
# Player finished, redraw play buttons
2016-11-13 11:46:54 +01:00
if state.playproc_finished and bpy.context.screen != None:
2016-10-19 13:28:06 +02:00
state.playproc_finished = False
2016-11-23 15:34:59 +01:00
redraw_ui = True
2016-10-19 13:28:06 +02:00
# Compilation finished
2016-11-13 11:46:54 +01:00
if state.compileproc_finished and bpy.context.screen != None:
2016-10-19 13:28:06 +02:00
state.compileproc_finished = False
2016-11-23 15:34:59 +01:00
redraw_ui = True
2016-10-19 13:28:06 +02:00
# Compilation succesfull
if state.compileproc_success:
# Notify embedded player
2016-11-23 15:34:59 +01:00
if state.krom_running:
2017-07-02 20:48:19 +02:00
if state.recompiled and wrd.arm_play_live_patch:
2017-07-03 15:16:15 +02:00
# state.is_paused = False
2017-07-02 20:48:19 +02:00
barmory.parse_code()
for s in state.mod_scripts:
barmory.call_js('armory.Scene.patchTrait("' + s + '");')
else:
barmory.call_js('armory.Scene.patch();')
2016-10-19 13:28:06 +02:00
# Or switch to armory space
2017-03-15 12:30:14 +01:00
elif arm.utils.with_krom() and state.in_viewport:
2016-10-19 13:28:06 +02:00
state.play_area.type = 'VIEW_ARMORY'
# Prevent immediate operator patch
if len(ops) > 0:
last_operator = ops[-1]
2016-11-12 18:30:39 +01:00
# No attribute when using multiple windows?
2017-04-01 21:25:57 +02:00
if hasattr(bpy.context, 'active_object'):
obj = bpy.context.active_object
if obj != None:
if obj.is_updated_data: # + data.is_updated
recache(obj)
if obj.type == 'ARMATURE': # Newly parented objects needs to be recached
for c in obj.children:
if c.is_updated_data:
recache(c)
if hasattr(bpy.context, 'sculpt_object') and bpy.context.sculpt_object != None:
recache(bpy.context.sculpt_object)
if hasattr(bpy.context, 'active_pose_bone') and bpy.context.active_pose_bone != None:
recache(bpy.context.active_object)
2016-10-19 13:28:06 +02:00
2017-01-03 00:16:54 +01:00
if hasattr(bpy.context, 'object'):
obj = bpy.context.object
if obj != None:
if operators_changed:
2017-02-15 16:55:46 +01:00
op_changed(ops[-1], obj)
2017-01-03 00:16:54 +01:00
if obj.active_material != None and obj.active_material.is_updated:
2017-01-18 14:52:51 +01:00
if obj.active_material.lock_cache == True: # is_cached was set to true
obj.active_material.lock_cache = False
else:
obj.active_material.is_cached = False
2016-11-07 00:31:48 +01:00
2017-07-03 15:16:15 +02:00
# Invalidate logic node tree cache if it is being edited..
space = arm.utils.logic_editor_space()
if space != None:
space.node_tree.is_cached = False
2017-05-25 16:48:41 +02:00
2017-04-01 21:25:57 +02:00
def recache(edit_obj):
if edit_obj.type == 'MESH':
2017-08-21 12:17:55 +02:00
edit_obj.data.arm_cached = False
2017-04-01 21:25:57 +02:00
elif edit_obj.type == 'ARMATURE':
2017-08-21 12:17:55 +02:00
edit_obj.data.arm_data_cached = False
2017-04-01 21:25:57 +02:00
2017-02-15 16:55:46 +01:00
def op_changed(op, obj):
# Recache mesh data
if op.bl_idname == 'OBJECT_OT_modifier_add' or \
op.bl_idname == 'OBJECT_OT_modifier_remove' or \
op.bl_idname == 'OBJECT_OT_transform_apply' or \
op.bl_idname == 'OBJECT_OT_shade_smooth' or \
op.bl_idname == 'OBJECT_OT_shade_flat':
2017-08-21 12:17:55 +02:00
obj.data.arm_cached = False
2017-02-09 22:52:47 +01:00
2017-03-06 02:29:03 +01:00
appended_py_paths = []
2016-10-31 19:29:03 +01:00
@persistent
def on_load_post(context):
2017-03-06 02:29:03 +01:00
global appended_py_paths
2017-04-01 21:25:57 +02:00
global first_update
first_update = True
2017-02-09 22:52:47 +01:00
2016-10-31 19:29:03 +01:00
props.init_properties_on_load()
2017-03-15 12:30:14 +01:00
make_renderer.reload_blend_data()
2016-10-31 19:29:03 +01:00
2017-03-06 02:29:03 +01:00
wrd = bpy.data.worlds['Arm']
2017-05-24 23:04:24 +02:00
wrd.arm_recompile = True
2017-09-05 00:36:16 +02:00
if os.path.exists(arm.utils.get_fp() + '/Libraries'):
libs = os.listdir(arm.utils.get_fp() + '/Libraries')
for lib in libs:
if os.path.isdir(arm.utils.get_fp() + '/Libraries/' + lib):
fp = arm.utils.get_fp() + '/Libraries/' + lib
if fp not in appended_py_paths and os.path.exists(fp + '/blender.py'):
sys.path.append(fp)
appended_py_paths.append(fp)
import blender
blender.register()
2017-02-09 22:52:47 +01:00
2016-10-31 19:29:03 +01:00
@persistent
def on_save_pre(context):
props.init_properties_on_save()
2016-10-19 13:28:06 +02:00
def register():
2017-09-09 20:53:46 +02:00
if hasattr(bpy.app.handlers, 'scene_update_post'):
bpy.app.handlers.scene_update_post.append(on_scene_update_post)
2016-10-31 19:29:03 +01:00
bpy.app.handlers.save_pre.append(on_save_pre)
bpy.app.handlers.load_post.append(on_load_post)
2017-01-13 15:09:23 +01:00
# On windows, on_load_post is not called when opening .blend file from explorer
2017-03-15 12:30:14 +01:00
if arm.utils.get_os() == 'win' and arm.utils.get_fp() != '':
2017-01-13 15:09:23 +01:00
on_load_post(None)
2016-10-19 13:28:06 +02:00
def unregister():
2017-09-09 20:53:46 +02:00
if hasattr(bpy.app.handlers, 'scene_update_post'):
bpy.app.handlers.scene_update_post.remove(on_scene_update_post)
2016-10-31 19:29:03 +01:00
bpy.app.handlers.save_pre.remove(on_save_pre)
2017-01-13 15:11:30 +01:00
bpy.app.handlers.load_post.remove(on_load_post)