armory/blender/arm/props_ui.py
E1e5en 5451b3e7e2 Added settings for building APK
Added a panel with settings:
- Building APK After Publish - to start the build after the project has been successfully published (False by default). Disabled if SDK path is not specified;
- Emulator - list of installed emulators in Android Studio (AVD Manager). At the start of Blender, the list is always empty, to fill and update it, you must click the Refresh button. To start the emulator, if you wish, you need to press the "Start" button (the list of emulators is obtained with the emulator -list-avds command, the launch is performed with the emulator -avd [name] command). The "Start" button is disabled if the name of the emulator is not selected from the list;
- Run Emulator After Building APK - launch the emulator after successfully building the APK file. Disabled if no APK build is installed or no emulator name selected.

To perform these operations, you need to specify the ANDROID_SDK_ROOT environment variable, if it is not specified in the OS, then the "Android SDK Path" setting is read and set as the environment variable os.environ ['ANDROID_SDK_ROOT'] to perform operations.
If no value is specified, then the user receives a corresponding message to the console. If the specified value is incorrect, then the user will receive messages from the corresponding programs.
2020-10-22 19:19:03 +03:00

2332 lines
93 KiB
Python

import os
import time
import bpy
from bpy.props import *
import arm.api
import arm.assets as assets
import arm.log as log
import arm.make as make
import arm.make_state as state
import arm.props as props
import arm.props_properties
import arm.nodes_logic
import arm.proxy
import arm.utils
from arm.lightmapper.utility import icon
from arm.lightmapper.properties.denoiser import oidn, optix
import importlib
# Menu in object region
class ARM_PT_ObjectPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.object
if obj == None:
return
layout.prop(obj, 'arm_export')
if not obj.arm_export:
return
layout.prop(obj, 'arm_spawn')
layout.prop(obj, 'arm_mobile')
layout.prop(obj, 'arm_animation_enabled')
if obj.type == 'MESH':
layout.prop(obj, 'arm_instanced')
wrd = bpy.data.worlds['Arm']
layout.prop_search(obj, "arm_tilesheet", wrd, "arm_tilesheetlist", text="Tilesheet")
if obj.arm_tilesheet != '':
selected_ts = None
for ts in wrd.arm_tilesheetlist:
if ts.name == obj.arm_tilesheet:
selected_ts = ts
break
layout.prop_search(obj, "arm_tilesheet_action", selected_ts, "arm_tilesheetactionlist", text="Action")
# Properties list
arm.props_properties.draw_properties(layout, obj)
# Lightmapping props
if obj.type == "MESH":
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_use")
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
pass
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_unwrap_margin")
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filter_override")
row = layout.row()
if obj.TLM_ObjectProperties.tlm_mesh_filter_override:
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_mode")
row = layout.row(align=True)
if obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Gaussian":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_gaussian_strength")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Box":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_box_strength")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Bilateral":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_diameter")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_color_deviation")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_coordinate_deviation")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
else:
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_median_kernel", expand=True)
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
class ARM_PT_ModifiersPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "modifier"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.object
if obj == None:
return
layout.operator("arm.invalidate_cache")
class ARM_PT_ParticlesPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "particle"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.particle_system
if obj == None:
return
layout.prop(obj.settings, 'arm_loop')
layout.prop(obj.settings, 'arm_count_mult')
class ARM_PT_PhysicsPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "physics"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.object
if obj == None:
return
if obj.rigid_body != None:
layout.prop(obj, 'arm_rb_linear_factor')
layout.prop(obj, 'arm_rb_angular_factor')
layout.prop(obj, 'arm_rb_trigger')
layout.prop(obj, 'arm_rb_force_deactivation')
layout.prop(obj, 'arm_rb_ccd')
if obj.soft_body != None:
layout.prop(obj, 'arm_soft_body_margin')
# Menu in data region
class ARM_PT_DataPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "data"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.object
if obj == None:
return
wrd = bpy.data.worlds['Arm']
if obj.type == 'CAMERA':
layout.prop(obj.data, 'arm_frustum_culling')
elif obj.type == 'MESH' or obj.type == 'FONT' or obj.type == 'META':
layout.prop(obj.data, 'arm_dynamic_usage')
layout.operator("arm.invalidate_cache")
elif obj.type == 'LIGHT':
layout.prop(obj.data, 'arm_clip_start')
layout.prop(obj.data, 'arm_clip_end')
layout.prop(obj.data, 'arm_fov')
layout.prop(obj.data, 'arm_shadows_bias')
layout.prop(wrd, 'arm_light_ies_texture')
layout.prop(wrd, 'arm_light_clouds_texture')
elif obj.type == 'SPEAKER':
layout.prop(obj.data, 'arm_play_on_start')
layout.prop(obj.data, 'arm_loop')
layout.prop(obj.data, 'arm_stream')
elif obj.type == 'ARMATURE':
layout.prop(obj.data, 'arm_autobake')
pass
class ARM_PT_WorldPropsPanel(bpy.types.Panel):
bl_label = "Armory World Properties"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "world"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
world = context.world
if world is None:
return
layout.prop(world, 'arm_use_clouds')
col = layout.column(align=True)
col.enabled = world.arm_use_clouds
col.prop(world, 'arm_clouds_lower')
col.prop(world, 'arm_clouds_upper')
col.prop(world, 'arm_clouds_precipitation')
col.prop(world, 'arm_clouds_secondary')
col.prop(world, 'arm_clouds_wind')
col.prop(world, 'arm_clouds_steps')
class ARM_PT_ScenePropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = bpy.context.scene
if scene == None:
return
row = layout.row()
column = row.column()
row.prop(scene, 'arm_export')
class InvalidateCacheButton(bpy.types.Operator):
'''Delete cached mesh data'''
bl_idname = "arm.invalidate_cache"
bl_label = "Invalidate Cache"
def execute(self, context):
context.object.data.arm_cached = False
return{'FINISHED'}
class InvalidateMaterialCacheButton(bpy.types.Operator):
'''Delete cached material data'''
bl_idname = "arm.invalidate_material_cache"
bl_label = "Invalidate Cache"
def execute(self, context):
context.material.arm_cached = False
context.material.signature = ''
return{'FINISHED'}
class ARM_PT_MaterialPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mat = bpy.context.material
if mat == None:
return
layout.prop(mat, 'arm_cast_shadow')
columnb = layout.column()
wrd = bpy.data.worlds['Arm']
columnb.enabled = len(wrd.arm_rplist) > 0 and arm.utils.get_rp().rp_renderer == 'Forward'
columnb.prop(mat, 'arm_receive_shadow')
layout.prop(mat, 'arm_two_sided')
columnb = layout.column()
columnb.enabled = not mat.arm_two_sided
columnb.prop(mat, 'arm_cull_mode')
layout.prop(mat, 'arm_material_id')
layout.prop(mat, 'arm_overlay')
layout.prop(mat, 'arm_decal')
layout.prop(mat, 'arm_discard')
columnb = layout.column()
columnb.enabled = mat.arm_discard
columnb.prop(mat, 'arm_discard_opacity')
columnb.prop(mat, 'arm_discard_opacity_shadows')
layout.prop(mat, 'arm_custom_material')
layout.prop(mat, 'arm_skip_context')
layout.prop(mat, 'arm_particle_fade')
layout.prop(mat, 'arm_billboard')
layout.operator("arm.invalidate_material_cache")
class ARM_PT_MaterialBlendingPropsPanel(bpy.types.Panel):
bl_label = "Blending"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_MaterialPropsPanel"
def draw_header(self, context):
if context.material == None:
return
self.layout.prop(context.material, 'arm_blending', text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mat = bpy.context.material
if mat == None:
return
flow = layout.grid_flow()
flow.enabled = mat.arm_blending
col = flow.column()
col.prop(mat, 'arm_blending_source')
col.prop(mat, 'arm_blending_destination')
col.prop(mat, 'arm_blending_operation')
col = flow.column()
col.prop(mat, 'arm_blending_source_alpha')
col.prop(mat, 'arm_blending_destination_alpha')
col.prop(mat, 'arm_blending_operation_alpha')
class ARM_PT_ArmoryPlayerPanel(bpy.types.Panel):
bl_label = "Armory Player"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
row = layout.row(align=True)
row.alignment = 'EXPAND'
if state.proc_play is None and state.proc_build is None:
row.operator("arm.play", icon="PLAY")
else:
row.operator("arm.stop", icon="MESH_PLANE")
row.operator("arm.clean_menu")
layout.prop(wrd, 'arm_runtime')
layout.prop(wrd, 'arm_play_camera')
layout.prop(wrd, 'arm_play_scene')
if log.num_warnings > 0:
box = layout.box()
# Less spacing between lines
col = box.column(align=True)
col.label(text=f'{log.num_warnings} warnings occurred during compilation!', icon='ERROR')
# Blank icon to achieve the same indentation as the line before
col.label(text='Please open the console to get more information.', icon='BLANK1')
class ARM_PT_ArmoryExporterPanel(bpy.types.Panel):
bl_label = "Armory Exporter"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
row = layout.row(align=True)
row.alignment = 'EXPAND'
row.operator("arm.build_project")
# row.operator("arm.patch_project")
row.operator("arm.publish_project", icon="EXPORT")
row.enabled = wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0
rows = 2
if len(wrd.arm_exporterlist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_ExporterList", "The_List", wrd, "arm_exporterlist", wrd, "arm_exporterlist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_exporterlist.new_item", icon='ADD', text="")
col.operator("arm_exporterlist.delete_item", icon='REMOVE', text="")
col.menu("ARM_MT_ExporterListSpecials", icon='DOWNARROW_HLT', text="")
if len(wrd.arm_exporterlist) > 1:
col.separator()
op = col.operator("arm_exporterlist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_exporterlist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0:
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
box = layout.box().column()
box.prop(item, 'arm_project_target')
if item.arm_project_target == 'custom':
box.prop(item, 'arm_project_khamake')
box.prop(item, arm.utils.target_to_gapi(item.arm_project_target))
wrd.arm_rpcache_list.clear() # Make UIList work with prop_search()
for i in wrd.arm_rplist:
wrd.arm_rpcache_list.add().name = i.name
box.prop_search(item, "arm_project_rp", wrd, "arm_rpcache_list", text="Render Path")
box.prop_search(item, 'arm_project_scene', bpy.data, 'scenes', text='Scene')
layout.separator()
col = layout.column()
col.prop(wrd, 'arm_project_name')
col.prop(wrd, 'arm_project_package')
col.prop(wrd, 'arm_project_bundle')
col.prop(wrd, 'arm_project_version')
col.prop(wrd, 'arm_project_icon')
col.prop(wrd, 'arm_dce')
col.prop(wrd, 'arm_compiler_inline')
col.prop(wrd, 'arm_minify_js')
col.prop(wrd, 'arm_optimize_data')
col.prop(wrd, 'arm_asset_compression')
col.prop(wrd, 'arm_single_data_file')
class ARM_PT_ArmoryExporterAndroidSettingsPanel(bpy.types.Panel):
bl_label = "Android Settings"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = { 'DEFAULT_CLOSED' }
bl_parent_id = "ARM_PT_ArmoryExporterPanel"
@classmethod
def poll(cls, context):
wrd = bpy.data.worlds['Arm']
is_check = False
for item in wrd.arm_exporterlist:
is_check = item.arm_project_target == 'android-hl'
if is_check:
break
return is_check
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0:
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
layout.enabled = item.arm_project_target == 'android-hl'
# Options
row = layout.row()
row.prop(wrd, 'arm_winorient')
row = layout.row()
row.prop(wrd, 'arm_project_android_sdk_compile')
row = layout.row()
row.prop(wrd, 'arm_project_android_sdk_min')
row = layout.row()
row.prop(wrd, 'arm_project_android_sdk_target')
class ARM_PT_ArmoryExporterAndroidPermissionsPanel(bpy.types.Panel):
bl_label = "Permissions"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = { 'DEFAULT_CLOSED' }
bl_parent_id = "ARM_PT_ArmoryExporterAndroidSettingsPanel"
@classmethod
def poll(cls, context):
wrd = bpy.data.worlds['Arm']
is_check = False
for item in wrd.arm_exporterlist:
is_check = item.arm_project_target == 'android-hl'
if is_check:
break
return is_check
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0:
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
layout.enabled = item.arm_project_target == 'android-hl'
# Permission
row = layout.row()
rows = 2
if len(wrd.arm_exporter_android_permission_list) > 1:
rows = 4
row.template_list("ARM_UL_Exporter_AndroidPermissionList", "The_List", wrd, "arm_exporter_android_permission_list", wrd, "arm_exporter_android_permission_list_index", rows=rows)
col = row.column(align=True)
col.operator("arm_exporter_android_permission_list.new_item", icon='ADD', text="")
col.operator("arm_exporter_android_permission_list.delete_item", icon='REMOVE', text="")
row = layout.row()
if wrd.arm_exporter_android_permission_list_index >= 0 and len(wrd.arm_exporter_android_permission_list) > 0:
item = wrd.arm_exporter_android_permission_list[wrd.arm_exporter_android_permission_list_index]
row = layout.row()
row.prop(item, 'arm_android_permissions')
class ARM_PT_ArmoryExporterAndroidAbiPanel(bpy.types.Panel):
bl_label = "Android ABI Filters"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = { 'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_ArmoryExporterAndroidSettingsPanel"
@classmethod
def poll(cls, context):
wrd = bpy.data.worlds['Arm']
is_check = False
for item in wrd.arm_exporterlist:
is_check = item.arm_project_target == 'android-hl'
if is_check:
break
return is_check
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0:
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
layout.enabled = item.arm_project_target == 'android-hl'
# ABIs
row = layout.row()
rows = 2
if len(wrd.arm_exporter_android_abi_list) > 1:
rows = 4
row.template_list("ARM_UL_Exporter_AndroidAbiList", "The_List", wrd, "arm_exporter_android_abi_list", wrd, "arm_exporter_android_abi_list_index", rows=rows)
col = row.column(align=True)
col.operator("arm_exporter_android_abi_list.new_item", icon='ADD', text="")
col.operator("arm_exporter_android_abi_list.delete_item", icon='REMOVE', text="")
row = layout.row()
if wrd.arm_exporter_android_abi_list_index >= 0 and len(wrd.arm_exporter_android_abi_list) > 0:
item = wrd.arm_exporter_android_abi_list[wrd.arm_exporter_android_abi_list_index]
row = layout.row()
row.prop(item, 'arm_android_abi')
class ARM_PT_ArmoryExporterAndroidBuildAPKPanel(bpy.types.Panel):
bl_label = "Building APK"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = { 'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_ArmoryExporterAndroidSettingsPanel"
@classmethod
def poll(cls, context):
wrd = bpy.data.worlds['Arm']
is_check = False
for item in wrd.arm_exporterlist:
is_check = item.arm_project_target == 'android-hl'
if is_check:
break
return is_check
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0:
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
layout.enabled = item.arm_project_target == 'android-hl'
row = layout.row()
row.prop(wrd, 'arm_project_android_build_apk')
path = arm.utils.get_android_sdk_root_path()
row.enabled = len(path) > 0
row = layout.row()
row.prop(wrd, 'arm_project_android_list_avd')
col = row.column(align=True)
col.operator('arm.update_list_android_emulator', text='', icon='FILE_REFRESH')
col.enabled = len(path) > 0
col = row.column(align=True)
col.operator('arm.run_android_emulator', text='', icon='PLAY')
col.enabled = len(path) > 0 and len(arm.utils.get_android_emulator_name()) > 0
row = layout.row()
row.prop(wrd, 'arm_project_android_run_avd')
row.enabled = arm.utils.get_project_android_build_apk() and len(arm.utils.get_android_emulator_name()) > 0
class ARM_PT_ArmoryProjectPanel(bpy.types.Panel):
bl_label = "Armory Project"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
row = layout.row(align=True)
row.operator("arm.open_editor", icon="DESKTOP")
row.operator("arm.open_project_folder", icon="FILE_FOLDER")
class ARM_PT_ProjectFlagsPanel(bpy.types.Panel):
bl_label = "Flags"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_parent_id = "ARM_PT_ArmoryProjectPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
layout.prop(wrd, 'arm_verbose_output')
layout.prop(wrd, 'arm_cache_build')
layout.prop(wrd, 'arm_live_patch')
layout.prop(wrd, 'arm_stream_scene')
layout.prop(wrd, 'arm_batch_meshes')
layout.prop(wrd, 'arm_batch_materials')
layout.prop(wrd, 'arm_write_config')
layout.prop(wrd, 'arm_minimize')
layout.prop(wrd, 'arm_deinterleaved_buffers')
layout.prop(wrd, 'arm_export_tangents')
layout.prop(wrd, 'arm_loadscreen')
layout.prop(wrd, 'arm_texture_quality')
layout.prop(wrd, 'arm_sound_quality')
class ARM_PT_ProjectFlagsDebugConsolePanel(bpy.types.Panel):
bl_label = "Debug Console"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_ProjectFlagsPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
row = layout.row()
row.enabled = wrd.arm_ui != 'Disabled'
row.prop(wrd, 'arm_debug_console')
row = layout.row()
row.enabled = wrd.arm_debug_console
row.prop(wrd, 'arm_debug_console_position')
row = layout.row()
row.enabled = wrd.arm_debug_console
row.prop(wrd, 'arm_debug_console_scale')
row = layout.row()
row.enabled = wrd.arm_debug_console
row.prop(wrd, 'arm_debug_console_visible')
class ARM_PT_ProjectWindowPanel(bpy.types.Panel):
bl_label = "Window"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_ArmoryProjectPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
layout.prop(wrd, 'arm_winmode')
layout.prop(wrd, 'arm_winresize')
col = layout.column()
col.enabled = wrd.arm_winresize
col.prop(wrd, 'arm_winmaximize')
layout.prop(wrd, 'arm_winminimize')
layout.prop(wrd, 'arm_vsync')
class ARM_PT_ProjectModulesPanel(bpy.types.Panel):
bl_label = "Modules"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_ArmoryProjectPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
layout.prop(wrd, 'arm_audio')
layout.prop(wrd, 'arm_physics')
if wrd.arm_physics != 'Disabled':
layout.prop(wrd, 'arm_physics_engine')
layout.prop(wrd, 'arm_navigation')
if wrd.arm_navigation != 'Disabled':
layout.prop(wrd, 'arm_navigation_engine')
layout.prop(wrd, 'arm_ui')
layout.prop_search(wrd, 'arm_khafile', bpy.data, 'texts', text='Khafile')
layout.prop(wrd, 'arm_project_root')
class ArmVirtualInputPanel(bpy.types.Panel):
bl_label = "Armory Virtual Input"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
class ArmoryPlayButton(bpy.types.Operator):
'''Launch player in new window'''
bl_idname = 'arm.play'
bl_label = 'Play'
def execute(self, context):
if state.proc_build != None:
return {"CANCELLED"}
if not arm.utils.check_saved(self):
return {"CANCELLED"}
if not arm.utils.check_sdkpath(self):
return {"CANCELLED"}
arm.utils.check_projectpath(None)
arm.utils.check_default_props()
assets.invalidate_enabled = False
make.play()
assets.invalidate_enabled = True
return{'FINISHED'}
class ArmoryStopButton(bpy.types.Operator):
'''Stop currently running player'''
bl_idname = 'arm.stop'
bl_label = 'Stop'
def execute(self, context):
if state.proc_play != None:
state.proc_play.terminate()
state.proc_play = None
elif state.proc_build != None:
state.proc_build.terminate()
state.proc_build = None
return{'FINISHED'}
class ArmoryBuildProjectButton(bpy.types.Operator):
'''Build and compile project'''
bl_idname = 'arm.build_project'
bl_label = 'Build'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
if not arm.utils.check_sdkpath(self):
return {"CANCELLED"}
arm.utils.check_projectpath(self)
arm.utils.check_default_props()
wrd = bpy.data.worlds['Arm']
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
if item.arm_project_rp == '':
item.arm_project_rp = wrd.arm_rplist[wrd.arm_rplist_index].name
if item.arm_project_scene == None:
item.arm_project_scene = context.scene
# Assume unique rp names
rplist_index = wrd.arm_rplist_index
for i in range(0, len(wrd.arm_rplist)):
if wrd.arm_rplist[i].name == item.arm_project_rp:
wrd.arm_rplist_index = i
break
assets.invalidate_shader_cache(None, None)
assets.invalidate_enabled = False
make.build(item.arm_project_target, is_export=True)
make.compile()
wrd.arm_rplist_index = rplist_index
assets.invalidate_enabled = True
return{'FINISHED'}
class ArmoryPublishProjectButton(bpy.types.Operator):
'''Build project ready for publishing'''
bl_idname = 'arm.publish_project'
bl_label = 'Publish'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
if not arm.utils.check_sdkpath(self):
return {"CANCELLED"}
self.report({'INFO'}, 'Publishing project, check console for details.')
arm.utils.check_projectpath(self)
arm.utils.check_default_props()
wrd = bpy.data.worlds['Arm']
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
if item.arm_project_rp == '':
item.arm_project_rp = wrd.arm_rplist[wrd.arm_rplist_index].name
if item.arm_project_scene == None:
item.arm_project_scene = context.scene
# Assume unique rp names
rplist_index = wrd.arm_rplist_index
for i in range(0, len(wrd.arm_rplist)):
if wrd.arm_rplist[i].name == item.arm_project_rp:
wrd.arm_rplist_index = i
break
make.clean()
assets.invalidate_enabled = False
make.build(item.arm_project_target, is_publish=True, is_export=True)
make.compile()
wrd.arm_rplist_index = rplist_index
assets.invalidate_enabled = True
return{'FINISHED'}
class ArmoryOpenProjectFolderButton(bpy.types.Operator):
'''Open project folder'''
bl_idname = 'arm.open_project_folder'
bl_label = 'Project Folder'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
arm.utils.open_folder(arm.utils.get_fp())
return{'FINISHED'}
class ArmoryOpenEditorButton(bpy.types.Operator):
'''Launch this project in the IDE'''
bl_idname = 'arm.open_editor'
bl_label = 'Code Editor'
bl_description = 'Open Project in IDE'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
arm.utils.check_default_props()
if not os.path.exists(arm.utils.get_fp() + "/khafile.js"):
print('Generating Krom project for IDE build configuration')
make.build('krom')
arm.utils.open_editor()
return{'FINISHED'}
class CleanMenu(bpy.types.Menu):
bl_label = "Ok?"
bl_idname = "OBJECT_MT_clean_menu"
def draw(self, context):
layout = self.layout
layout.operator("arm.clean_project")
class CleanButtonMenu(bpy.types.Operator):
'''Clean cached data'''
bl_label = "Clean"
bl_idname = "arm.clean_menu"
def execute(self, context):
bpy.ops.wm.call_menu(name=CleanMenu.bl_idname)
return {"FINISHED"}
class ArmoryCleanProjectButton(bpy.types.Operator):
'''Delete all cached project data'''
bl_idname = 'arm.clean_project'
bl_label = 'Clean Project'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
make.clean()
return{'FINISHED'}
def draw_view3d_header(self, context):
if state.proc_build is not None:
self.layout.label(text='Compiling..')
elif log.info_text != '':
self.layout.label(text=log.info_text)
def draw_view3d_object_menu(self, context):
self.layout.separator()
self.layout.operator_context = 'INVOKE_DEFAULT'
self.layout.operator('arm.copy_traits_to_active')
class ARM_PT_RenderPathPanel(bpy.types.Panel):
bl_label = "Armory Render Path"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
rows = 2
if len(wrd.arm_rplist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_RPList", "The_List", wrd, "arm_rplist", wrd, "arm_rplist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_rplist.new_item", icon='ADD', text="")
col.operator("arm_rplist.delete_item", icon='REMOVE', text="")
if len(wrd.arm_rplist) > 1:
col.separator()
op = col.operator("arm_rplist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_rplist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if wrd.arm_rplist_index < 0 or len(wrd.arm_rplist) == 0:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
if len(arm.api.drivers) > 0:
rpdat.rp_driver_list.clear()
rpdat.rp_driver_list.add().name = 'Armory'
for d in arm.api.drivers:
rpdat.rp_driver_list.add().name = arm.api.drivers[d]['driver_name']
layout.prop_search(rpdat, "rp_driver", rpdat, "rp_driver_list", text="Driver")
layout.separator()
if rpdat.rp_driver != 'Armory' and arm.api.drivers[rpdat.rp_driver]['draw_props'] != None:
arm.api.drivers[rpdat.rp_driver]['draw_props'](layout)
return
class ARM_PT_RenderPathRendererPanel(bpy.types.Panel):
bl_label = "Renderer"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.prop(rpdat, 'rp_renderer')
if rpdat.rp_renderer == 'Forward':
layout.prop(rpdat, 'rp_depthprepass')
layout.prop(rpdat, 'arm_material_model')
layout.prop(rpdat, 'rp_translucency_state')
layout.prop(rpdat, 'rp_overlays_state')
layout.prop(rpdat, 'rp_decals_state')
layout.prop(rpdat, 'rp_blending_state')
layout.prop(rpdat, 'rp_draw_order')
layout.prop(rpdat, 'arm_samples_per_pixel')
layout.prop(rpdat, 'arm_texture_filter')
layout.prop(rpdat, 'rp_sss_state')
col = layout.column()
col.enabled = rpdat.rp_sss_state != 'Off'
col.prop(rpdat, 'arm_sss_width')
layout.prop(rpdat, 'arm_rp_displacement')
if rpdat.arm_rp_displacement == 'Tessellation':
layout.label(text='Mesh')
layout.prop(rpdat, 'arm_tess_mesh_inner')
layout.prop(rpdat, 'arm_tess_mesh_outer')
layout.label(text='Shadow')
layout.prop(rpdat, 'arm_tess_shadows_inner')
layout.prop(rpdat, 'arm_tess_shadows_outer')
layout.prop(rpdat, 'arm_particles')
layout.prop(rpdat, 'arm_skin')
row = layout.row()
row.enabled = rpdat.arm_skin == 'On'
row.prop(rpdat, 'arm_skin_max_bones_auto')
row = layout.row()
row.enabled = not rpdat.arm_skin_max_bones_auto
row.prop(rpdat, 'arm_skin_max_bones')
layout.prop(rpdat, "rp_hdr")
layout.prop(rpdat, "rp_stereo")
layout.prop(rpdat, 'arm_culling')
layout.prop(rpdat, 'rp_pp')
class ARM_PT_RenderPathShadowsPanel(bpy.types.Panel):
bl_label = "Shadows"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw_header(self, context):
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
self.layout.prop(rpdat, "rp_shadows", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.enabled = rpdat.rp_shadows
layout.prop(rpdat, 'rp_shadowmap_cube')
layout.prop(rpdat, 'rp_shadowmap_cascade')
layout.prop(rpdat, 'rp_shadowmap_cascades')
col = layout.column()
col2 = col.column()
col2.enabled = rpdat.rp_shadowmap_cascades != '1'
col2.prop(rpdat, 'arm_shadowmap_split')
col.prop(rpdat, 'arm_shadowmap_bounds')
col.prop(rpdat, 'arm_pcfsize')
class ARM_PT_RenderPathVoxelsPanel(bpy.types.Panel):
bl_label = "Voxel AO"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw_header(self, context):
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
self.layout.prop(rpdat, "rp_voxelao", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.enabled = rpdat.rp_voxelao
layout.prop(rpdat, 'arm_voxelgi_shadows')
layout.prop(rpdat, 'arm_voxelgi_cones')
layout.prop(rpdat, 'rp_voxelgi_resolution')
layout.prop(rpdat, 'rp_voxelgi_resolution_z')
layout.prop(rpdat, 'arm_voxelgi_dimensions')
layout.prop(rpdat, 'arm_voxelgi_revoxelize')
col2 = layout.column()
col2.enabled = rpdat.arm_voxelgi_revoxelize
col2.prop(rpdat, 'arm_voxelgi_camera')
col2.prop(rpdat, 'arm_voxelgi_temporal')
layout.prop(rpdat, 'arm_voxelgi_occ')
layout.prop(rpdat, 'arm_voxelgi_step')
layout.prop(rpdat, 'arm_voxelgi_range')
layout.prop(rpdat, 'arm_voxelgi_offset')
layout.prop(rpdat, 'arm_voxelgi_aperture')
class ARM_PT_RenderPathWorldPanel(bpy.types.Panel):
bl_label = "World"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.prop(rpdat, "rp_background")
layout.prop(rpdat, 'arm_irradiance')
col = layout.column()
col.enabled = rpdat.arm_irradiance
col.prop(rpdat, 'arm_radiance')
colb = col.column()
colb.enabled = rpdat.arm_radiance
colb.prop(rpdat, 'arm_radiance_size')
layout.prop(rpdat, 'arm_clouds')
layout.prop(rpdat, "rp_water")
col = layout.column(align=True)
col.enabled = rpdat.rp_water
col.prop(rpdat, 'arm_water_level')
col.prop(rpdat, 'arm_water_density')
col.prop(rpdat, 'arm_water_displace')
col.prop(rpdat, 'arm_water_speed')
col.prop(rpdat, 'arm_water_freq')
col.prop(rpdat, 'arm_water_refract')
col.prop(rpdat, 'arm_water_reflect')
col.prop(rpdat, 'arm_water_color')
class ARM_PT_RenderPathPostProcessPanel(bpy.types.Panel):
bl_label = "Post Process"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw_header(self, context):
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
self.layout.prop(rpdat, "rp_render_to_texture", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.enabled = rpdat.rp_render_to_texture
row = layout.row()
row.prop(rpdat, "rp_antialiasing")
layout.prop(rpdat, "rp_supersampling")
layout.prop(rpdat, 'arm_rp_resolution')
if rpdat.arm_rp_resolution == 'Custom':
layout.prop(rpdat, 'arm_rp_resolution_size')
layout.prop(rpdat, 'arm_rp_resolution_filter')
layout.prop(rpdat, 'rp_dynres')
layout.separator()
row = layout.row()
row.prop(rpdat, "rp_ssgi")
col = layout.column()
col.enabled = rpdat.rp_ssgi != 'Off'
col.prop(rpdat, 'arm_ssgi_half_res')
col.prop(rpdat, 'arm_ssgi_rays')
col.prop(rpdat, 'arm_ssgi_radius')
col.prop(rpdat, 'arm_ssgi_strength')
col.prop(rpdat, 'arm_ssgi_max_steps')
layout.separator()
layout.prop(rpdat, "rp_ssr")
col = layout.column()
col.enabled = rpdat.rp_ssr
col.prop(rpdat, 'arm_ssr_half_res')
col.prop(rpdat, 'arm_ssr_ray_step')
col.prop(rpdat, 'arm_ssr_min_ray_step')
col.prop(rpdat, 'arm_ssr_search_dist')
col.prop(rpdat, 'arm_ssr_falloff_exp')
col.prop(rpdat, 'arm_ssr_jitter')
layout.separator()
layout.prop(rpdat, 'arm_ssrs')
col = layout.column()
col.enabled = rpdat.arm_ssrs
col.prop(rpdat, 'arm_ssrs_ray_step')
layout.prop(rpdat, 'arm_micro_shadowing')
layout.separator()
layout.prop(rpdat, "rp_bloom")
col = layout.column()
col.enabled = rpdat.rp_bloom
col.prop(rpdat, 'arm_bloom_threshold')
col.prop(rpdat, 'arm_bloom_strength')
col.prop(rpdat, 'arm_bloom_radius')
layout.separator()
layout.prop(rpdat, "rp_motionblur")
col = layout.column()
col.enabled = rpdat.rp_motionblur != 'Off'
col.prop(rpdat, 'arm_motion_blur_intensity')
layout.separator()
layout.prop(rpdat, "rp_volumetriclight")
col = layout.column()
col.enabled = rpdat.rp_volumetriclight
col.prop(rpdat, 'arm_volumetric_light_air_color')
col.prop(rpdat, 'arm_volumetric_light_air_turbidity')
col.prop(rpdat, 'arm_volumetric_light_steps')
layout.separator()
layout.prop(rpdat, "rp_chromatic_aberration")
col = layout.column()
col.enabled = rpdat.rp_chromatic_aberration
col.prop(rpdat, 'arm_chromatic_aberration_type')
col.prop(rpdat, 'arm_chromatic_aberration_strength')
if rpdat.arm_chromatic_aberration_type == "Spectral":
col.prop(rpdat, 'arm_chromatic_aberration_samples')
class ARM_PT_RenderPathCompositorPanel(bpy.types.Panel):
bl_label = "Compositor"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "ARM_PT_RenderPathPanel"
def draw_header(self, context):
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
self.layout.prop(rpdat, "rp_compositornodes", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
if len(wrd.arm_rplist) <= wrd.arm_rplist_index:
return
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
layout.enabled = rpdat.rp_compositornodes
layout.prop(rpdat, 'arm_tonemap')
layout.prop(rpdat, 'arm_letterbox')
col = layout.column()
col.enabled = rpdat.arm_letterbox
col.prop(rpdat, 'arm_letterbox_size')
layout.prop(rpdat, 'arm_sharpen')
col = layout.column()
col.enabled = rpdat.arm_sharpen
col.prop(rpdat, 'arm_sharpen_strength')
layout.prop(rpdat, 'arm_fisheye')
layout.prop(rpdat, 'arm_vignette')
col = layout.column()
col.enabled = rpdat.arm_vignette
col.prop(rpdat, 'arm_vignette_strength')
layout.prop(rpdat, 'arm_lensflare')
layout.prop(rpdat, 'arm_grain')
col = layout.column()
col.enabled = rpdat.arm_grain
col.prop(rpdat, 'arm_grain_strength')
layout.prop(rpdat, 'arm_fog')
col = layout.column(align=True)
col.enabled = rpdat.arm_fog
col.prop(rpdat, 'arm_fog_color')
col.prop(rpdat, 'arm_fog_amounta')
col.prop(rpdat, 'arm_fog_amountb')
layout.separator()
layout.prop(rpdat, "rp_autoexposure")
col = layout.column()
col.enabled = rpdat.rp_autoexposure
col.prop(rpdat, 'arm_autoexposure_strength', text='Strength')
col.prop(rpdat, 'arm_autoexposure_speed', text='Speed')
layout.prop(rpdat, 'arm_lens_texture')
if rpdat.arm_lens_texture != "":
layout.prop(rpdat, 'arm_lens_texture_masking')
if rpdat.arm_lens_texture_masking:
layout.prop(rpdat, 'arm_lens_texture_masking_centerMinClip')
layout.prop(rpdat, 'arm_lens_texture_masking_centerMaxClip')
layout.prop(rpdat, 'arm_lens_texture_masking_luminanceMin')
layout.prop(rpdat, 'arm_lens_texture_masking_luminanceMax')
layout.prop(rpdat, 'arm_lens_texture_masking_brightnessExp')
layout.prop(rpdat, 'arm_lut_texture')
class ARM_PT_BakePanel(bpy.types.Panel):
bl_label = "Armory Bake"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scn = bpy.data.scenes[context.scene.name]
row = layout.row(align=True)
row.prop(scn, "arm_bakemode", expand=True)
if scn.arm_bakemode == "Static Map":
row = layout.row(align=True)
row.alignment = 'EXPAND'
row.operator("arm.bake_textures", icon="RENDER_STILL")
row.operator("arm.bake_apply")
col = layout.column()
col.prop(scn, 'arm_bakelist_scale')
col.prop(scn.cycles, "samples")
layout.prop(scn, 'arm_bakelist_unwrap')
rows = 2
if len(scn.arm_bakelist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_BakeList", "The_List", scn, "arm_bakelist", scn, "arm_bakelist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_bakelist.new_item", icon='ADD', text="")
col.operator("arm_bakelist.delete_item", icon='REMOVE', text="")
col.menu("ARM_MT_BakeListSpecials", icon='DOWNARROW_HLT', text="")
if len(scn.arm_bakelist) > 1:
col.separator()
op = col.operator("arm_bakelist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_bakelist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if scn.arm_bakelist_index >= 0 and len(scn.arm_bakelist) > 0:
item = scn.arm_bakelist[scn.arm_bakelist_index]
layout.prop_search(item, "obj", bpy.data, "objects", text="Object")
layout.prop(item, "res_x")
layout.prop(item, "res_y")
else:
scene = context.scene
sceneProperties = scene.TLM_SceneProperties
row = layout.row(align=True)
row = layout.row(align=True)
#We list LuxCoreRender as available, by default we assume Cycles exists
row.prop(sceneProperties, "tlm_lightmap_engine")
if sceneProperties.tlm_lightmap_engine == "Cycles":
#CYCLES SETTINGS HERE
engineProperties = scene.TLM_EngineProperties
row = layout.row(align=True)
row.label(text="General Settings")
row = layout.row(align=True)
row.operator("tlm.build_lightmaps")
row = layout.row(align=True)
row.operator("tlm.clean_lightmaps")
row = layout.row(align=True)
row.operator("tlm.explore_lightmaps")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_apply_on_unwrap")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_headless")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_alert_on_finish")
row = layout.row(align=True)
row.label(text="Cycles Settings")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_quality")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_resolution_scale")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_bake_mode")
if scene.TLM_EngineProperties.tlm_bake_mode == "Background":
row = layout.row(align=True)
row.label(text="Warning! Background mode is currently unstable", icon_value=2)
row = layout.row(align=True)
row.prop(engineProperties, "tlm_caching_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_directional_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_lightmap_savedir")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_dilation_margin")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_exposure_multiplier")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_setting_supersample")
elif sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
#LUXCORE SETTINGS HERE
luxcore_available = False
#Look for Luxcorerender in the renderengine classes
for engine in bpy.types.RenderEngine.__subclasses__():
if engine.bl_idname == "LUXCORE":
luxcore_available = True
break
row = layout.row(align=True)
if not luxcore_available:
row.label(text="Please install BlendLuxCore.")
else:
row.label(text="LuxCoreRender not yet available.")
elif sceneProperties.tlm_lightmap_engine == "OctaneRender":
#LUXCORE SETTINGS HERE
octane_available = False
row = layout.row(align=True)
row.label(text="Octane Render not yet available.")
##################
#DENOISE SETTINGS!
row = layout.row(align=True)
row.label(text="Denoise Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_denoise_use")
row = layout.row(align=True)
if sceneProperties.tlm_denoise_use:
row.prop(sceneProperties, "tlm_denoise_engine", expand=True)
row = layout.row(align=True)
if sceneProperties.tlm_denoise_engine == "Integrated":
row.label(text="No options for Integrated.")
elif sceneProperties.tlm_denoise_engine == "OIDN":
denoiseProperties = scene.TLM_OIDNEngineProperties
row.prop(denoiseProperties, "tlm_oidn_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_threads")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_affinity")
# row = layout.row(align=True)
# row.prop(denoiseProperties, "tlm_denoise_ao")
elif sceneProperties.tlm_denoise_engine == "Optix":
denoiseProperties = scene.TLM_OptixEngineProperties
row.prop(denoiseProperties, "tlm_optix_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_denoise_ao")
##################
#FILTERING SETTINGS!
row = layout.row(align=True)
row.label(text="Filtering Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_filtering_use")
row = layout.row(align=True)
if sceneProperties.tlm_filtering_use:
if sceneProperties.tlm_filtering_engine == "OpenCV":
cv2 = importlib.util.find_spec("cv2")
if cv2 is None:
row = layout.row(align=True)
row.label(text="OpenCV is not installed. Install it below.")
row = layout.row(align=True)
row.label(text="It is recommended to install as administrator.")
row = layout.row(align=True)
row.operator("tlm.install_opencv_lightmaps")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_mode")
row = layout.row(align=True)
if scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_gaussian_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_box_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_diameter")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_color_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_coordinate_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row.prop(scene.TLM_SceneProperties, "tlm_filtering_median_kernel", expand=True)
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_numpy_filtering_mode")
##################
#ENCODING SETTINGS!
row = layout.row(align=True)
row.label(text="Encoding Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_use")
row = layout.row(align=True)
if sceneProperties.tlm_encoding_use:
row.prop(sceneProperties, "tlm_encoding_mode", expand=True)
if sceneProperties.tlm_encoding_mode == "RGBM" or sceneProperties.tlm_encoding_mode == "RGBD":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_range")
if sceneProperties.tlm_encoding_mode == "LogLuv":
pass
if sceneProperties.tlm_encoding_mode == "HDR":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_format")
row = layout.row(align=True)
row.label(text="Encoding Settings")
row = layout.row(align=True)
row = layout.row(align=True)
row.operator("tlm.enable_selection")
row = layout.row(align=True)
row.operator("tlm.disable_selection")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_override_object_settings")
if sceneProperties.tlm_override_object_settings:
row = layout.row(align=True)
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if sceneProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
if scene.TLM_AtlasList_index >= 0 and len(scene.TLM_AtlasList) > 0:
row = layout.row()
item = scene.TLM_AtlasList[scene.TLM_AtlasList_index]
row.prop_search(sceneProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
else:
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
else:
row.prop(sceneProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_unwrap_margin")
row = layout.row(align=True)
row.operator("tlm.remove_uv_selection")
row = layout.row(align=True)
##################
#SELECTION OPERATORS!
row = layout.row(align=True)
row.label(text="Selection Operators")
row = layout.row(align=True)
row = layout.row(align=True)
row.operator("tlm.enable_selection")
row = layout.row(align=True)
row.operator("tlm.disable_selection")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_override_object_settings")
if sceneProperties.tlm_override_object_settings:
row = layout.row(align=True)
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if sceneProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
if scene.TLM_AtlasList_index >= 0 and len(scene.TLM_AtlasList) > 0:
row = layout.row()
item = scene.TLM_AtlasList[scene.TLM_AtlasList_index]
row.prop_search(sceneProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
else:
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
else:
row.prop(sceneProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_unwrap_margin")
row = layout.row(align=True)
row.operator("tlm.remove_uv_selection")
row = layout.row(align=True)
class ArmGenLodButton(bpy.types.Operator):
'''Automatically generate LoD levels'''
bl_idname = 'arm.generate_lod'
bl_label = 'Auto Generate'
def lod_name(self, name, level):
return name + '_LOD' + str(level + 1)
def execute(self, context):
obj = context.object
if obj == None:
return{'CANCELLED'}
# Clear
mdata = context.object.data
mdata.arm_lodlist_index = 0
mdata.arm_lodlist.clear()
# Lod levels
wrd = bpy.data.worlds['Arm']
ratio = wrd.arm_lod_gen_ratio
num_levels = wrd.arm_lod_gen_levels
for level in range(0, num_levels):
new_obj = obj.copy()
for i in range(0, 3):
new_obj.location[i] = 0
new_obj.rotation_euler[i] = 0
new_obj.scale[i] = 1
new_obj.data = obj.data.copy()
new_obj.name = self.lod_name(obj.name, level)
new_obj.parent = obj
new_obj.hide_viewport = True
new_obj.hide_render = True
mod = new_obj.modifiers.new('Decimate', 'DECIMATE')
mod.ratio = ratio
ratio *= wrd.arm_lod_gen_ratio
context.scene.collection.objects.link(new_obj)
# Screen sizes
for level in range(0, num_levels):
mdata.arm_lodlist.add()
mdata.arm_lodlist[-1].name = self.lod_name(obj.name, level)
mdata.arm_lodlist[-1].screen_size_prop = (1 - (1 / (num_levels + 1)) * level) - (1 / (num_levels + 1))
return{'FINISHED'}
class ARM_PT_LodPanel(bpy.types.Panel):
bl_label = "Armory Lod"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
obj = bpy.context.object
# Mesh only for now
if obj.type != 'MESH':
return
mdata = obj.data
rows = 2
if len(mdata.arm_lodlist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_LodList", "The_List", mdata, "arm_lodlist", mdata, "arm_lodlist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_lodlist.new_item", icon='ADD', text="")
col.operator("arm_lodlist.delete_item", icon='REMOVE', text="")
if len(mdata.arm_lodlist) > 1:
col.separator()
op = col.operator("arm_lodlist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_lodlist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if mdata.arm_lodlist_index >= 0 and len(mdata.arm_lodlist) > 0:
item = mdata.arm_lodlist[mdata.arm_lodlist_index]
layout.prop_search(item, "name", bpy.data, "objects", text="Object")
layout.prop(item, "screen_size_prop")
layout.prop(mdata, "arm_lod_material")
# Auto lod for meshes
if obj.type == 'MESH':
layout.separator()
layout.operator("arm.generate_lod")
wrd = bpy.data.worlds['Arm']
layout.prop(wrd, 'arm_lod_gen_levels')
layout.prop(wrd, 'arm_lod_gen_ratio')
class ArmGenTerrainButton(bpy.types.Operator):
'''Generate terrain sectors'''
bl_idname = 'arm.generate_terrain'
bl_label = 'Generate'
def execute(self, context):
scn = context.scene
if scn == None:
return{'CANCELLED'}
sectors = scn.arm_terrain_sectors
size = scn.arm_terrain_sector_size
height_scale = scn.arm_terrain_height_scale
# Create material
mat = bpy.data.materials.new(name="Terrain")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
node = nodes.new('ShaderNodeDisplacement')
node.location = (-200, 100)
node.inputs[2].default_value = height_scale
node.space = 'WORLD'
links.new(nodes['Material Output'].inputs[2], node.outputs[0])
node = nodes.new('ShaderNodeTexImage')
node.location = (-600, 100)
node.interpolation = 'Closest'
node.extension = 'EXTEND'
node.arm_material_param = True
node.name = '_TerrainHeight'
node.label = '_TerrainHeight' # Height-map texture link for this sector
links.new(nodes['Displacement'].inputs[0], nodes['_TerrainHeight'].outputs[0])
node = nodes.new('ShaderNodeBump')
node.location = (-200, -200)
node.inputs[0].default_value = 5.0
links.new(nodes['Bump'].inputs[2], nodes['_TerrainHeight'].outputs[0])
links.new(nodes['Principled BSDF'].inputs[17], nodes['Bump'].outputs[0])
# Create sectors
root_obj = bpy.data.objects.new("Terrain", None)
root_obj.location[0] = 0
root_obj.location[1] = 0
root_obj.location[2] = 0
root_obj.arm_export = False
scn.collection.objects.link(root_obj)
scn.arm_terrain_object = root_obj
for i in range(sectors[0] * sectors[1]):
j = str(i + 1).zfill(2)
x = i % sectors[0]
y = int(i / sectors[0])
bpy.ops.mesh.primitive_plane_add(location=(x * size, -y * size, 0))
slice_obj = bpy.context.active_object
slice_obj.scale[0] = size / 2
slice_obj.scale[1] = -(size / 2)
slice_obj.scale[2] = height_scale
slice_obj.data.materials.append(mat)
for p in slice_obj.data.polygons:
p.use_smooth = True
slice_obj.name = 'Terrain.' + j
slice_obj.parent = root_obj
sub_mod = slice_obj.modifiers.new('Subdivision', 'SUBSURF')
sub_mod.subdivision_type = 'SIMPLE'
disp_mod = slice_obj.modifiers.new('Displace', 'DISPLACE')
disp_mod.texture_coords = 'UV'
disp_mod.texture = bpy.data.textures.new(name='Terrain.' + j, type='IMAGE')
disp_mod.texture.extension = 'EXTEND'
disp_mod.texture.use_interpolation = False
disp_mod.texture.use_mipmap = False
disp_mod.texture.image = bpy.data.images.load(filepath=scn.arm_terrain_textures+'/heightmap_' + j + '.png')
f = 1
levels = 0
while f < disp_mod.texture.image.size[0]:
f *= 2
levels += 1
sub_mod.levels = sub_mod.render_levels = levels
return{'FINISHED'}
class ARM_PT_TerrainPanel(bpy.types.Panel):
bl_label = "Armory Terrain"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scn = bpy.context.scene
if scn == None:
return
layout.prop(scn, 'arm_terrain_textures')
layout.prop(scn, 'arm_terrain_sectors')
layout.prop(scn, 'arm_terrain_sector_size')
layout.prop(scn, 'arm_terrain_height_scale')
layout.operator('arm.generate_terrain')
layout.prop(scn, 'arm_terrain_object')
class ARM_PT_TilesheetPanel(bpy.types.Panel):
bl_label = "Armory Tilesheet"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
wrd = bpy.data.worlds['Arm']
rows = 2
if len(wrd.arm_tilesheetlist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_TilesheetList", "The_List", wrd, "arm_tilesheetlist", wrd, "arm_tilesheetlist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_tilesheetlist.new_item", icon='ADD', text="")
col.operator("arm_tilesheetlist.delete_item", icon='REMOVE', text="")
if len(wrd.arm_tilesheetlist) > 1:
col.separator()
op = col.operator("arm_tilesheetlist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_tilesheetlist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if wrd.arm_tilesheetlist_index >= 0 and len(wrd.arm_tilesheetlist) > 0:
dat = wrd.arm_tilesheetlist[wrd.arm_tilesheetlist_index]
layout.prop(dat, "tilesx_prop")
layout.prop(dat, "tilesy_prop")
layout.prop(dat, "framerate_prop")
layout.label(text='Actions')
rows = 2
if len(dat.arm_tilesheetactionlist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_TilesheetList", "The_List", dat, "arm_tilesheetactionlist", dat, "arm_tilesheetactionlist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_tilesheetactionlist.new_item", icon='ADD', text="")
col.operator("arm_tilesheetactionlist.delete_item", icon='REMOVE', text="")
if len(dat.arm_tilesheetactionlist) > 1:
col.separator()
op = col.operator("arm_tilesheetactionlist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_tilesheetactionlist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if dat.arm_tilesheetactionlist_index >= 0 and len(dat.arm_tilesheetactionlist) > 0:
adat = dat.arm_tilesheetactionlist[dat.arm_tilesheetactionlist_index]
layout.prop(adat, "start_prop")
layout.prop(adat, "end_prop")
layout.prop(adat, "loop_prop")
class ARM_PT_ProxyPanel(bpy.types.Panel):
bl_label = "Armory Proxy"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.operator("arm.make_proxy")
obj = bpy.context.object
if obj != None and obj.proxy != None:
layout.label(text="Sync")
layout.prop(obj, "arm_proxy_sync_loc")
layout.prop(obj, "arm_proxy_sync_rot")
layout.prop(obj, "arm_proxy_sync_scale")
layout.prop(obj, "arm_proxy_sync_materials")
layout.prop(obj, "arm_proxy_sync_modifiers")
layout.prop(obj, "arm_proxy_sync_traits")
row = layout.row()
row.enabled = obj.arm_proxy_sync_traits
row.prop(obj, "arm_proxy_sync_trait_props")
layout.operator("arm.proxy_toggle_all")
layout.operator("arm.proxy_apply_all")
class ArmMakeProxyButton(bpy.types.Operator):
'''Create proxy from linked object'''
bl_idname = 'arm.make_proxy'
bl_label = 'Make Proxy'
def execute(self, context):
obj = context.object
if obj == None:
return{'CANCELLED'}
if obj.library == None:
self.report({'ERROR'}, 'Select linked object')
arm.proxy.make(obj)
return{'FINISHED'}
class ArmProxyToggleAllButton(bpy.types.Operator):
bl_idname = 'arm.proxy_toggle_all'
bl_label = 'Toggle All'
def execute(self, context):
obj = context.object
b = not obj.arm_proxy_sync_loc
obj.arm_proxy_sync_loc = b
obj.arm_proxy_sync_rot = b
obj.arm_proxy_sync_scale = b
obj.arm_proxy_sync_materials = b
obj.arm_proxy_sync_modifiers = b
obj.arm_proxy_sync_traits = b
obj.arm_proxy_sync_trait_props = b
return{'FINISHED'}
class ArmProxyApplyAllButton(bpy.types.Operator):
bl_idname = 'arm.proxy_apply_all'
bl_label = 'Apply to All'
def execute(self, context):
for obj in bpy.data.objects:
if obj.proxy == None:
continue
if obj.proxy == context.object.proxy:
obj.arm_proxy_sync_loc = context.object.arm_proxy_sync_loc
obj.arm_proxy_sync_rot = context.object.arm_proxy_sync_rot
obj.arm_proxy_sync_scale = context.object.arm_proxy_sync_scale
obj.arm_proxy_sync_materials = context.object.arm_proxy_sync_materials
obj.arm_proxy_sync_modifiers = context.object.arm_proxy_sync_modifiers
obj.arm_proxy_sync_traits = context.object.arm_proxy_sync_traits
obj.arm_proxy_sync_trait_props = context.object.arm_proxy_sync_trait_props
return{'FINISHED'}
class ArmSyncProxyButton(bpy.types.Operator):
bl_idname = 'arm.sync_proxy'
bl_label = 'Sync'
def execute(self, context):
if len(bpy.data.libraries) > 0:
for obj in bpy.data.objects:
if obj == None or obj.proxy == None:
continue
if obj.arm_proxy_sync_loc:
arm.proxy.sync_location(obj)
if obj.arm_proxy_sync_rot:
arm.proxy.sync_rotation(obj)
if obj.arm_proxy_sync_scale:
arm.proxy.sync_scale(obj)
if obj.arm_proxy_sync_materials:
arm.proxy.sync_materials(obj)
if obj.arm_proxy_sync_modifiers:
arm.proxy.sync_modifiers(obj)
if obj.arm_proxy_sync_traits:
arm.proxy.sync_traits(obj)
print('Armory: Proxy objects synchronized')
return{'FINISHED'}
class ArmPrintTraitsButton(bpy.types.Operator):
bl_idname = 'arm.print_traits'
bl_label = 'Print Traits'
def execute(self, context):
for s in bpy.data.scenes:
print(s.name + ' traits:')
for o in s.objects:
for t in o.arm_traitlist:
if not t.enabled_prop:
continue
tname = t.node_tree_prop.name if t.type_prop == 'Logic Nodes' else t.class_name_prop
print('Object {0} - {1}'.format(o.name, tname))
return{'FINISHED'}
class ARM_PT_MaterialNodePanel(bpy.types.Panel):
bl_label = 'Armory Material Node'
bl_idname = 'ARM_PT_MaterialNodePanel'
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = 'Node'
@classmethod
def poll(cls, context):
return (context.space_data.tree_type == 'ShaderNodeTree'
and context.space_data.edit_tree
and context.space_data.shader_type == 'OBJECT')
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
n = context.active_node
if n != None and (n.bl_idname == 'ShaderNodeRGB' or n.bl_idname == 'ShaderNodeValue' or n.bl_idname == 'ShaderNodeTexImage'):
layout.prop(context.active_node, 'arm_material_param')
class ARM_OT_ShowFileVersionInfo(bpy.types.Operator):
bl_label = 'Show old file version info'
bl_idname = 'arm.show_old_file_version_info'
bl_description = ('Displays an info panel that warns about opening a file'
'which was created in a previous version of Armory')
# bl_options = {'INTERNAL'}
wrd = None
def draw_message_box(self, context):
file_version = ARM_OT_ShowFileVersionInfo.wrd.arm_version
current_version = props.arm_version
layout = self.layout
layout = layout.column(align=True)
layout.alignment = 'EXPAND'
if current_version == file_version:
layout.label('This file was saved in', icon='INFO')
layout.label('the current Armory version', icon='BLANK1')
layout.separator()
layout.label(f'(version: {current_version}')
row = layout.row(align=True)
row.active_default = True
row.operator('arm.discard_popup', text='Ok')
# this will help order versions better, somewhat.
# note: this is NOT complete
current_version = tuple( current_version.split('.') )
file_version = tuple( file_version.split('.') )
if current_version > file_version:
layout.label(text='Warning: This file was saved in a', icon='ERROR')
layout.label(text='previous version of Armory!', icon='BLANK1')
layout.separator()
layout.label(text='Please inform yourself about breaking changes!', icon='BLANK1')
layout.label(text=f'File saved in: {file_version}', icon='BLANK1')
layout.label(text=f'Current version: {current_version}', icon='BLANK1')
layout.separator()
layout.separator()
layout.label(text='Should Armory try to automatically update', icon='BLANK1')
layout.label(text='the file to the current SDK version?', icon='BLANK1')
layout.separator()
row = layout.row(align=True)
row.active_default = True
row.operator('arm.update_file_sdk', text='Yes')
row.active_default = False
row.operator('arm.discard_popup', text='No')
else:
layout.label(text='Warning: This file was saved in a', icon='ERROR')
layout.label(text='future version of Armory!', icon='BLANK1')
layout.separator()
layout.label(text='It is impossible to downgrade a file,', icon='BLANK1')
layout.label(text='Something will probably be broken here.', icon='BLANK1')
layout.label(text=f'File saved in: {file_version}', icon='BLANK1')
layout.label(text=f'Current version: {current_version}', icon='BLANK1')
layout.separator()
layout.separator()
layout.label(text='Please check how this file was created', icon='BLANK1')
layout.separator()
row = layout.row(align=True)
row.active_default = True
row.operator('arm.discard_popup', text='Ok')
def execute(self, context):
ARM_OT_ShowFileVersionInfo.wrd = bpy.data.worlds['Arm']
context.window_manager.popover(ARM_OT_ShowFileVersionInfo.draw_message_box, ui_units_x=16)
return {"FINISHED"}
class ARM_OT_ShowNodeUpdateErrors(bpy.types.Operator):
bl_label = 'Show upgrade failure details'
bl_idname = 'arm.show_node_update_errors'
bl_description = ('Displays an info panel that shows the different errors that occurred when upgrading nodes')
wrd = None # a helper internal variable
def draw_message_box(self, context):
list_of_errors = arm.nodes_logic.replacement_errors.copy()
# note: list_of_errors is a set of tuples: `(error_type, node_class, tree_name)`
# where `error_type` can be "unregistered", "update failed", "future version", "bad version", or "misc."
file_version = ARM_OT_ShowNodeUpdateErrors.wrd.arm_version
current_version = props.arm_version
# this will help order versions better, somewhat.
# note: this is NOT complete
current_version_2 = tuple( current_version.split('.') )
file_version_2 = tuple( file_version.split('.') )
is_armory_upgrade = (current_version_2 > file_version_2)
error_types = set()
errored_trees = set()
errored_nodes = set()
for error_entry in list_of_errors:
error_types.add(error_entry[0])
errored_nodes.add(error_entry[1])
errored_trees.add(error_entry[2])
layout = self.layout
layout = layout.column(align=True)
layout.alignment = 'EXPAND'
layout.label(text="Some nodes failed to be updated to the current armory version", icon="ERROR")
if current_version==file_version:
layout.label(text="(This might be because you are using a development snapshot, or a homemade version ;) )", icon='BLANK1')
elif not is_armory_upgrade:
layout.label(text="(Please note that it is not possible do downgrade nodes to a previous version either.", icon='BLANK1')
layout.label(text="This might be the cause of your problem.)", icon='BLANK1')
layout.label(text=f'File saved in: {file_version}', icon='BLANK1')
layout.label(text=f'Current version: {current_version}', icon='BLANK1')
layout.separator()
if 'update failed' in error_types:
layout.label(text="Some nodes do not have an update procedure to deal with the version saved in this file.", icon='BLANK1')
if current_version==file_version:
layout.label(text="(if you are a developer, this might be because you didn't implement it yet.)", icon='BLANK1')
if 'bad version' in error_types:
layout.label(text="Some nodes do not have version information attached to them.", icon='BLANK1')
if 'unregistered' in error_types:
if is_armory_upgrade:
layout.label(text='Some nodes seem to be too old to be understood by armory anymore', icon='BLANK1')
else:
layout.label(text="Some nodes are unknown to armory, either because they are too new or too old.", icon='BLANK1')
if 'future version' in error_types:
if is_armory_upgrade:
layout.label(text='Somehow, some nodes seem to have been created with a future version of armory.', icon='BLANK1')
else:
layout.label(text='Some nodes seem to have been created with a future version of armory.', icon='BLANK1')
if 'misc.' in error_types:
layout.label(text="Some nodes' update procedure failed to complete")
layout.separator()
layout.label(text='the nodes impacted are the following:', icon='BLANK1')
for node in errored_nodes:
layout.label(text=f' {node}', icon='BLANK1')
layout.separator()
layout.label(text='the node trees impacted are the following:', icon='BLANK1')
for tree in errored_trees:
layout.label(text=f' "{tree}"', icon='BLANK1')
layout.separator()
layout.label(text="A detailed error report has been saved next to the blender file.", icon='BLANK1')
layout.label(text="the file name is \"node_update_failure\", followed by the current time.", icon='BLANK1')
layout.separator()
row = layout.row(align=True)
row.active_default = False
row.operator('arm.discard_popup', text='Ok')
def execute(self, context):
ARM_OT_ShowNodeUpdateErrors.wrd = bpy.data.worlds['Arm']
context.window_manager.popover(ARM_OT_ShowNodeUpdateErrors.draw_message_box, ui_units_x=32)
return {"FINISHED"}
class ARM_OT_UpdateFileSDK(bpy.types.Operator):
bl_idname = 'arm.update_file_sdk'
bl_label = 'Update file to current SDK version'
bl_description = bl_label
bl_options = {'INTERNAL'}
def execute(self, context):
wrd = bpy.data.worlds['Arm']
# This allows for seamless migration from ealier versions of Armory
for rp in wrd.arm_rplist: # TODO: deprecated
if rp.rp_gi != 'Off':
rp.rp_gi = 'Off'
rp.rp_voxelao = True
# Replace deprecated nodes
arm.nodes_logic.replaceAll()
wrd.arm_version = props.arm_version
wrd.arm_commit = props.arm_commit
arm.make.clean()
print(f'Project updated to SDK {props.arm_version}. Please save the .blend file.')
return {'FINISHED'}
class ARM_OT_DiscardPopup(bpy.types.Operator):
"""Empty operator for discarding dialogs."""
bl_idname = 'arm.discard_popup'
bl_label = 'OK'
bl_description = 'Discard'
bl_options = {'INTERNAL'}
def execute(self, context):
return {'FINISHED'}
class ArmoryUpdateListAndroidEmulatorButton(bpy.types.Operator):
'''Updating the list of emulators for the Android platform'''
bl_idname = 'arm.update_list_android_emulator'
bl_label = 'Update List Emulators'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
if not arm.utils.check_sdkpath(self):
return {"CANCELLED"}
if len(arm.utils.get_android_sdk_root_path()) == 0:
return {"CANCELLED"}
os.environ['ANDROID_SDK_ROOT'] = arm.utils.get_android_sdk_root_path()
items, err = arm.utils.get_android_emulators_list()
if len(err) > 0:
print('Update List Emulators Warning: File "'+ arm.utils.get_android_emulator_file() +'" not found. Check that the variable ANDROID_SDK_ROOT is correct in environment variables or in "Android SDK Path" setting: \n- If you specify an environment variable ANDROID_SDK_ROOT, then you need to restart Blender;\n- If you specify the setting "Android SDK Path", then repeat operation "Publish"')
return{'FINISHED'}
items_enum = []
for i in items:
items_enum.append((i, i, i))
bpy.types.World.arm_project_android_list_avd = EnumProperty(items=items_enum, name="Emulator", update=assets.invalidate_compiler_cache)
return{'FINISHED'}
class ArmoryUpdateListAndroidEmulatorRunButton(bpy.types.Operator):
'''Launch Android emulator selected from the list'''
bl_idname = 'arm.run_android_emulator'
bl_label = 'Launch Emulator'
def execute(self, context):
if not arm.utils.check_saved(self):
return {"CANCELLED"}
if not arm.utils.check_sdkpath(self):
return {"CANCELLED"}
if len(arm.utils.get_android_sdk_root_path()) == 0:
return {"CANCELLED"}
make.run_android_emulators(arm.utils.get_android_emulator_name())
return{'FINISHED'}
def register():
bpy.utils.register_class(ARM_PT_ObjectPropsPanel)
bpy.utils.register_class(ARM_PT_ModifiersPropsPanel)
bpy.utils.register_class(ARM_PT_ParticlesPropsPanel)
bpy.utils.register_class(ARM_PT_PhysicsPropsPanel)
bpy.utils.register_class(ARM_PT_DataPropsPanel)
bpy.utils.register_class(ARM_PT_ScenePropsPanel)
bpy.utils.register_class(ARM_PT_WorldPropsPanel)
bpy.utils.register_class(InvalidateCacheButton)
bpy.utils.register_class(InvalidateMaterialCacheButton)
bpy.utils.register_class(ARM_PT_MaterialPropsPanel)
bpy.utils.register_class(ARM_PT_MaterialBlendingPropsPanel)
bpy.utils.register_class(ARM_PT_ArmoryPlayerPanel)
bpy.utils.register_class(ARM_PT_ArmoryExporterPanel)
bpy.utils.register_class(ARM_PT_ArmoryExporterAndroidSettingsPanel)
bpy.utils.register_class(ARM_PT_ArmoryExporterAndroidPermissionsPanel)
bpy.utils.register_class(ARM_PT_ArmoryExporterAndroidAbiPanel)
bpy.utils.register_class(ARM_PT_ArmoryExporterAndroidBuildAPKPanel)
bpy.utils.register_class(ARM_PT_ArmoryProjectPanel)
bpy.utils.register_class(ARM_PT_ProjectFlagsPanel)
bpy.utils.register_class(ARM_PT_ProjectFlagsDebugConsolePanel)
bpy.utils.register_class(ARM_PT_ProjectWindowPanel)
bpy.utils.register_class(ARM_PT_ProjectModulesPanel)
bpy.utils.register_class(ARM_PT_RenderPathPanel)
bpy.utils.register_class(ARM_PT_RenderPathRendererPanel)
bpy.utils.register_class(ARM_PT_RenderPathShadowsPanel)
bpy.utils.register_class(ARM_PT_RenderPathVoxelsPanel)
bpy.utils.register_class(ARM_PT_RenderPathWorldPanel)
bpy.utils.register_class(ARM_PT_RenderPathPostProcessPanel)
bpy.utils.register_class(ARM_PT_RenderPathCompositorPanel)
bpy.utils.register_class(ARM_PT_BakePanel)
# bpy.utils.register_class(ArmVirtualInputPanel)
bpy.utils.register_class(ArmoryPlayButton)
bpy.utils.register_class(ArmoryStopButton)
bpy.utils.register_class(ArmoryBuildProjectButton)
bpy.utils.register_class(ArmoryOpenProjectFolderButton)
bpy.utils.register_class(ArmoryOpenEditorButton)
bpy.utils.register_class(CleanMenu)
bpy.utils.register_class(CleanButtonMenu)
bpy.utils.register_class(ArmoryCleanProjectButton)
bpy.utils.register_class(ArmoryPublishProjectButton)
bpy.utils.register_class(ArmGenLodButton)
bpy.utils.register_class(ARM_PT_LodPanel)
bpy.utils.register_class(ArmGenTerrainButton)
bpy.utils.register_class(ARM_PT_TerrainPanel)
bpy.utils.register_class(ARM_PT_TilesheetPanel)
bpy.utils.register_class(ARM_PT_ProxyPanel)
bpy.utils.register_class(ArmMakeProxyButton)
bpy.utils.register_class(ArmProxyToggleAllButton)
bpy.utils.register_class(ArmProxyApplyAllButton)
bpy.utils.register_class(ArmSyncProxyButton)
bpy.utils.register_class(ArmPrintTraitsButton)
bpy.utils.register_class(ARM_PT_MaterialNodePanel)
bpy.utils.register_class(ARM_OT_UpdateFileSDK)
bpy.utils.register_class(ARM_OT_ShowFileVersionInfo)
bpy.utils.register_class(ARM_OT_ShowNodeUpdateErrors)
bpy.utils.register_class(ARM_OT_DiscardPopup)
bpy.utils.register_class(ArmoryUpdateListAndroidEmulatorButton)
bpy.utils.register_class(ArmoryUpdateListAndroidEmulatorRunButton)
bpy.types.VIEW3D_HT_header.append(draw_view3d_header)
bpy.types.VIEW3D_MT_object.append(draw_view3d_object_menu)
def unregister():
bpy.types.VIEW3D_MT_object.remove(draw_view3d_object_menu)
bpy.types.VIEW3D_HT_header.remove(draw_view3d_header)
bpy.utils.unregister_class(ArmoryUpdateListAndroidEmulatorRunButton)
bpy.utils.unregister_class(ArmoryUpdateListAndroidEmulatorButton)
bpy.utils.unregister_class(ARM_OT_DiscardPopup)
bpy.utils.unregister_class(ARM_OT_ShowNodeUpdateErrors)
bpy.utils.unregister_class(ARM_OT_ShowFileVersionInfo)
bpy.utils.unregister_class(ARM_OT_UpdateFileSDK)
bpy.utils.unregister_class(ARM_PT_ObjectPropsPanel)
bpy.utils.unregister_class(ARM_PT_ModifiersPropsPanel)
bpy.utils.unregister_class(ARM_PT_ParticlesPropsPanel)
bpy.utils.unregister_class(ARM_PT_PhysicsPropsPanel)
bpy.utils.unregister_class(ARM_PT_DataPropsPanel)
bpy.utils.unregister_class(ARM_PT_WorldPropsPanel)
bpy.utils.unregister_class(ARM_PT_ScenePropsPanel)
bpy.utils.unregister_class(InvalidateCacheButton)
bpy.utils.unregister_class(InvalidateMaterialCacheButton)
bpy.utils.unregister_class(ARM_PT_MaterialPropsPanel)
bpy.utils.unregister_class(ARM_PT_MaterialBlendingPropsPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryPlayerPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryExporterAndroidBuildAPKPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryExporterAndroidAbiPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryExporterAndroidPermissionsPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryExporterAndroidSettingsPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryExporterPanel)
bpy.utils.unregister_class(ARM_PT_ArmoryProjectPanel)
bpy.utils.unregister_class(ARM_PT_ProjectFlagsDebugConsolePanel)
bpy.utils.unregister_class(ARM_PT_ProjectFlagsPanel)
bpy.utils.unregister_class(ARM_PT_ProjectWindowPanel)
bpy.utils.unregister_class(ARM_PT_ProjectModulesPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathRendererPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathShadowsPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathVoxelsPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathWorldPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathPostProcessPanel)
bpy.utils.unregister_class(ARM_PT_RenderPathCompositorPanel)
bpy.utils.unregister_class(ARM_PT_BakePanel)
# bpy.utils.unregister_class(ArmVirtualInputPanel)
bpy.utils.unregister_class(ArmoryPlayButton)
bpy.utils.unregister_class(ArmoryStopButton)
bpy.utils.unregister_class(ArmoryBuildProjectButton)
bpy.utils.unregister_class(ArmoryOpenProjectFolderButton)
bpy.utils.unregister_class(ArmoryOpenEditorButton)
bpy.utils.unregister_class(CleanMenu)
bpy.utils.unregister_class(CleanButtonMenu)
bpy.utils.unregister_class(ArmoryCleanProjectButton)
bpy.utils.unregister_class(ArmoryPublishProjectButton)
bpy.utils.unregister_class(ArmGenLodButton)
bpy.utils.unregister_class(ARM_PT_LodPanel)
bpy.utils.unregister_class(ArmGenTerrainButton)
bpy.utils.unregister_class(ARM_PT_TerrainPanel)
bpy.utils.unregister_class(ARM_PT_TilesheetPanel)
bpy.utils.unregister_class(ARM_PT_ProxyPanel)
bpy.utils.unregister_class(ArmMakeProxyButton)
bpy.utils.unregister_class(ArmProxyToggleAllButton)
bpy.utils.unregister_class(ArmProxyApplyAllButton)
bpy.utils.unregister_class(ArmSyncProxyButton)
bpy.utils.unregister_class(ArmPrintTraitsButton)
bpy.utils.unregister_class(ARM_PT_MaterialNodePanel)