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"}

        # Compare version Blender and Armory (major, minor)
        if not arm.utils.compare_version_blender_arm():
            self.report({'INFO'}, 'For Armory to work correctly, you need Blender 2.83 LTS.')

        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):
        # Compare version Blender and Armory (major, minor)
        if not arm.utils.compare_version_blender_arm():
            self.report({'INFO'}, 'For Armory to work correctly, you need Blender 2.83 LTS.')

        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):
        # Compare version Blender and Armory (major, minor)
        if not arm.utils.compare_version_blender_arm():
            self.report({'INFO'}, 'For Armory to work correctly, you need Blender 2.83 LTS.')

        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)
