2025-01-22 16:18:30 +01:00
import json
import os
import sys
import shutil
import textwrap
import mathutils
import bpy
from bpy . props import *
from lnx . lightmapper . panels import scene
import lnx . api
import lnx . assets as assets
from lnx . exporter import LeenkxExporter
import lnx . log as log
import lnx . logicnode . replacement
import lnx . make as make
import lnx . make_state as state
import lnx . props as props
import lnx . props_properties
import lnx . props_traits
import lnx . nodes_logic
import lnx . ui_icons as ui_icons
import lnx . utils
import lnx . utils_vs
import lnx . write_probes
if lnx . is_reload ( __name__ ) :
lnx . api = lnx . reload_module ( lnx . api )
assets = lnx . reload_module ( assets )
lnx . exporter = lnx . reload_module ( lnx . exporter )
from lnx . exporter import LeenkxExporter
log = lnx . reload_module ( log )
lnx . logicnode . replacement = lnx . reload_module ( lnx . logicnode . replacement )
make = lnx . reload_module ( make )
state = lnx . reload_module ( state )
props = lnx . reload_module ( props )
lnx . props_properties = lnx . reload_module ( lnx . props_properties )
lnx . props_traits = lnx . reload_module ( lnx . props_traits )
lnx . nodes_logic = lnx . reload_module ( lnx . nodes_logic )
ui_icons = lnx . reload_module ( ui_icons )
lnx . utils = lnx . reload_module ( lnx . utils )
lnx . utils_vs = lnx . reload_module ( lnx . utils_vs )
lnx . write_probes = lnx . reload_module ( lnx . write_probes )
else :
lnx . enable_reload ( __name__ )
class LNX_PT_ObjectPropsPanel ( bpy . types . Panel ) :
""" Menu in object region. """
bl_label = " Leenkx 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
col = layout . column ( )
col . prop ( obj , ' lnx_export ' )
if not obj . lnx_export :
return
col . prop ( obj , ' lnx_spawn ' )
col . prop ( obj , ' lnx_mobile ' )
col . prop ( obj , ' lnx_animation_enabled ' )
col . prop ( obj , ' lnx_visible_shadow ' )
if obj . type == ' MESH ' :
layout . prop ( obj , ' lnx_instanced ' )
wrd = bpy . data . worlds [ ' Lnx ' ]
layout . prop_search ( obj , " lnx_tilesheet " , wrd , " lnx_tilesheetlist " , text = " Tilesheet " )
if obj . lnx_tilesheet != ' ' :
selected_ts = None
for ts in wrd . lnx_tilesheetlist :
if ts . name == obj . lnx_tilesheet :
selected_ts = ts
break
layout . prop_search ( obj , " lnx_tilesheet_action " , selected_ts , " lnx_tilesheetactionlist " , text = " Action " )
layout . prop ( obj , " lnx_use_custom_tilesheet_node " )
# Properties list
lnx . 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_use_default_channel " )
if not obj . TLM_ObjectProperties . tlm_use_default_channel :
row = layout . row ( )
row . prop_search ( obj . TLM_ObjectProperties , " tlm_uv_channel " , obj . data , " uv_layers " , text = ' UV Channel ' )
row = layout . row ( )
row . prop ( obj . TLM_ObjectProperties , " tlm_mesh_lightmap_resolution " )
if obj . TLM_ObjectProperties . tlm_use_default_channel :
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 == " AtlasGroupA " :
if scene . TLM_AtlasListItem > = 0 and len ( scene . TLM_AtlasList ) > 0 :
row = layout . row ( )
item = scene . TLM_AtlasList [ scene . TLM_AtlasListItem ]
row . prop_search ( obj . TLM_ObjectProperties , " tlm_atlas_pointer " , scene , " TLM_AtlasList " , text = ' Atlas Group ' )
row = layout . row ( )
else :
row = layout . label ( text = " Add Atlas Groups from the scene lightmapping settings. " )
row = layout . row ( )
else :
row = layout . row ( )
row . prop ( obj . TLM_ObjectProperties , " tlm_postpack_object " )
row = layout . row ( )
if obj . TLM_ObjectProperties . tlm_postpack_object and obj . TLM_ObjectProperties . tlm_mesh_lightmap_unwrap_mode != " AtlasGroupA " :
if scene . TLM_PostAtlasListItem > = 0 and len ( scene . TLM_PostAtlasList ) > 0 :
row = layout . row ( )
item = scene . TLM_PostAtlasList [ scene . TLM_PostAtlasListItem ]
row . prop_search ( obj . TLM_ObjectProperties , " tlm_postatlas_pointer " , scene , " TLM_PostAtlasList " , text = ' Atlas Group ' )
row = layout . row ( )
else :
row = layout . label ( text = " Add Atlas Groups from the scene lightmapping settings. " )
row = layout . row ( )
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 " )
#If UV Packer installed
if " UV-Packer " in bpy . context . preferences . addons . keys ( ) :
row . prop ( obj . TLM_ObjectProperties , " tlm_use_uv_packer " )
if obj . TLM_ObjectProperties . tlm_use_uv_packer :
row = layout . row ( align = True )
row . prop ( obj . TLM_ObjectProperties , " tlm_uv_packer_padding " )
row = layout . row ( align = True )
row . prop ( obj . TLM_ObjectProperties , " tlm_uv_packer_packing_engine " )
class LNX_PT_ModifiersPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 ( " lnx.invalidate_cache " )
class LNX_PT_ParticlesPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 , ' lnx_loop ' )
layout . prop ( obj . settings , ' lnx_count_mult ' )
class LNX_PT_PhysicsPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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
rb = obj . rigid_body
if rb is not None :
col = layout . column ( )
row = col . row ( )
row . alignment = ' RIGHT '
rb_type = ' Dynamic '
if LeenkxExporter . rigid_body_static ( rb ) :
rb_type = ' Static '
if rb . kinematic :
rb_type = ' Kinematic '
row . label ( text = ( f ' Rigid Body Export Type: { rb_type } ' ) , icon = ' AUTO ' )
layout . prop ( obj , ' lnx_rb_linear_factor ' )
layout . prop ( obj , ' lnx_rb_angular_factor ' )
layout . prop ( obj , ' lnx_rb_angular_friction ' )
layout . prop ( obj , ' lnx_rb_trigger ' )
layout . prop ( obj , ' lnx_rb_ccd ' )
if obj . soft_body is not None :
layout . prop ( obj , ' lnx_soft_body_margin ' )
if obj . rigid_body_constraint is not None :
layout . prop ( obj , ' lnx_relative_physics_constraint ' )
class LNX_OT_AddArmatureRootMotion ( bpy . types . Operator ) :
bl_idname = " lnx.add_root_motion_bone "
bl_label = " Add root bone "
bl_description = " Add a new bone and set it as the root bone. This may be used for root motion. "
@classmethod
def poll ( cls , context ) :
obj = context . object
if obj . mode == ' EDIT ' :
return False
armature = obj . data
if armature . bones :
if armature . bones . active :
return True
def execute ( self , context ) :
obj = context . object
current_mode = obj . mode
bpy . ops . object . mode_set ( mode = ' EDIT ' )
armature = obj . data
edit_bones = armature . edit_bones
current_root_pos = edit_bones . active . head
current_root_len = edit_bones . active . length
new_root = edit_bones . new ( " LeenkxRoot " )
new_root . head = current_root_pos
new_root . tail = new_root . head + mathutils . Vector ( ( 0.0 , 0.0 , current_root_len ) )
bpy . ops . object . mode_set ( mode = current_mode )
return { ' FINISHED ' }
# Menu in data region
class LNX_PT_DataPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 [ ' Lnx ' ]
if obj . type == ' CAMERA ' :
layout . prop ( obj . data , ' lnx_frustum_culling ' )
elif obj . type == ' MESH ' or obj . type == ' FONT ' or obj . type == ' META ' :
layout . prop ( obj . data , ' lnx_dynamic_usage ' )
layout . operator ( " lnx.invalidate_cache " )
elif obj . type == ' LIGHT ' :
layout . prop ( obj . data , ' lnx_clip_start ' )
layout . prop ( obj . data , ' lnx_clip_end ' )
layout . prop ( obj . data , ' lnx_fov ' )
layout . prop ( obj . data , ' lnx_shadows_bias ' )
layout . prop ( wrd , ' lnx_light_ies_texture ' )
layout . prop ( wrd , ' lnx_light_clouds_texture ' )
elif obj . type == ' SPEAKER ' :
layout . prop ( obj . data , ' lnx_play_on_start ' )
layout . prop ( obj . data , ' lnx_loop ' )
layout . prop ( obj . data , ' lnx_stream ' )
elif obj . type == ' ARMATURE ' :
layout . prop ( obj . data , ' lnx_autobake ' )
layout . prop ( obj . data , ' lnx_relative_bone_constraints ' )
if obj . data . bones :
if obj . data . bones . active :
layout . label ( text = ' Current Root: ' + obj . data . bones . active . name )
layout . operator ( ' lnx.add_root_motion_bone ' , text = ' Add new root bone ' , icon = ' ADD ' )
pass
class LNX_PT_WorldPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 , ' lnx_use_clouds ' )
col = layout . column ( align = True )
col . enabled = world . lnx_use_clouds
col . prop ( world , ' lnx_darken_clouds ' )
col . prop ( world , ' lnx_clouds_lower ' )
col . prop ( world , ' lnx_clouds_upper ' )
col . prop ( world , ' lnx_clouds_precipitation ' )
col . prop ( world , ' lnx_clouds_secondary ' )
col . prop ( world , ' lnx_clouds_wind ' )
col . prop ( world , ' lnx_clouds_steps ' )
class LNX_PT_ScenePropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 , ' lnx_export ' )
class InvalidateCacheButton ( bpy . types . Operator ) :
""" Delete cached mesh data """
bl_idname = " lnx.invalidate_cache "
bl_label = " Invalidate Cache "
def execute ( self , context ) :
context . object . data . lnx_cached = False
return { ' FINISHED ' }
class InvalidateMaterialCacheButton ( bpy . types . Operator ) :
""" Delete cached material data """
bl_idname = " lnx.invalidate_material_cache "
bl_label = " Invalidate Cache "
def execute ( self , context ) :
context . material . lnx_cached = False
context . material . signature = ' '
return { ' FINISHED ' }
class LNX_OT_NewCustomMaterial ( bpy . types . Operator ) :
bl_idname = " lnx.new_custom_material "
bl_label = " New Custom Material "
bl_description = " Add a new custom material. This will create all the necessary files and folders "
def poll_mat_name ( self , context ) :
project_dir = lnx . utils . get_fp ( )
shader_dir_dst = os . path . join ( project_dir , ' Shaders ' )
mat_name = lnx . utils . safestr ( self . mat_name )
self . mat_exists = os . path . isdir ( os . path . join ( project_dir , ' Bundled ' , mat_name ) )
vert_exists = os . path . isfile ( os . path . join ( shader_dir_dst , f ' { mat_name } .vert.glsl ' ) )
frag_exists = os . path . isfile ( os . path . join ( shader_dir_dst , f ' { mat_name } .frag.glsl ' ) )
self . shader_exists = vert_exists or frag_exists
mat_name : StringProperty (
name = ' Material Name ' , description = ' The name of the new material ' ,
default = ' MyCustomMaterial ' ,
update = poll_mat_name )
mode : EnumProperty (
name = ' Target RP ' , description = ' Choose for which render path mode the new material is created ' ,
default = ' deferred ' ,
items = [ ( ' deferred ' , ' Deferred ' , ' Create the material for a deferred render path ' ) ,
( ' forward ' , ' Forward ' , ' Create the material for a forward render path ' ) ] )
mat_exists : BoolProperty (
name = ' Material Already Exists ' ,
default = False ,
options = { ' HIDDEN ' , ' SKIP_SAVE ' } )
shader_exists : BoolProperty (
name = ' Shaders Already Exist ' ,
default = False ,
options = { ' HIDDEN ' , ' SKIP_SAVE ' } )
def invoke ( self , context , event ) :
if not bpy . data . is_saved :
self . report ( { ' INFO ' } , " Please save your file first " )
return { " CANCELLED " }
# Try to set deferred/forward based on the selected render path
try :
self . mode = ' forward ' if lnx . utils . get_rp ( ) . rp_renderer == ' Forward ' else ' deferred '
except IndexError :
# No render path, use default (deferred)
pass
self . poll_mat_name ( context )
wm = context . window_manager
return wm . invoke_props_dialog ( self , width = 300 )
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
layout . prop ( self , ' mat_name ' )
layout . prop ( self , ' mode ' , expand = True )
if self . mat_exists :
box = layout . box ( )
box . alert = True
col = box . column ( align = True )
col . label ( text = ' A custom material with that name already exists, ' , icon = ' ERROR ' )
col . label ( text = ' clicking on \' OK \' will override the material! ' , icon = ' BLANK1 ' )
if self . shader_exists :
box = layout . box ( )
box . alert = True
col = box . column ( align = True )
col . label ( text = ' Shader file(s) with that name already exists, ' , icon = ' ERROR ' )
col . label ( text = ' clicking on \' OK \' will override the shader(s)! ' , icon = ' BLANK1 ' )
def execute ( self , context ) :
if self . mat_name == ' ' :
return { ' CANCELLED ' }
project_dir = lnx . utils . get_fp ( )
shader_dir_src = os . path . join ( lnx . utils . get_sdk_path ( ) , ' leenkx ' , ' Shaders ' , ' custom_mat_presets ' )
shader_dir_dst = os . path . join ( project_dir , ' Shaders ' )
mat_name = lnx . utils . safestr ( self . mat_name )
mat_dir = os . path . join ( project_dir , ' Bundled ' , mat_name )
os . makedirs ( mat_dir , exist_ok = True )
os . makedirs ( shader_dir_dst , exist_ok = True )
# Shader data
if self . mode == ' forward ' :
col_attachments = [ ' RGBA64 ' ]
constants = [ { ' link ' : ' _worldViewProjectionMatrix ' , ' name ' : ' WVP ' , ' type ' : ' mat4 ' } ]
vertex_elems = [ { ' name ' : ' pos ' , ' data ' : ' short4norm ' } ]
else :
col_attachments = [ ' RGBA64 ' , ' RGBA64 ' ]
constants = [
{ ' link ' : ' _worldViewProjectionMatrix ' , ' name ' : ' WVP ' , ' type ' : ' mat4 ' } ,
{ ' link ' : ' _normalMatrix ' , ' name ' : ' N ' , ' type ' : ' mat3 ' }
]
vertex_elems = [
{ ' name ' : ' pos ' , ' data ' : ' short4norm ' } ,
{ ' name ' : ' nor ' , ' data ' : ' short2norm ' }
]
con = {
' color_attachments ' : col_attachments ,
' compare_mode ' : ' less ' ,
' constants ' : constants ,
' cull_mode ' : ' clockwise ' ,
' depth_write ' : True ,
' fragment_shader ' : f ' { mat_name } .frag ' ,
' name ' : ' mesh ' ,
' texture_units ' : [ ] ,
' vertex_shader ' : f ' { mat_name } .vert ' ,
' vertex_elements ' : vertex_elems
}
data = {
' shader_datas ' : [ {
' contexts ' : [ con ] ,
' name ' : f ' { mat_name } '
} ]
}
# Save shader data file
with open ( os . path . join ( mat_dir , f ' { mat_name } .json ' ) , ' w ' ) as datafile :
json . dump ( data , datafile , indent = 4 , sort_keys = True )
# Copy preset shaders to project
if self . mode == ' forward ' :
shutil . copy ( os . path . join ( shader_dir_src , ' custom_mat_forward.frag.glsl ' ) , os . path . join ( shader_dir_dst , f ' { mat_name } .frag.glsl ' ) )
shutil . copy ( os . path . join ( shader_dir_src , ' custom_mat_forward.vert.glsl ' ) , os . path . join ( shader_dir_dst , f ' { mat_name } .vert.glsl ' ) )
else :
shutil . copy ( os . path . join ( shader_dir_src , ' custom_mat_deferred.frag.glsl ' ) , os . path . join ( shader_dir_dst , f ' { mat_name } .frag.glsl ' ) )
shutil . copy ( os . path . join ( shader_dir_src , ' custom_mat_deferred.vert.glsl ' ) , os . path . join ( shader_dir_dst , f ' { mat_name } .vert.glsl ' ) )
# True if called from the material properties tab, else it was called from the search menu
if hasattr ( context , ' material ' ) and context . material is not None :
context . material . lnx_custom_material = mat_name
return { ' FINISHED ' }
class LNX_PG_BindTexturesListItem ( bpy . types . PropertyGroup ) :
uniform_name : StringProperty (
name = ' Uniform Name ' ,
description = ' The name of the sampler uniform as used in the shader ' ,
default = ' ImageTexture ' ,
)
image : PointerProperty (
name = ' Image ' ,
type = bpy . types . Image ,
description = ' The image to attach to the texture unit ' ,
)
class LNX_UL_BindTexturesList ( bpy . types . UIList ) :
def draw_item ( self , context , layout , data , item : LNX_PG_BindTexturesListItem , icon , active_data , active_propname , index ) :
row = layout . row ( align = True )
if item . image is not None :
row . label ( text = item . uniform_name , icon_value = item . image . preview . icon_id )
else :
row . label ( text = ' <empty> ' , icon = ' ERROR ' )
class LNX_OT_BindTexturesListNewItem ( bpy . types . Operator ) :
bl_idname = " lnx_bind_textures_list.new_item "
bl_label = " Add Texture Binding "
bl_description = " Add a new texture binding to the list "
bl_options = { ' INTERNAL ' }
@classmethod
def poll ( cls , context ) :
mat = context . material
if mat is None :
return False
return True
def execute ( self , context ) :
mat = context . material
mat . lnx_bind_textures_list . add ( )
mat . lnx_bind_textures_list_index = len ( mat . lnx_bind_textures_list ) - 1
return { ' FINISHED ' }
class LNX_OT_BindTexturesListDeleteItem ( bpy . types . Operator ) :
bl_idname = " lnx_bind_textures_list.delete_item "
bl_label = " Remove Texture Binding "
bl_description = " Delete the selected texture binding from the list "
bl_options = { ' INTERNAL ' }
@classmethod
def poll ( cls , context ) :
mat = context . material
if mat is None :
return False
return len ( mat . lnx_bind_textures_list ) > 0
def execute ( self , context ) :
mat = context . material
lst = mat . lnx_bind_textures_list
index = mat . lnx_bind_textures_list_index
if len ( lst ) < = index :
return { ' FINISHED ' }
lst . remove ( index )
if index > 0 :
index = index - 1
mat . lnx_bind_textures_list_index = index
return { ' FINISHED ' }
class LNX_PT_MaterialPropsPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 is None :
return
layout . prop ( mat , ' lnx_cast_shadow ' )
columnb = layout . column ( )
wrd = bpy . data . worlds [ ' Lnx ' ]
columnb . enabled = len ( wrd . lnx_rplist ) > 0 and lnx . utils . get_rp ( ) . rp_renderer == ' Forward '
columnb . prop ( mat , ' lnx_receive_shadow ' )
layout . prop ( mat , ' lnx_ignore_irradiance ' )
layout . prop ( mat , ' lnx_two_sided ' )
columnb = layout . column ( )
columnb . enabled = not mat . lnx_two_sided
columnb . prop ( mat , ' lnx_cull_mode ' )
layout . prop ( mat , ' lnx_material_id ' )
layout . prop ( mat , ' lnx_depth_read ' )
layout . prop ( mat , ' lnx_overlay ' )
layout . prop ( mat , ' lnx_decal ' )
layout . prop ( mat , ' lnx_discard ' )
columnb = layout . column ( )
columnb . enabled = mat . lnx_discard
columnb . prop ( mat , ' lnx_discard_opacity ' )
columnb . prop ( mat , ' lnx_discard_opacity_shadows ' )
row = layout . row ( align = True )
row . prop ( mat , ' lnx_custom_material ' )
row . operator ( ' lnx.new_custom_material ' , text = ' ' , icon = ' ADD ' )
layout . prop ( mat , ' lnx_skip_context ' )
layout . prop ( mat , ' lnx_particle_fade ' )
layout . prop ( mat , ' lnx_billboard ' )
layout . operator ( " lnx.invalidate_material_cache " )
class LNX_PT_BindTexturesPropsPanel ( bpy . types . Panel ) :
bl_label = " Bind Textures "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " material "
bl_options = { ' DEFAULT_CLOSED ' }
bl_parent_id = " LNX_PT_MaterialPropsPanel "
@classmethod
def poll ( cls , context ) :
mat = context . material
if mat is None :
return False
return mat . lnx_custom_material != ' '
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
mat = bpy . context . material
if mat is None :
return
row = layout . row ( align = True )
col = row . column ( align = True )
col . template_list ( ' LNX_UL_BindTexturesList ' , ' ' , mat , ' lnx_bind_textures_list ' , mat , ' lnx_bind_textures_list_index ' )
if mat . lnx_bind_textures_list_index > = 0 and len ( mat . lnx_bind_textures_list ) > 0 :
item = mat . lnx_bind_textures_list [ mat . lnx_bind_textures_list_index ]
box = col . box ( )
if item . image is None :
_row = box . row ( )
_row . alert = True
_row . alignment = ' RIGHT '
_row . label ( text = " No image selected, skipping export " )
box . prop ( item , ' uniform_name ' )
box . prop ( item , ' image ' )
col = row . column ( align = True )
col . operator ( " lnx_bind_textures_list.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_bind_textures_list.delete_item " , icon = ' REMOVE ' , text = " " )
class LNX_PT_MaterialDriverPropsPanel ( bpy . types . Panel ) :
""" Per-material properties for custom render path drivers """
bl_label = " Leenkx Driver Properties "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " material "
@classmethod
def poll ( cls , context ) :
mat = context . material
if mat is None :
return False
wrd = bpy . data . worlds [ ' Lnx ' ]
if wrd . lnx_rplist_index < 0 or len ( wrd . lnx_rplist ) == 0 :
return False
if len ( lnx . api . drivers ) == 0 :
return False
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
return rpdat . rp_driver != ' Leenkx ' and lnx . api . drivers [ rpdat . rp_driver ] [ ' draw_mat_props ' ] is not None
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
lnx . api . drivers [ rpdat . rp_driver ] [ ' draw_mat_props ' ] ( layout , context . material )
class LNX_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 = " LNX_PT_MaterialPropsPanel "
def draw_header ( self , context ) :
if context . material is None :
return
self . layout . prop ( context . material , ' lnx_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 is None :
return
flow = layout . grid_flow ( )
flow . enabled = mat . lnx_blending
col = flow . column ( align = True )
col . prop ( mat , ' lnx_blending_source ' )
col . prop ( mat , ' lnx_blending_destination ' )
col . prop ( mat , ' lnx_blending_operation ' )
flow . separator ( )
col = flow . column ( align = True )
col . prop ( mat , ' lnx_blending_source_alpha ' )
col . prop ( mat , ' lnx_blending_destination_alpha ' )
col . prop ( mat , ' lnx_blending_operation_alpha ' )
class LNX_PT_LeenkxPlayerPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 [ ' Lnx ' ]
row = layout . row ( align = True )
row . alignment = ' EXPAND '
row . scale_y = 1.3
if state . proc_play is None and state . proc_build is None :
row . operator ( " lnx.play " , icon = " PLAY " )
else :
if bpy . app . version < ( 3 , 0 , 0 ) :
row . operator ( " lnx.stop " , icon = " CANCEL " , text = " " )
2025-01-30 13:21:20 +00:00
elif bpy . app . version > ( 3 , 0 , 0 ) and bpy . app . version < ( 4 , 3 , 2 ) :
2025-01-22 16:18:30 +01:00
row . operator ( " lnx.stop " , icon = " SEQUENCE_COLOR_01 " , text = " " )
2025-01-30 13:21:20 +00:00
else :
row . operator ( " lnx.stop " , icon = " EVENT_MEDIASTOP " , text = " " )
2025-01-22 16:18:30 +01:00
row . operator ( " lnx.clean_menu " , icon = " BRUSH_DATA " )
col = layout . box ( ) . column ( )
col . prop ( wrd , ' lnx_runtime ' )
col . prop ( wrd , ' lnx_play_camera ' )
col . prop ( wrd , ' lnx_play_scene ' )
col . prop_search ( wrd , ' lnx_play_renderpath ' , wrd , ' lnx_rplist ' , text = ' Render Path ' )
if log . num_warnings > 0 :
box = layout . box ( )
box . alert = True
col = box . column ( align = True )
warnings = ' warnings ' if log . num_warnings > 1 else ' warning '
col . label ( text = f ' { log . num_warnings } { warnings } occurred during compilation! ' , icon = ' ERROR ' )
# Blank icon to achieve the same indentation as the line before
# prevent showing "open console" twice:
if log . num_errors == 0 :
col . label ( text = ' Please open the console to get more information. ' , icon = ' BLANK1 ' )
if log . num_errors > 0 :
box = layout . box ( )
box . alert = True
# Less spacing between lines
col = box . column ( align = True )
errors = ' errors ' if log . num_errors > 1 else ' error '
col . label ( text = f ' { log . num_errors } { errors } occurred during compilation! ' , icon = ' CANCEL ' )
# 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 LNX_PT_LeenkxExporterPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 [ ' Lnx ' ]
row = layout . row ( align = True )
row . alignment = ' EXPAND '
row . scale_y = 1.3
row . operator ( " lnx.build_project " , icon = " MOD_BUILD " )
# row.operator("lnx.patch_project")
row . operator ( " lnx.publish_project " , icon = " EXPORT " )
rows = 2
if len ( wrd . lnx_exporterlist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_ExporterList " , " The_List " , wrd , " lnx_exporterlist " , wrd , " lnx_exporterlist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_exporterlist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_exporterlist.delete_item " , icon = ' REMOVE ' , text = " " )
col . menu ( " LNX_MT_ExporterListSpecials " , icon = ' DOWNARROW_HLT ' , text = " " )
if len ( wrd . lnx_exporterlist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_exporterlist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_exporterlist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if wrd . lnx_exporterlist_index > = 0 and len ( wrd . lnx_exporterlist ) > 0 :
item = wrd . lnx_exporterlist [ wrd . lnx_exporterlist_index ]
box = layout . box ( ) . column ( )
box . prop ( item , ' lnx_project_target ' )
box . prop ( item , ' lnx_project_khamake ' )
box . prop ( item , lnx . utils . target_to_gapi ( item . lnx_project_target ) )
box . prop_search ( item , " lnx_project_rp " , wrd , " lnx_rplist " , text = " Render Path " )
box . prop_search ( item , ' lnx_project_scene ' , bpy . data , ' scenes ' , text = ' Scene ' )
layout . separator ( )
col = layout . column ( align = True )
col . prop ( wrd , ' lnx_project_name ' )
col . prop ( wrd , ' lnx_project_package ' )
col . prop ( wrd , ' lnx_project_bundle ' )
col = layout . column ( align = True )
col . prop ( wrd , ' lnx_project_version ' )
col . prop ( wrd , ' lnx_project_version_autoinc ' )
col = layout . column ( )
col . prop ( wrd , ' lnx_project_icon ' )
col = layout . column ( heading = ' Code Output ' , align = True )
col . prop ( wrd , ' lnx_dce ' )
col . prop ( wrd , ' lnx_compiler_inline ' )
col . prop ( wrd , ' lnx_minify_js ' )
col . prop ( wrd , ' lnx_no_traces ' )
col = layout . column ( heading = ' Data ' , align = True )
col . prop ( wrd , ' lnx_minimize ' )
col . prop ( wrd , ' lnx_optimize_data ' )
col . prop ( wrd , ' lnx_asset_compression ' )
col . prop ( wrd , ' lnx_single_data_file ' )
class ExporterTargetSettingsMixin :
""" Mixin for common exporter setting subpanel functionality.
Panels that inherit from this mixin need to have a lnx_target
variable for polling . """
bl_space_type = ' PROPERTIES '
bl_region_type = ' WINDOW '
bl_context = ' render '
bl_parent_id = ' LNX_PT_LeenkxExporterPanel '
# Override this in sub classes
lnx_panel = ' '
@classmethod
def poll ( cls , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
if ( len ( wrd . lnx_exporterlist ) > 0 ) and ( wrd . lnx_exporterlist_index > = 0 ) :
item = wrd . lnx_exporterlist [ wrd . lnx_exporterlist_index ]
return item . lnx_project_target == cls . lnx_target
return False
def draw_header ( self , context ) :
self . layout . label ( text = ' ' , icon = ' SETTINGS ' )
class LNX_PT_LeenkxExporterAndroidSettingsPanel ( ExporterTargetSettingsMixin , bpy . types . Panel ) :
bl_label = " Android Settings "
lnx_target = ' android-hl ' # See ExporterTargetSettingsMixin
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
col = layout . column ( )
col . prop ( wrd , ' lnx_winorient ' )
col . prop ( wrd , ' lnx_project_android_sdk_min ' )
col . prop ( wrd , ' lnx_project_android_sdk_target ' )
col . prop ( wrd , ' lnx_project_android_sdk_compile ' )
class LNX_PT_LeenkxExporterAndroidPermissionsPanel ( bpy . types . Panel ) :
bl_label = " Permissions "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " render "
bl_options = { ' DEFAULT_CLOSED ' }
bl_parent_id = " LNX_PT_LeenkxExporterAndroidSettingsPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
# Permission
row = layout . row ( )
rows = 2
if len ( wrd . lnx_exporter_android_permission_list ) > 1 :
rows = 4
row . template_list ( " LNX_UL_Exporter_AndroidPermissionList " , " The_List " , wrd , " lnx_exporter_android_permission_list " , wrd , " lnx_exporter_android_permission_list_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_exporter_android_permission_list.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_exporter_android_permission_list.delete_item " , icon = ' REMOVE ' , text = " " )
row = layout . row ( )
if wrd . lnx_exporter_android_permission_list_index > = 0 and len ( wrd . lnx_exporter_android_permission_list ) > 0 :
item = wrd . lnx_exporter_android_permission_list [ wrd . lnx_exporter_android_permission_list_index ]
row = layout . row ( )
row . prop ( item , ' lnx_android_permissions ' )
class LNX_PT_LeenkxExporterAndroidAbiPanel ( 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 = " LNX_PT_LeenkxExporterAndroidSettingsPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
# ABIs
row = layout . row ( )
rows = 2
if len ( wrd . lnx_exporter_android_abi_list ) > 1 :
rows = 4
row . template_list ( " LNX_UL_Exporter_AndroidAbiList " , " The_List " , wrd , " lnx_exporter_android_abi_list " , wrd , " lnx_exporter_android_abi_list_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_exporter_android_abi_list.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_exporter_android_abi_list.delete_item " , icon = ' REMOVE ' , text = " " )
row = layout . row ( )
if wrd . lnx_exporter_android_abi_list_index > = 0 and len ( wrd . lnx_exporter_android_abi_list ) > 0 :
item = wrd . lnx_exporter_android_abi_list [ wrd . lnx_exporter_android_abi_list_index ]
row = layout . row ( )
row . prop ( item , ' lnx_android_abi ' )
class LNX_PT_LeenkxExporterAndroidBuildAPKPanel ( 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 = " LNX_PT_LeenkxExporterAndroidSettingsPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
path = lnx . utils . get_android_sdk_root_path ( )
col = layout . column ( )
row = col . row ( )
row . enabled = len ( path ) > 0
row . prop ( wrd , ' lnx_project_android_build_apk ' )
row = col . row ( )
row . enabled = wrd . lnx_project_android_build_apk
row . prop ( wrd , ' lnx_project_android_rename_apk ' )
row = col . row ( )
row . enabled = wrd . lnx_project_android_build_apk and len ( lnx . utils . get_android_apk_copy_path ( ) ) > 0
row . prop ( wrd , ' lnx_project_android_copy_apk ' )
row = col . row ( align = True )
row . prop ( wrd , ' lnx_project_android_list_avd ' )
sub = row . column ( align = True )
sub . enabled = len ( path ) > 0
sub . operator ( ' lnx.update_list_android_emulator ' , text = ' ' , icon = ' FILE_REFRESH ' )
sub = row . column ( align = True )
sub . enabled = len ( path ) > 0 and len ( lnx . utils . get_android_emulator_name ( ) ) > 0
sub . operator ( ' lnx.run_android_emulator ' , text = ' ' , icon = ' PLAY ' )
row = col . row ( )
row . enabled = lnx . utils . get_project_android_build_apk ( ) and len ( lnx . utils . get_android_emulator_name ( ) ) > 0
row . prop ( wrd , ' lnx_project_android_run_avd ' )
class LNX_PT_LeenkxExporterHTML5SettingsPanel ( ExporterTargetSettingsMixin , bpy . types . Panel ) :
bl_label = " HTML5 Settings "
lnx_target = ' html5 ' # See ExporterTargetSettingsMixin
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
col = layout . column ( )
col . prop ( wrd , ' lnx_project_html5_popupmenu_in_browser ' )
row = col . row ( )
row . enabled = len ( lnx . utils . get_html5_copy_path ( ) ) > 0
row . prop ( wrd , ' lnx_project_html5_copy ' )
row = col . row ( )
row . enabled = len ( lnx . utils . get_html5_copy_path ( ) ) > 0 and wrd . lnx_project_html5_copy and len ( lnx . utils . get_link_web_server ( ) ) > 0
row . prop ( wrd , ' lnx_project_html5_start_browser ' )
class LNX_PT_LeenkxExporterWindowsSettingsPanel ( ExporterTargetSettingsMixin , bpy . types . Panel ) :
bl_label = " Windows Settings "
lnx_target = ' windows-hl ' # See ExporterTargetSettingsMixin
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
is_windows = lnx . utils . get_os_is_windows ( )
col = layout . column ( )
col . prop ( wrd , ' lnx_project_win_list_vs ' )
row = col . row ( )
row . enabled = is_windows
row . prop ( wrd , ' lnx_project_win_build ' , text = ' After Publish ' )
layout = layout . column ( )
layout . enabled = is_windows
if is_windows and wrd . lnx_project_win_build != ' nothing ' and not lnx . utils_vs . is_version_installed ( wrd . lnx_project_win_list_vs ) :
box = draw_error_box (
layout ,
' The selected version of Visual Studio could not be found and '
' may not be installed. The " After Publish " action may not work '
' as intended. '
)
box . operator ( ' lnx.update_list_installed_vs ' , icon = ' FILE_REFRESH ' )
layout . separator ( )
col = layout . column ( )
col . enabled = wrd . lnx_project_win_build . startswith ( ' compile ' )
col . prop ( wrd , ' lnx_project_win_build_mode ' )
col . prop ( wrd , ' lnx_project_win_build_arch ' )
col . prop ( wrd , ' lnx_project_win_build_log ' )
col . prop ( wrd , ' lnx_project_win_build_cpu ' )
col . prop ( wrd , ' lnx_project_win_build_open ' )
class LNX_PT_LeenkxProjectPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 ( " lnx.open_editor " , icon = " DESKTOP " )
row . operator ( " lnx.open_project_folder " , icon = " FILE_FOLDER " )
class LNX_PT_ProjectFlagsPanel ( bpy . types . Panel ) :
bl_label = " Flags "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " render "
bl_parent_id = " LNX_PT_LeenkxProjectPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
col = layout . column ( heading = ' Debug ' , align = True )
col . prop ( wrd , ' lnx_verbose_output ' )
col . prop ( wrd , ' lnx_cache_build ' )
col . prop ( wrd , ' lnx_clear_on_compile ' )
col . prop ( wrd , ' lnx_assert_level ' )
col . prop ( wrd , ' lnx_assert_quit ' )
col = layout . column ( heading = ' Runtime ' , align = True )
col . prop ( wrd , ' lnx_live_patch ' )
col . prop ( wrd , ' lnx_stream_scene ' )
col . prop ( wrd , ' lnx_loadscreen ' )
col . prop ( wrd , ' lnx_write_config ' )
col = layout . column ( heading = ' Renderer ' , align = True )
col . prop ( wrd , ' lnx_batch_meshes ' )
col . prop ( wrd , ' lnx_batch_materials ' )
col . prop ( wrd , ' lnx_deinterleaved_buffers ' )
col . prop ( wrd , ' lnx_export_tangents ' )
col = layout . column ( heading = ' Quality ' )
row = col . row ( ) # To expand below property UI horizontally
row . prop ( wrd , ' lnx_canvas_img_scaling_quality ' , expand = True )
col . prop ( wrd , ' lnx_texture_quality ' )
col . prop ( wrd , ' lnx_sound_quality ' )
col = layout . column ( heading = ' External Assets ' )
col . prop ( wrd , ' lnx_copy_override ' )
col . operator ( ' lnx.copy_to_bundled ' , icon = ' IMAGE_DATA ' )
class LNX_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 = " LNX_PT_ProjectFlagsPanel "
def draw_header ( self , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
self . layout . prop ( wrd , ' lnx_debug_console ' , text = ' ' )
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
col = layout . column ( )
col . enabled = wrd . lnx_debug_console
col . prop ( wrd , ' lnx_debug_console_position ' )
col . prop ( wrd , ' lnx_debug_console_scale ' )
col . prop ( wrd , ' lnx_debug_console_visible ' )
col . prop ( wrd , ' lnx_debug_console_trace_pos ' )
class LNX_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 = " LNX_PT_LeenkxProjectPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
layout . prop ( wrd , ' lnx_winmode ' )
col = layout . column ( align = True )
col . prop ( wrd , ' lnx_winresize ' )
sub = col . column ( )
sub . enabled = wrd . lnx_winresize
sub . prop ( wrd , ' lnx_winmaximize ' )
col . enabled = True
col . prop ( wrd , ' lnx_winminimize ' )
layout . prop ( wrd , ' lnx_vsync ' )
class LNX_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 = " LNX_PT_LeenkxProjectPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
layout . prop ( wrd , ' lnx_audio ' )
layout . prop ( wrd , ' lnx_physics ' )
if wrd . lnx_physics != ' Disabled ' :
layout . prop ( wrd , ' lnx_physics_engine ' )
layout . prop ( wrd , ' lnx_navigation ' )
if wrd . lnx_navigation != ' Disabled ' :
layout . prop ( wrd , ' lnx_navigation_engine ' )
layout . prop ( wrd , ' lnx_ui ' )
layout . prop ( wrd , ' lnx_network ' )
layout . prop_search ( wrd , ' lnx_khafile ' , bpy . data , ' texts ' )
layout . prop ( wrd , ' lnx_project_root ' )
class LnxVirtualInputPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 LeenkxPlayButton ( bpy . types . Operator ) :
''' Launch player in new window '''
bl_idname = ' lnx.play '
bl_label = ' Play '
def invoke ( self , context , event ) :
if event . shift :
state . is_play = True
make . build_success ( )
return { ' FINISHED ' }
return self . execute ( context )
def execute ( self , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
if state . proc_build is not None :
return { " CANCELLED " }
lnx . utils . check_blender_version ( self )
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
if not lnx . utils . check_sdkpath ( self ) :
return { " CANCELLED " }
lnx . utils . check_projectpath ( None )
lnx . utils . check_default_props ( )
assets . invalidate_enabled = False
if wrd . lnx_clear_on_compile :
os . system ( " cls " )
make . play ( )
assets . invalidate_enabled = True
return { ' FINISHED ' }
class LeenkxStopButton ( bpy . types . Operator ) :
''' Stop currently running player '''
bl_idname = ' lnx.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
lnx . write_probes . check_last_cmft_time ( )
return { ' FINISHED ' }
class LeenkxBuildProjectButton ( bpy . types . Operator ) :
""" Build and compile project """
bl_idname = ' lnx.build_project '
bl_label = ' Build '
@classmethod
def poll ( cls , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
return wrd . lnx_exporterlist_index > = 0 and len ( wrd . lnx_exporterlist ) > 0
def execute ( self , context ) :
lnx . utils . check_blender_version ( self )
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
if not lnx . utils . check_sdkpath ( self ) :
return { " CANCELLED " }
lnx . utils . check_projectpath ( self )
lnx . utils . check_default_props ( )
wrd = bpy . data . worlds [ ' Lnx ' ]
item = wrd . lnx_exporterlist [ wrd . lnx_exporterlist_index ]
if item . lnx_project_rp == ' ' :
item . lnx_project_rp = wrd . lnx_rplist [ wrd . lnx_rplist_index ] . name
if item . lnx_project_scene == None :
item . lnx_project_scene = context . scene
# Assume unique rp names
rplist_index = wrd . lnx_rplist_index
for i in range ( 0 , len ( wrd . lnx_rplist ) ) :
if wrd . lnx_rplist [ i ] . name == item . lnx_project_rp :
wrd . lnx_rplist_index = i
break
assets . invalidate_shader_cache ( None , None )
assets . invalidate_enabled = False
if wrd . lnx_clear_on_compile :
os . system ( " cls " )
make . build ( item . lnx_project_target , is_export = True )
make . compile ( )
wrd . lnx_rplist_index = rplist_index
assets . invalidate_enabled = True
return { ' FINISHED ' }
class LeenkxPublishProjectButton ( bpy . types . Operator ) :
""" Build project ready for publishing. """
bl_idname = ' lnx.publish_project '
bl_label = ' Publish '
@classmethod
def poll ( cls , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
return wrd . lnx_exporterlist_index > = 0 and len ( wrd . lnx_exporterlist ) > 0
def execute ( self , context ) :
lnx . utils . check_blender_version ( self )
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
if not lnx . utils . check_sdkpath ( self ) :
return { " CANCELLED " }
self . report ( { ' INFO ' } , ' Publishing project, check console for details. ' )
lnx . utils . check_projectpath ( self )
lnx . utils . check_default_props ( )
wrd = bpy . data . worlds [ ' Lnx ' ]
item = wrd . lnx_exporterlist [ wrd . lnx_exporterlist_index ]
if item . lnx_project_rp == ' ' :
item . lnx_project_rp = wrd . lnx_rplist [ wrd . lnx_rplist_index ] . name
if item . lnx_project_scene == None :
item . lnx_project_scene = context . scene
# Assume unique rp names
rplist_index = wrd . lnx_rplist_index
for i in range ( 0 , len ( wrd . lnx_rplist ) ) :
if wrd . lnx_rplist [ i ] . name == item . lnx_project_rp :
wrd . lnx_rplist_index = i
break
make . clean ( )
assets . invalidate_enabled = False
if wrd . lnx_clear_on_compile :
os . system ( " cls " )
make . build ( item . lnx_project_target , is_publish = True , is_export = True )
make . compile ( )
wrd . lnx_rplist_index = rplist_index
assets . invalidate_enabled = True
return { ' FINISHED ' }
class LeenkxOpenProjectFolderButton ( bpy . types . Operator ) :
''' Open project folder '''
bl_idname = ' lnx.open_project_folder '
bl_label = ' Project Folder '
def execute ( self , context ) :
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
lnx . utils . open_folder ( lnx . utils . get_fp ( ) )
return { ' FINISHED ' }
class LeenkxOpenEditorButton ( bpy . types . Operator ) :
''' Launch this project in the IDE '''
bl_idname = ' lnx.open_editor '
bl_label = ' Code Editor '
bl_description = ' Open Project in IDE '
def execute ( self , context ) :
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
lnx . utils . check_default_props ( )
if not os . path . exists ( lnx . utils . get_fp ( ) + " /khafile.js " ) :
print ( ' Generating Krom project for IDE build configuration ' )
make . build ( ' krom ' )
lnx . 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 ( " lnx.clean_project " )
class CleanButtonMenu ( bpy . types . Operator ) :
''' Clean cached data '''
bl_label = " Clean "
bl_idname = " lnx.clean_menu "
def execute ( self , context ) :
bpy . ops . wm . call_menu ( name = CleanMenu . bl_idname )
return { " FINISHED " }
class LeenkxCleanProjectButton ( bpy . types . Operator ) :
''' Delete all cached project data '''
bl_idname = ' lnx.clean_project '
bl_label = ' Clean Project '
def execute ( self , context ) :
if not lnx . 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 ( ' lnx.copy_traits_to_active ' )
class LNX_PT_TopbarPanel ( bpy . types . Panel ) :
bl_label = " Leenkx Player "
bl_space_type = " VIEW_3D "
bl_region_type = " WINDOW "
bl_options = { ' INSTANCED ' }
def draw_header ( self , context ) :
row = self . layout . row ( align = True )
if state . proc_play is None and state . proc_build is None :
row . operator ( " lnx.play " , icon = " PLAY " , text = " " )
else :
if bpy . app . version < ( 3 , 0 , 0 ) :
row . operator ( " lnx.stop " , icon = " CANCEL " , text = " " )
2025-01-30 13:21:20 +00:00
elif bpy . app . version > ( 3 , 0 , 0 ) and bpy . app . version < ( 4 , 3 , 2 ) :
2025-01-22 16:18:30 +01:00
row . operator ( " lnx.stop " , icon = " SEQUENCE_COLOR_01 " , text = " " )
2025-01-30 13:21:20 +00:00
else :
row . operator ( " lnx.stop " , icon = " EVENT_MEDIASTOP " , text = " " )
2025-01-22 16:18:30 +01:00
row . operator ( " lnx.clean_menu " , icon = " BRUSH_DATA " , text = " " )
row . operator ( " lnx.open_editor " , icon = " DESKTOP " , text = " " )
row . operator ( " lnx.open_project_folder " , icon = " FILE_FOLDER " , text = " " )
def draw ( self , context ) :
col = self . layout . column ( )
wrd = bpy . data . worlds [ ' Lnx ' ]
col . label ( text = " Leenkx Launch " )
col . separator ( )
col . prop ( wrd , ' lnx_runtime ' )
col . prop ( wrd , ' lnx_play_camera ' )
col . prop ( wrd , ' lnx_play_scene ' )
col . prop_search ( wrd , ' lnx_play_renderpath ' , wrd , ' lnx_rplist ' , text = ' Render Path ' )
col . prop ( wrd , ' lnx_debug_console ' , text = " Debug Console " )
def draw_space_topbar ( self , context ) :
# for some blender reasons, topbar is instanced twice. this avoids doubling the panel
if context . region . alignment == ' RIGHT ' :
self . layout . popover ( panel = " LNX_PT_TopbarPanel " , text = " " )
class LNX_PT_RenderPathPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 [ ' Lnx ' ]
rows = 2
if len ( wrd . lnx_rplist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_RPList " , " The_List " , wrd , " lnx_rplist " , wrd , " lnx_rplist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_rplist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_rplist.delete_item " , icon = ' REMOVE ' , text = " " )
if len ( wrd . lnx_rplist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_rplist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_rplist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if wrd . lnx_rplist_index < 0 or len ( wrd . lnx_rplist ) == 0 :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
if len ( lnx . api . drivers ) > 0 :
layout . prop_search ( rpdat , " rp_driver " , wrd , " rp_driver_list " , text = " Driver " )
layout . separator ( )
if rpdat . rp_driver != ' Leenkx ' and lnx . api . drivers [ rpdat . rp_driver ] [ ' draw_props ' ] != None :
lnx . api . drivers [ rpdat . rp_driver ] [ ' draw_props ' ] ( layout )
return
class LNX_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 = " LNX_PT_RenderPathPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . prop ( rpdat , ' rp_renderer ' )
if rpdat . rp_renderer == ' Forward ' :
layout . prop ( rpdat , ' rp_depthprepass ' )
layout . prop ( rpdat , ' lnx_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_depth_texture_state ' )
layout . prop ( rpdat , ' rp_draw_order ' )
layout . prop ( rpdat , ' lnx_samples_per_pixel ' )
layout . prop ( rpdat , ' lnx_texture_filter ' )
layout . prop ( rpdat , ' rp_sss_state ' )
col = layout . column ( )
col . enabled = rpdat . rp_sss_state != ' Off '
col . prop ( rpdat , ' lnx_sss_width ' )
layout . prop ( rpdat , ' lnx_rp_displacement ' )
if rpdat . lnx_rp_displacement == ' Tessellation ' :
layout . label ( text = ' Mesh ' )
layout . prop ( rpdat , ' lnx_tess_mesh_inner ' )
layout . prop ( rpdat , ' lnx_tess_mesh_outer ' )
layout . label ( text = ' Shadow ' )
layout . prop ( rpdat , ' lnx_tess_shadows_inner ' )
layout . prop ( rpdat , ' lnx_tess_shadows_outer ' )
layout . prop ( rpdat , ' lnx_particles ' )
layout . separator ( factor = 0.1 )
col = layout . column ( )
col . prop ( rpdat , ' lnx_skin ' )
col = col . column ( )
col . enabled = rpdat . lnx_skin == ' On '
col . prop ( rpdat , ' lnx_use_armature_deform_only ' )
col . prop ( rpdat , ' lnx_skin_max_bones_auto ' )
row = col . row ( )
row . enabled = not rpdat . lnx_skin_max_bones_auto
row . prop ( rpdat , ' lnx_skin_max_bones ' )
layout . separator ( factor = 0.1 )
col = layout . column ( )
col . prop ( rpdat , ' lnx_morph_target ' )
col = col . column ( )
col . enabled = rpdat . lnx_morph_target == ' On '
layout . separator ( factor = 0.1 )
col = layout . column ( )
col . prop ( rpdat , " rp_hdr " )
col . prop ( rpdat , " rp_stereo " )
col . prop ( rpdat , ' lnx_culling ' )
col . prop ( rpdat , ' rp_pp ' )
class LNX_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 = " LNX_PT_RenderPathPanel "
def draw_header ( self , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
self . layout . prop ( rpdat , " rp_shadows " , text = " " )
def compute_subdivs ( self , max , subdivs ) :
l = [ max ]
for i in range ( subdivs - 1 ) :
l . append ( int ( max / 2 ) )
max = max / 2
return l
def tiles_per_light_type ( self , rpdat : lnx . props_renderpath . LnxRPListItem , light_type : str ) - > int :
if light_type == ' point ' :
return 6
elif light_type == ' spot ' :
return 1
else :
return int ( rpdat . rp_shadowmap_cascades )
def lights_number_atlas ( self , rpdat : lnx . props_renderpath . LnxRPListItem , atlas_size : int , shadowmap_size : int , light_type : str ) - > int :
''' Compute number lights that could fit in an atlas '''
lights = atlas_size / shadowmap_size
lights * = lights / self . tiles_per_light_type ( rpdat , light_type )
return int ( lights )
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . enabled = rpdat . rp_shadows
col = layout . column ( )
col . enabled = not rpdat . rp_shadowmap_atlas_single_map or not rpdat . rp_shadowmap_atlas
col . 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 , ' lnx_shadowmap_split ' )
col . prop ( rpdat , ' lnx_shadowmap_bounds ' )
col . prop ( rpdat , ' lnx_pcfsize ' )
layout . separator ( )
layout . prop ( rpdat , ' rp_shadowmap_atlas ' )
colatlas = layout . column ( )
colatlas . enabled = rpdat . rp_shadowmap_atlas
colatlas . prop ( rpdat , ' rp_max_lights ' )
colatlas . prop ( rpdat , ' rp_max_lights_cluster ' )
colatlas . prop ( rpdat , ' rp_shadowmap_atlas_lod ' )
colatlas_lod = colatlas . column ( )
colatlas_lod . enabled = rpdat . rp_shadowmap_atlas_lod
colatlas_lod . prop ( rpdat , ' rp_shadowmap_atlas_lod_subdivisions ' )
colatlas_lod_info = colatlas_lod . row ( )
colatlas_lod_info . alignment = ' RIGHT '
subdivs_list = self . compute_subdivs ( int ( rpdat . rp_shadowmap_cascade ) , int ( rpdat . rp_shadowmap_atlas_lod_subdivisions ) )
subdiv_text = " Subdivisions for spot lights: " + ' , ' . join ( map ( str , subdivs_list ) )
colatlas_lod_info . label ( text = subdiv_text , icon = " IMAGE_ZDEPTH " )
if not rpdat . rp_shadowmap_atlas_single_map :
colatlas_lod_info = colatlas_lod . row ( )
colatlas_lod_info . alignment = ' RIGHT '
subdivs_list = self . compute_subdivs ( int ( rpdat . rp_shadowmap_cube ) , int ( rpdat . rp_shadowmap_atlas_lod_subdivisions ) )
subdiv_text = " Subdivisions for point lights: " + ' , ' . join ( map ( str , subdivs_list ) )
colatlas_lod_info . label ( text = subdiv_text , icon = " IMAGE_ZDEPTH " )
size_warning = int ( rpdat . rp_shadowmap_cascade ) > 2048 or int ( rpdat . rp_shadowmap_cube ) > 2048
colatlas . prop ( rpdat , ' rp_shadowmap_atlas_single_map ' )
# show size for single texture
if rpdat . rp_shadowmap_atlas_single_map :
colatlas_single = colatlas . column ( )
colatlas_single . prop ( rpdat , ' rp_shadowmap_atlas_max_size ' )
if rpdat . rp_shadowmap_atlas_max_size != ' ' :
atlas_size = int ( rpdat . rp_shadowmap_atlas_max_size )
shadowmap_size = int ( rpdat . rp_shadowmap_cascade )
if shadowmap_size > 2048 :
size_warning = True
point_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' point ' )
spot_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' spot ' )
dir_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' sun ' )
col = colatlas_single . row ( )
col . alignment = ' RIGHT '
col . label ( text = f ' Enough space for { point_lights } point lights or { spot_lights } spot lights or { dir_lights } directional lights. ' )
else :
# show size for all types
colatlas_mixed = colatlas . column ( )
colatlas_mixed . prop ( rpdat , ' rp_shadowmap_atlas_max_size_spot ' )
if rpdat . rp_shadowmap_atlas_max_size_spot != ' ' :
atlas_size = int ( rpdat . rp_shadowmap_atlas_max_size_spot )
shadowmap_size = int ( rpdat . rp_shadowmap_cascade )
spot_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' spot ' )
if shadowmap_size > 2048 :
size_warning = True
col = colatlas_mixed . row ( )
col . alignment = ' RIGHT '
col . label ( text = f ' Enough space for { spot_lights } spot lights. ' )
colatlas_mixed . prop ( rpdat , ' rp_shadowmap_atlas_max_size_point ' )
if rpdat . rp_shadowmap_atlas_max_size_point != ' ' :
atlas_size = int ( rpdat . rp_shadowmap_atlas_max_size_point )
shadowmap_size = int ( rpdat . rp_shadowmap_cube )
point_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' point ' )
if shadowmap_size > 2048 :
size_warning = True
col = colatlas_mixed . row ( )
col . alignment = ' RIGHT '
col . label ( text = f ' Enough space for { point_lights } point lights. ' )
colatlas_mixed . prop ( rpdat , ' rp_shadowmap_atlas_max_size_sun ' )
if rpdat . rp_shadowmap_atlas_max_size_sun != ' ' :
atlas_size = int ( rpdat . rp_shadowmap_atlas_max_size_sun )
shadowmap_size = int ( rpdat . rp_shadowmap_cascade )
dir_lights = self . lights_number_atlas ( rpdat , atlas_size , shadowmap_size , ' sun ' )
if shadowmap_size > 2048 :
size_warning = True
col = colatlas_mixed . row ( )
col . alignment = ' RIGHT '
col . label ( text = f ' Enough space for { dir_lights } directional lights. ' )
# show warning when user picks a size higher than 2048 (arbitrary number).
if size_warning :
col = layout . column ( )
row = col . row ( )
row . alignment = ' RIGHT '
row . label ( text = ' Warning: Game will crash if texture size is higher than max texture size allowed by target. ' , icon = ' ERROR ' )
class LNX_PT_RenderPathVoxelsPanel ( bpy . types . Panel ) :
bl_label = " Voxels "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " render "
bl_options = { ' DEFAULT_CLOSED ' }
bl_parent_id = " LNX_PT_RenderPathPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . prop ( rpdat , ' rp_voxels ' )
col = layout . column ( )
col . enabled = rpdat . rp_voxels != ' Off '
col2 = col . column ( )
col2 . enabled = rpdat . rp_voxels == ' Voxel GI '
col3 = col . column ( )
col3 . enabled = rpdat . rp_voxels == ' Voxel AO '
col . prop ( rpdat , ' lnx_voxelgi_shadows ' , text = ' Shadows ' )
col2 . prop ( rpdat , ' lnx_voxelgi_refract ' , text = ' Refraction ' )
col . prop ( rpdat , ' lnx_voxelgi_clipmap_count ' )
#col.prop(rpdat, 'lnx_voxelgi_cones')
col . prop ( rpdat , ' rp_voxelgi_resolution ' )
col . prop ( rpdat , ' lnx_voxelgi_size ' )
#col.prop(rpdat, 'rp_voxelgi_resolution_z')
col2 . enabled = rpdat . rp_voxels == ' Voxel GI '
#col.prop(rpdat, 'lnx_voxelgi_temporal')
col . label ( text = " Light " )
col2 = col . column ( )
col2 . enabled = rpdat . rp_voxels == ' Voxel GI '
col2 . prop ( rpdat , ' lnx_voxelgi_diff ' )
col2 . prop ( rpdat , ' lnx_voxelgi_spec ' )
col2 . prop ( rpdat , ' lnx_voxelgi_refr ' )
col . prop ( rpdat , ' lnx_voxelgi_shad ' )
col . prop ( rpdat , ' lnx_voxelgi_occ ' )
col . label ( text = " Ray " )
col . prop ( rpdat , ' lnx_voxelgi_offset ' )
col . prop ( rpdat , ' lnx_voxelgi_step ' )
col . prop ( rpdat , ' lnx_voxelgi_range ' )
#col.prop(rpdat, 'lnx_voxelgi_aperture')
class LNX_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 = " LNX_PT_RenderPathPanel "
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . prop ( rpdat , " rp_background " )
col = layout . column ( )
col . prop ( rpdat , ' lnx_irradiance ' )
colb = col . column ( )
colb . enabled = rpdat . lnx_irradiance
colb . prop ( rpdat , ' lnx_radiance ' )
sub = colb . row ( )
sub . enabled = rpdat . lnx_radiance
sub . prop ( rpdat , ' lnx_radiance_size ' )
layout . separator ( )
layout . prop ( rpdat , ' lnx_clouds ' )
col = layout . column ( align = True )
col . prop ( rpdat , " rp_water " )
col = col . column ( align = True )
col . enabled = rpdat . rp_water
col . prop ( rpdat , ' lnx_water_level ' )
col . prop ( rpdat , ' lnx_water_density ' )
col . prop ( rpdat , ' lnx_water_displace ' )
col . prop ( rpdat , ' lnx_water_speed ' )
col . prop ( rpdat , ' lnx_water_freq ' )
col . prop ( rpdat , ' lnx_water_refract ' )
col . prop ( rpdat , ' lnx_water_reflect ' )
col . prop ( rpdat , ' lnx_water_color ' )
class LNX_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 = " LNX_PT_RenderPathPanel "
def draw_header ( self , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_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 [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . enabled = rpdat . rp_render_to_texture
col = layout . column ( )
col . prop ( rpdat , " rp_antialiasing " )
col . prop ( rpdat , " rp_supersampling " )
col = layout . column ( )
col . prop ( rpdat , ' lnx_rp_resolution ' )
if rpdat . lnx_rp_resolution == ' Custom ' :
col . prop ( rpdat , ' lnx_rp_resolution_size ' )
col . prop ( rpdat , ' lnx_rp_resolution_filter ' )
col . prop ( rpdat , ' rp_dynres ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_ssgi " )
sub = col . column ( )
sub . enabled = rpdat . rp_ssgi != ' Off '
sub . prop ( rpdat , ' lnx_ssgi_half_res ' )
sub . prop ( rpdat , ' lnx_ssgi_rays ' )
sub . prop ( rpdat , ' lnx_ssgi_radius ' )
sub . prop ( rpdat , ' lnx_ssgi_strength ' )
sub . prop ( rpdat , ' lnx_ssgi_max_steps ' )
layout . separator ( )
row = layout . row ( )
row . enabled = rpdat . lnx_material_model == ' Full '
row . prop ( rpdat , ' lnx_micro_shadowing ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_ssr " )
col . prop ( rpdat , ' lnx_ssr_half_res ' )
col = col . column ( )
col . enabled = rpdat . rp_ssr
col . prop ( rpdat , ' lnx_ssr_ray_step ' )
col . prop ( rpdat , ' lnx_ssr_search_dist ' )
col . prop ( rpdat , ' lnx_ssr_falloff_exp ' )
col . prop ( rpdat , ' lnx_ssr_jitter ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_ss_refraction " )
col = col . column ( )
col . enabled = rpdat . rp_ss_refraction
col . prop ( rpdat , ' lnx_ss_refraction_ray_step ' )
col . prop ( rpdat , ' lnx_ss_refraction_search_dist ' )
col . prop ( rpdat , ' lnx_ss_refraction_falloff_exp ' )
col . prop ( rpdat , ' lnx_ss_refraction_jitter ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_ssrs ' )
col = col . column ( )
col . enabled = rpdat . lnx_ssrs
col . prop ( rpdat , ' lnx_ssrs_ray_step ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_bloom " )
_col = col . column ( )
_col . enabled = rpdat . rp_bloom
if bpy . app . version < = ( 4 , 2 , 4 ) :
_col . prop ( rpdat , ' lnx_bloom_follow_blender ' )
if not rpdat . lnx_bloom_follow_blender :
_col . prop ( rpdat , ' lnx_bloom_threshold ' )
_col . prop ( rpdat , ' lnx_bloom_knee ' )
_col . prop ( rpdat , ' lnx_bloom_radius ' )
_col . prop ( rpdat , ' lnx_bloom_strength ' )
else :
_col . prop ( rpdat , ' lnx_bloom_threshold ' )
_col . prop ( rpdat , ' lnx_bloom_knee ' )
_col . prop ( rpdat , ' lnx_bloom_radius ' )
_col . prop ( rpdat , ' lnx_bloom_strength ' )
_col . prop ( rpdat , ' lnx_bloom_quality ' )
_col . prop ( rpdat , ' lnx_bloom_anti_flicker ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_motionblur " )
col = col . column ( )
col . enabled = rpdat . rp_motionblur != ' Off '
col . prop ( rpdat , ' lnx_motion_blur_intensity ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_volumetriclight " )
col = col . column ( )
col . enabled = rpdat . rp_volumetriclight
col . prop ( rpdat , ' lnx_volumetric_light_air_color ' )
col . prop ( rpdat , ' lnx_volumetric_light_air_turbidity ' )
col . prop ( rpdat , ' lnx_volumetric_light_steps ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_chromatic_aberration " )
col = col . column ( )
col . enabled = rpdat . rp_chromatic_aberration
col . prop ( rpdat , ' lnx_chromatic_aberration_type ' )
col . prop ( rpdat , ' lnx_chromatic_aberration_strength ' )
if rpdat . lnx_chromatic_aberration_type == " Spectral " :
col . prop ( rpdat , ' lnx_chromatic_aberration_samples ' )
class LNX_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 = " LNX_PT_RenderPathPanel "
def draw_header ( self , context ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_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 [ ' Lnx ' ]
if len ( wrd . lnx_rplist ) < = wrd . lnx_rplist_index :
return
rpdat = wrd . lnx_rplist [ wrd . lnx_rplist_index ]
layout . enabled = rpdat . rp_compositornodes
layout . prop ( rpdat , ' lnx_tonemap ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_letterbox ' )
col = col . column ( align = True )
col . enabled = rpdat . lnx_letterbox
col . prop ( rpdat , ' lnx_letterbox_color ' )
col . prop ( rpdat , ' lnx_letterbox_size ' )
layout . separator ( )
col = layout . column ( )
draw_conditional_prop ( col , ' Distort ' , rpdat , ' lnx_distort ' , ' lnx_distort_strength ' )
draw_conditional_prop ( col , ' Film Grain ' , rpdat , ' lnx_grain ' , ' lnx_grain_strength ' )
draw_conditional_prop ( col , ' Sharpen ' , rpdat , ' lnx_sharpen ' , ' lnx_sharpen_strength ' )
draw_conditional_prop ( col , ' Vignette ' , rpdat , ' lnx_vignette ' , ' lnx_vignette_strength ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_fog ' )
col = col . column ( align = True )
col . enabled = rpdat . lnx_fog
col . prop ( rpdat , ' lnx_fog_color ' )
col . prop ( rpdat , ' lnx_fog_amounta ' )
col . prop ( rpdat , ' lnx_fog_amountb ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , " rp_autoexposure " )
sub = col . column ( align = True )
sub . enabled = rpdat . rp_autoexposure
sub . prop ( rpdat , ' lnx_autoexposure_strength ' , text = ' Strength ' )
sub . prop ( rpdat , ' lnx_autoexposure_speed ' , text = ' Speed ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_fisheye ' )
col . prop ( rpdat , ' lnx_lensflare ' )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_lens ' )
col = col . column ( align = True )
col . enabled = rpdat . lnx_lens
col . prop ( rpdat , ' lnx_lens_texture ' )
if rpdat . lnx_lens_texture != " " :
col . prop ( rpdat , ' lnx_lens_texture_masking ' )
if rpdat . lnx_lens_texture_masking :
sub = col . column ( align = True )
sub . prop ( rpdat , ' lnx_lens_texture_masking_centerMinClip ' )
sub . prop ( rpdat , ' lnx_lens_texture_masking_centerMaxClip ' )
sub = col . column ( align = True )
sub . prop ( rpdat , ' lnx_lens_texture_masking_luminanceMin ' )
sub . prop ( rpdat , ' lnx_lens_texture_masking_luminanceMax ' )
col . prop ( rpdat , ' lnx_lens_texture_masking_brightnessExp ' )
layout . separator ( )
layout . separator ( )
col = layout . column ( )
col . prop ( rpdat , ' lnx_lut ' )
col = col . column ( align = True )
col . enabled = rpdat . lnx_lut
col . prop ( rpdat , ' lnx_lut_texture ' )
layout . separator ( )
class LNX_PT_BakePanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 , " lnx_bakemode " , expand = True )
if scn . lnx_bakemode == " Static Map " :
row = layout . row ( align = True )
row . alignment = ' EXPAND '
row . operator ( " lnx.bake_textures " , icon = " RENDER_STILL " )
row . operator ( " lnx.bake_apply " )
col = layout . column ( )
col . prop ( scn , ' lnx_bakelist_scale ' )
col . prop ( scn . cycles , " samples " )
layout . prop ( scn , ' lnx_bakelist_unwrap ' )
rows = 2
if len ( scn . lnx_bakelist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_BakeList " , " The_List " , scn , " lnx_bakelist " , scn , " lnx_bakelist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_bakelist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_bakelist.delete_item " , icon = ' REMOVE ' , text = " " )
col . menu ( " LNX_MT_BakeListSpecials " , icon = ' DOWNARROW_HLT ' , text = " " )
if len ( scn . lnx_bakelist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_bakelist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_bakelist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if scn . lnx_bakelist_index > = 0 and len ( scn . lnx_bakelist ) > 0 :
item = scn . lnx_bakelist [ scn . lnx_bakelist_index ]
layout . prop_search ( item , " obj " , bpy . data , " objects " , text = " Object " )
layout . prop ( item , " res_x " )
layout . prop ( item , " res_y " )
class LnxGenLodButton ( bpy . types . Operator ) :
""" Automatically generate LoD levels. """
bl_idname = ' lnx.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 . lnx_lodlist_index = 0
mdata . lnx_lodlist . clear ( )
# Lod levels
wrd = bpy . data . worlds [ ' Lnx ' ]
ratio = wrd . lnx_lod_gen_ratio
num_levels = wrd . lnx_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 . lnx_lod_gen_ratio
context . scene . collection . objects . link ( new_obj )
# Screen sizes
for level in range ( 0 , num_levels ) :
mdata . lnx_lodlist . add ( )
mdata . lnx_lodlist [ - 1 ] . name = self . lod_name ( obj . name , level )
mdata . lnx_lodlist [ - 1 ] . screen_size_prop = ( 1 - ( 1 / ( num_levels + 1 ) ) * level ) - ( 1 / ( num_levels + 1 ) )
return { ' FINISHED ' }
class LNX_PT_LodPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 . lnx_lodlist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_LodList " , " The_List " , mdata , " lnx_lodlist " , mdata , " lnx_lodlist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_lodlist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_lodlist.delete_item " , icon = ' REMOVE ' , text = " " )
if len ( mdata . lnx_lodlist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_lodlist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_lodlist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if mdata . lnx_lodlist_index > = 0 and len ( mdata . lnx_lodlist ) > 0 :
item = mdata . lnx_lodlist [ mdata . lnx_lodlist_index ]
layout . prop_search ( item , " name " , bpy . data , " objects " , text = " Object " )
layout . prop ( item , " screen_size_prop " )
layout . prop ( mdata , " lnx_lod_material " )
# Auto lod for meshes
if obj . type == ' MESH ' :
layout . separator ( )
layout . operator ( " lnx.generate_lod " )
wrd = bpy . data . worlds [ ' Lnx ' ]
layout . prop ( wrd , ' lnx_lod_gen_levels ' )
layout . prop ( wrd , ' lnx_lod_gen_ratio ' )
class LnxGenTerrainButton ( bpy . types . Operator ) :
''' Generate terrain sectors '''
bl_idname = ' lnx.generate_terrain '
bl_label = ' Generate '
def execute ( self , context ) :
scn = context . scene
if scn == None :
return { ' CANCELLED ' }
sectors = scn . lnx_terrain_sectors
size = scn . lnx_terrain_sector_size
height_scale = scn . lnx_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 . lnx_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 [ 20 ] , 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 . lnx_export = False
scn . collection . objects . link ( root_obj )
scn . lnx_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 . lnx_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 LNX_PT_TerrainPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 , ' lnx_terrain_textures ' )
layout . prop ( scn , ' lnx_terrain_sectors ' )
layout . prop ( scn , ' lnx_terrain_sector_size ' )
layout . prop ( scn , ' lnx_terrain_height_scale ' )
layout . operator ( ' lnx.generate_terrain ' )
layout . prop ( scn , ' lnx_terrain_object ' )
class LNX_PT_TilesheetPanel ( bpy . types . Panel ) :
bl_label = " Leenkx 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 [ ' Lnx ' ]
rows = 2
if len ( wrd . lnx_tilesheetlist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_TilesheetList " , " The_List " , wrd , " lnx_tilesheetlist " , wrd , " lnx_tilesheetlist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_tilesheetlist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_tilesheetlist.delete_item " , icon = ' REMOVE ' , text = " " )
if len ( wrd . lnx_tilesheetlist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_tilesheetlist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_tilesheetlist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if wrd . lnx_tilesheetlist_index > = 0 and len ( wrd . lnx_tilesheetlist ) > 0 :
dat = wrd . lnx_tilesheetlist [ wrd . lnx_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 . lnx_tilesheetactionlist ) > 1 :
rows = 4
row = layout . row ( )
row . template_list ( " LNX_UL_TilesheetList " , " The_List " , dat , " lnx_tilesheetactionlist " , dat , " lnx_tilesheetactionlist_index " , rows = rows )
col = row . column ( align = True )
col . operator ( " lnx_tilesheetactionlist.new_item " , icon = ' ADD ' , text = " " )
col . operator ( " lnx_tilesheetactionlist.delete_item " , icon = ' REMOVE ' , text = " " )
if len ( dat . lnx_tilesheetactionlist ) > 1 :
col . separator ( )
op = col . operator ( " lnx_tilesheetactionlist.move_item " , icon = ' TRIA_UP ' , text = " " )
op . direction = ' UP '
op = col . operator ( " lnx_tilesheetactionlist.move_item " , icon = ' TRIA_DOWN ' , text = " " )
op . direction = ' DOWN '
if dat . lnx_tilesheetactionlist_index > = 0 and len ( dat . lnx_tilesheetactionlist ) > 0 :
adat = dat . lnx_tilesheetactionlist [ dat . lnx_tilesheetactionlist_index ]
layout . prop ( adat , " start_prop " )
layout . prop ( adat , " end_prop " )
layout . prop ( adat , " loop_prop " )
class LnxPrintTraitsButton ( bpy . types . Operator ) :
bl_idname = ' lnx.print_traits '
bl_label = ' Print All Traits '
bl_description = ' Returns all traits in current blend '
def execute ( self , context ) :
for s in bpy . data . scenes :
print ( ' Scene: {0} ' . format ( s . name ) )
for t in s . lnx_traitlist :
if not t . enabled_prop :
continue
tname = " undefined "
if t . type_prop == ' Haxe Script ' or " Bundled " :
tname = t . class_name_prop
if t . type_prop == ' Logic Nodes ' :
tname = t . node_tree_prop . name
if t . type_prop == ' UI Canvas ' :
tname = t . canvas_name_prop
if t . type_prop == ' WebAssembly ' :
tname = t . webassembly_prop
print ( ' Scene Trait: {0} ( " {1} " ) ' . format ( s . name , tname ) )
for o in s . objects :
for t in o . lnx_traitlist :
if not t . enabled_prop :
continue
tname = " undefined "
if t . type_prop == ' Haxe Script ' or " Bundled " :
tname = t . class_name_prop
if t . type_prop == ' Logic Nodes ' :
tname = t . node_tree_prop . name
if t . type_prop == ' UI Canvas ' :
tname = t . canvas_name_prop
if t . type_prop == ' WebAssembly ' :
tname = t . webassembly_prop
print ( ' Object Trait: {0} ( " {1} " ) ' . format ( o . name , tname ) )
return { ' FINISHED ' }
class LNX_PT_MaterialNodePanel ( bpy . types . Panel ) :
bl_label = ' Leenkx Material Node '
bl_idname = ' LNX_PT_MaterialNodePanel '
bl_space_type = ' NODE_EDITOR '
bl_region_type = ' UI '
bl_category = ' Leenkx '
@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 , ' lnx_material_param ' )
class LNX_OT_CopyToBundled ( bpy . types . Operator ) :
bl_label = ' Copy To Bundled '
bl_idname = ' lnx.copy_to_bundled '
bl_description = ( ' Copies and repaths external image assets to project Bundled folder ' )
def execute ( self , context ) :
self . copy_to_bundled ( bpy . data . images )
return { ' FINISHED ' }
@classmethod
def copy_to_bundled ( self , data ) :
wrd = bpy . data . worlds [ ' Lnx ' ]
project_path = lnx . utils . get_fp ( )
# Blend - Images
for asset in data :
# File is saved
if asset . filepath_from_user ( ) != ' ' :
bundled_filepath = project_path + ' /Bundled/ ' + asset . name
try :
# Exists -> Yes
if os . path . isfile ( bundled_filepath ) :
# Override -> Yes
if ( wrd . lnx_copy_override ) :
# Valid file
if asset . has_data :
asset . filepath_raw = bundled_filepath
asset . save ( )
asset . reload ( )
# Syntax - Yellow
print ( log . colorize ( f ' Asset name " { asset . name } " already exists, overriding the original ' , 33 ) , file = sys . stderr )
# Invalid file or corrupted
else :
# Syntax - Red
log . error ( f ' Asset name " { asset . name } " has no data to save or copy, skipping ' )
continue
# Override -> No
else :
# Syntax - Yellow
print ( log . colorize ( f ' Asset name " { asset . name } " already exists, skipping ' , 33 ) , file = sys . stderr )
continue
# Exists -> No
else :
# Valid file
if asset . has_data :
asset . filepath_raw = bundled_filepath
asset . save ( )
asset . reload ( )
# Syntax - Green
print ( log . colorize ( f ' Asset name " { asset . name } " was successfully copied ' , 32 ) , file = sys . stderr )
# Invalid file or corrupted
else :
# Syntax - Red
log . error ( f ' Asset name " { asset . name } " has no data to save or copy, skipping ' )
continue
except :
# Syntax - Red
log . error ( f ' Insufficient write permissions or other issues occurred ' )
continue
# File is unsaved
else :
# Syntax - Purple
log . warn ( f ' Asset name " { asset . name } " is either packed or unsaved, skipping ' )
continue
class LNX_OT_ShowFileVersionInfo ( bpy . types . Operator ) :
bl_label = ' Show old file version info '
bl_idname = ' lnx.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 Leenkx ' )
bl_options = { ' INTERNAL ' }
wrd = None
def draw_message_box ( self , context ) :
file_version = LNX_OT_ShowFileVersionInfo . wrd . lnx_version
current_version = props . lnx_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 Leenkx version ' , icon = ' BLANK1 ' )
layout . separator ( )
layout . label ( f ' (version: { current_version } ' )
row = layout . row ( align = True )
row . active_default = True
row . operator ( ' lnx.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 Leenkx! ' , 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 Leenkx 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 ( ' lnx.update_file_sdk ' , text = ' Yes ' )
row . active_default = False
row . operator ( ' lnx.discard_popup ' , text = ' No ' )
else :
layout . label ( text = ' Warning: This file was saved in a ' , icon = ' ERROR ' )
layout . label ( text = ' future version of Leenkx! ' , 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 ( ' lnx.discard_popup ' , text = ' Ok ' )
def execute ( self , context ) :
LNX_OT_ShowFileVersionInfo . wrd = bpy . data . worlds [ ' Lnx ' ]
context . window_manager . popover ( LNX_OT_ShowFileVersionInfo . draw_message_box , ui_units_x = 16 )
return { " FINISHED " }
class LNX_OT_ShowNodeUpdateErrors ( bpy . types . Operator ) :
bl_label = ' Show upgrade failure details '
bl_idname = ' lnx.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 = lnx . logicnode . replacement . 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 = LNX_OT_ShowNodeUpdateErrors . wrd . lnx_version
current_version = props . lnx_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_leenkx_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 Leenkx 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_leenkx_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_leenkx_upgrade :
layout . label ( text = ' Some nodes seem to be too old to be understood by leenkx anymore ' , icon = ' BLANK1 ' )
else :
layout . label ( text = " Some nodes are unknown to leenkx, either because they are too new or too old. " , icon = ' BLANK1 ' )
if ' future version ' in error_types :
if is_leenkx_upgrade :
layout . label ( text = ' Somehow, some nodes seem to have been created with a future version of leenkx. ' , icon = ' BLANK1 ' )
else :
layout . label ( text = ' Some nodes seem to have been created with a future version of leenkx. ' , 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 ( ' lnx.discard_popup ' , text = ' Ok ' )
row . operator ( ' lnx.open_project_folder ' , text = ' Open Project Folder ' , icon = " FILE_FOLDER " )
def execute ( self , context ) :
LNX_OT_ShowNodeUpdateErrors . wrd = bpy . data . worlds [ ' Lnx ' ]
context . window_manager . popover ( LNX_OT_ShowNodeUpdateErrors . draw_message_box , ui_units_x = 32 )
return { " FINISHED " }
class LNX_OT_UpdateFileSDK ( bpy . types . Operator ) :
bl_idname = ' lnx.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 [ ' Lnx ' ]
# This allows for seamless migration from earlier versions of Leenkx
for rp in wrd . lnx_rplist : # TODO: deprecated
if rp . rp_gi != ' Off ' :
rp . rp_gi = ' Off '
rp . rp_voxelao = True
# Replace deprecated nodes
lnx . logicnode . replacement . replace_all ( )
wrd . lnx_version = props . lnx_version
wrd . lnx_commit = props . lnx_commit
lnx . make . clean ( )
print ( f ' Project updated to SDK { props . lnx_version } . Please save the .blend file. ' )
return { ' FINISHED ' }
class LNX_OT_DiscardPopup ( bpy . types . Operator ) :
""" Empty operator for discarding dialogs. """
bl_idname = ' lnx.discard_popup '
bl_label = ' OK '
bl_description = ' Discard '
bl_options = { ' INTERNAL ' }
def execute ( self , context ) :
return { ' FINISHED ' }
class LeenkxUpdateListAndroidEmulatorButton ( bpy . types . Operator ) :
''' Updating the list of emulators for the Android platform '''
bl_idname = ' lnx.update_list_android_emulator '
bl_label = ' Update List Emulators '
def execute ( self , context ) :
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
if not lnx . utils . check_sdkpath ( self ) :
return { " CANCELLED " }
if len ( lnx . utils . get_android_sdk_root_path ( ) ) == 0 :
return { " CANCELLED " }
os . environ [ ' ANDROID_SDK_ROOT ' ] = lnx . utils . get_android_sdk_root_path ( )
items , err = lnx . utils . get_android_emulators_list ( )
if len ( err ) > 0 :
print ( ' Update List Emulators Warning: File " ' + lnx . 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 ' }
if len ( items ) > 0 :
items_enum = [ ]
for i in items :
items_enum . append ( ( i , i , i ) )
bpy . types . World . lnx_project_android_list_avd = EnumProperty ( items = items_enum , name = " Emulator " , update = assets . invalidate_compiler_cache )
return { ' FINISHED ' }
class LeenkxUpdateListAndroidEmulatorRunButton ( bpy . types . Operator ) :
''' Launch Android emulator selected from the list '''
bl_idname = ' lnx.run_android_emulator '
bl_label = ' Launch Emulator '
def execute ( self , context ) :
if not lnx . utils . check_saved ( self ) :
return { " CANCELLED " }
if not lnx . utils . check_sdkpath ( self ) :
return { " CANCELLED " }
if len ( lnx . utils . get_android_sdk_root_path ( ) ) == 0 :
return { " CANCELLED " }
make . run_android_emulators ( lnx . utils . get_android_emulator_name ( ) )
return { ' FINISHED ' }
class LeenkxUpdateListInstalledVSButton ( bpy . types . Operator ) :
""" Update the list of installed Visual Studio versions for the Windows platform """
bl_idname = ' lnx.update_list_installed_vs '
bl_label = ' (Re-)Fetch Installed Visual Studio Versions '
def execute ( self , context ) :
if not lnx . utils . get_os_is_windows ( ) :
return { " CANCELLED " }
success = lnx . utils_vs . fetch_installed_vs ( )
if not success :
self . report ( { " ERROR " } , " Could not fetch installed Visual Studio versions, check console for details. " )
return { ' CANCELLED ' }
return { ' FINISHED ' }
class LNX_PT_BulletDebugDrawingPanel ( bpy . types . Panel ) :
bl_label = " Leenkx Debug Drawing "
bl_space_type = " PROPERTIES "
bl_region_type = " WINDOW "
bl_context = " scene "
bl_options = { ' DEFAULT_CLOSED ' }
bl_parent_id = " SCENE_PT_rigid_body_world "
@classmethod
def poll ( cls , context ) :
return context . scene . rigidbody_world is not None
def draw ( self , context ) :
layout = self . layout
layout . use_property_split = True
layout . use_property_decorate = False
wrd = bpy . data . worlds [ ' Lnx ' ]
if wrd . lnx_physics_engine != ' Bullet ' :
row = layout . row ( )
row . alert = True
row . label ( text = " Physics debug drawing is only supported for the Bullet physics engine " )
col = layout . column ( align = False )
col . prop ( wrd , " lnx_bullet_dbg_draw_wireframe " )
col . prop ( wrd , " lnx_bullet_dbg_draw_aabb " )
col . prop ( wrd , " lnx_bullet_dbg_draw_contact_points " )
col . prop ( wrd , " lnx_bullet_dbg_draw_constraints " )
col . prop ( wrd , " lnx_bullet_dbg_draw_constraint_limits " )
col . prop ( wrd , " lnx_bullet_dbg_draw_normals " )
col . prop ( wrd , " lnx_bullet_dbg_draw_axis_gizmo " )
def draw_custom_node_menu ( self , context ) :
""" Extension of the node context menu.
https : / / blender . stackexchange . com / questions / 150101 / python - how - to - add - items - in - context - menu - in - 2 - 8
"""
if context . selected_nodes is None or len ( context . selected_nodes ) != 1 :
return
if context . space_data . tree_type == ' LnxLogicTreeType ' :
if context . selected_nodes [ 0 ] . bl_idname . startswith ( ' LN ' ) :
layout = self . layout
layout . separator ( )
layout . operator ( " lnx.open_node_documentation " , text = " Show documentation for this node " , icon = ' HELP ' )
layout . operator ( " lnx.open_node_haxe_source " , text = " Open .hx source in the browser " , icon_value = ui_icons . get_id ( " haxe " ) )
layout . operator ( " lnx.open_node_python_source " , text = " Open .py source in the browser " , icon = ' FILE_SCRIPT ' )
elif context . space_data . tree_type == ' ShaderNodeTree ' :
if context . active_node . bl_idname in ( ' ShaderNodeRGB ' , ' ShaderNodeValue ' , ' ShaderNodeTexImage ' ) :
layout = self . layout
layout . separator ( )
layout . prop ( context . active_node , ' lnx_material_param ' , text = ' Leenkx: Material Parameter ' )
def draw_conditional_prop ( layout : bpy . types . UILayout , heading : str , data : bpy . types . AnyType , prop_condition : str , prop_value : str ) - > None :
""" Draws a property row with a checkbox that enables a value field.
The function fails when prop_condition is not a boolean property .
"""
col = layout . column ( heading = heading )
row = col . row ( )
row . prop ( data , prop_condition , text = ' ' )
sub = row . row ( )
sub . enabled = getattr ( data , prop_condition )
sub . prop ( data , prop_value , expand = True )
def draw_error_box ( layout : bpy . types . UILayout , text : str ) - > bpy . types . UILayout :
""" Draw an error box in the given UILayout and return it for
further optional modification . The text is wrapped automatically
according to the current region ' s width.
"""
textwrap_width = max ( 0 , int ( ( bpy . context . region . width - 25 ) / 6 ) )
lines = textwrap . wrap ( text , width = textwrap_width , break_long_words = True )
box = layout . box ( )
col = box . column ( align = True )
col . alert = True
for idx , line in enumerate ( lines ) :
col . label ( text = line , icon = ' ERROR ' if idx == 0 else ' BLANK1 ' )
return box
def draw_multiline_with_icon ( layout : bpy . types . UILayout , layout_width_px : int , icon : str , text : str ) - > bpy . types . UILayout :
""" Draw a multiline string with an icon in the given UILayout
and return it for further optional modification .
The text is wrapped according to the given layout width .
"""
textwrap_width = max ( 0 , layout_width_px / / 6 )
lines = textwrap . wrap ( text , width = textwrap_width , break_long_words = True )
col = layout . column ( align = True )
col . scale_y = 0.8
for idx , line in enumerate ( lines ) :
col . label ( text = line , icon = icon if idx == 0 else ' BLANK1 ' )
return col
__REG_CLASSES = (
LNX_PT_ObjectPropsPanel ,
LNX_PT_ModifiersPropsPanel ,
LNX_PT_ParticlesPropsPanel ,
LNX_PT_PhysicsPropsPanel ,
LNX_PT_DataPropsPanel ,
LNX_PT_ScenePropsPanel ,
LNX_PT_WorldPropsPanel ,
InvalidateCacheButton ,
InvalidateMaterialCacheButton ,
LNX_OT_NewCustomMaterial ,
LNX_PG_BindTexturesListItem ,
LNX_UL_BindTexturesList ,
LNX_OT_BindTexturesListNewItem ,
LNX_OT_BindTexturesListDeleteItem ,
LNX_PT_MaterialPropsPanel ,
LNX_PT_BindTexturesPropsPanel ,
LNX_PT_MaterialBlendingPropsPanel ,
LNX_PT_MaterialDriverPropsPanel ,
LNX_PT_LeenkxPlayerPanel ,
LNX_PT_TopbarPanel ,
LNX_PT_LeenkxExporterPanel ,
LNX_PT_LeenkxExporterAndroidSettingsPanel ,
LNX_PT_LeenkxExporterAndroidPermissionsPanel ,
LNX_PT_LeenkxExporterAndroidAbiPanel ,
LNX_PT_LeenkxExporterAndroidBuildAPKPanel ,
LNX_PT_LeenkxExporterHTML5SettingsPanel ,
LNX_PT_LeenkxExporterWindowsSettingsPanel ,
LNX_PT_LeenkxProjectPanel ,
LNX_PT_ProjectFlagsPanel ,
LNX_PT_ProjectFlagsDebugConsolePanel ,
LNX_PT_ProjectWindowPanel ,
LNX_PT_ProjectModulesPanel ,
LNX_PT_RenderPathPanel ,
LNX_PT_RenderPathRendererPanel ,
LNX_PT_RenderPathShadowsPanel ,
LNX_PT_RenderPathVoxelsPanel ,
LNX_PT_RenderPathWorldPanel ,
LNX_PT_RenderPathPostProcessPanel ,
LNX_PT_RenderPathCompositorPanel ,
LNX_PT_BakePanel ,
# LnxVirtualInputPanel,
LeenkxPlayButton ,
LeenkxStopButton ,
LeenkxBuildProjectButton ,
LeenkxOpenProjectFolderButton ,
LeenkxOpenEditorButton ,
CleanMenu ,
CleanButtonMenu ,
LeenkxCleanProjectButton ,
LeenkxPublishProjectButton ,
LnxGenLodButton ,
LNX_PT_LodPanel ,
LnxGenTerrainButton ,
LNX_PT_TerrainPanel ,
LNX_PT_TilesheetPanel ,
LnxPrintTraitsButton ,
LNX_PT_MaterialNodePanel ,
LNX_OT_UpdateFileSDK ,
LNX_OT_CopyToBundled ,
LNX_OT_ShowFileVersionInfo ,
LNX_OT_ShowNodeUpdateErrors ,
LNX_OT_DiscardPopup ,
LeenkxUpdateListAndroidEmulatorButton ,
LeenkxUpdateListAndroidEmulatorRunButton ,
LeenkxUpdateListInstalledVSButton ,
LNX_PT_BulletDebugDrawingPanel ,
LNX_OT_AddArmatureRootMotion ,
scene . TLM_PT_Settings ,
scene . TLM_PT_Denoise ,
scene . TLM_PT_Filtering ,
scene . TLM_PT_Encoding ,
scene . TLM_PT_Utility ,
scene . TLM_PT_Additional ,
)
__reg_classes , __unreg_classes = bpy . utils . register_classes_factory ( __REG_CLASSES )
def register ( ) :
__reg_classes ( )
bpy . types . VIEW3D_HT_header . append ( draw_view3d_header )
bpy . types . VIEW3D_MT_object . append ( draw_view3d_object_menu )
bpy . types . NODE_MT_context_menu . append ( draw_custom_node_menu )
bpy . types . TOPBAR_HT_upper_bar . prepend ( draw_space_topbar )
bpy . types . Material . lnx_bind_textures_list = CollectionProperty ( type = LNX_PG_BindTexturesListItem )
bpy . types . Material . lnx_bind_textures_list_index = IntProperty ( name = ' Index for lnx_bind_textures_list ' , default = 0 )
def unregister ( ) :
bpy . types . NODE_MT_context_menu . remove ( draw_custom_node_menu )
bpy . types . VIEW3D_MT_object . remove ( draw_view3d_object_menu )
bpy . types . VIEW3D_HT_header . remove ( draw_view3d_header )
bpy . types . TOPBAR_HT_upper_bar . remove ( draw_space_topbar )
__unreg_classes ( )