diff --git a/addon/io_scs_tools/__init__.py b/addon/io_scs_tools/__init__.py index 422341a..d0e8288 100644 --- a/addon/io_scs_tools/__init__.py +++ b/addon/io_scs_tools/__init__.py @@ -22,37 +22,27 @@ "name": "SCS Tools", "description": "Setup models, Import-Export SCS data format", "author": "Simon Lusenc (50keda), Milos Zajic (4museman)", - "version": (1, 4, "4ae6843"), - "blender": (2, 75, 0), + "version": (1, 5, "78732b8"), + "blender": (2, 78, 0), "location": "File > Import-Export", - "wiki_url": "https://github.com/SCSSoftware/BlenderTools/wiki", + "wiki_url": "http://modding.scssoft.com/wiki/Documentation/Tools/SCS_Blender_Tools", "tracker_url": "http://forum.scssoft.com/viewforum.php?f=163", "support": "COMMUNITY", "category": "Import-Export"} - -def get_tools_version(): - """Returns Blender Tools version as string from bl_info["version"] dictonary value. - :return: string representation of bl_info["version"] tuple - :rtype: str - """ - ver = "" - for ver_num in bl_info["version"]: - ver += str(ver_num) + "." - return ver[:-1] - - import bpy import os import traceback from bpy.props import CollectionProperty, StringProperty, PointerProperty, BoolProperty from bpy_extras.io_utils import ImportHelper, ExportHelper +from io_scs_tools.consts import Icons as _ICONS_consts from io_scs_tools.imp import pix as _pix_import from io_scs_tools.internals.callbacks import open_gl as _open_gl_callback from io_scs_tools.internals.callbacks import persistent as _persistent_callback from io_scs_tools.internals import icons as _icons from io_scs_tools.utils import get_scs_globals as _get_scs_globals from io_scs_tools.utils.view3d import switch_layers_visibility as _switch_layers_visibility +from io_scs_tools.utils.view3d import has_view3d_space as _has_view3d_space from io_scs_tools.utils.printout import lprint # importing all SCS Tools modules which creates panels in UI from io_scs_tools import ui @@ -90,6 +80,25 @@ def check(self, context): return True def execute(self, context): + + # if paths are still initializing report that to user and don't execute import + if operators.world.SCSPathsInitialization.is_running(): + + self.report({'INFO'}, "Can't import yet, paths initialization is still in progress! Try again in few moments.") + + # there is no way to keep current operator alive if we want to abort import sequence. + # That's why we call another import operator, which will end up with + # printing out above info and taking us back to import screen with file browser. + bpy.ops.import_mesh.pim('INVOKE_DEFAULT') + + return {'FINISHED'} + + if not _has_view3d_space(context.screen): + message = "Cannot import SCS Models, no 3D viewport found! Make sure you have at least one 3D view visible." + self.report({'ERROR'}, message) + lprint("E " + message) + return {'FINISHED'} + paths = [os.path.join(self.directory, name.name) for name in self.files] if not paths: paths.append(self.path) @@ -111,17 +120,17 @@ def execute(self, context): _get_scs_globals().import_in_progress = False context.window.cursor_modal_restore() - traceback.print_exc() - lprint("E Unexpected %r accured during import, see stack trace above.", (type(e).__name__,)) + trace_str = traceback.format_exc().replace("\n", "\n\t ") + lprint("E Unexpected %r accured during import:\n\t %s", (type(e).__name__, trace_str)) if result is False: failed_files.append(str(filepath).replace("\\", "/")) if len(failed_files) > 0: - err_message = "E Following files failed to load:\n" + err_message = "E Following files failed to load:" for _ in failed_files: - err_message += "-> %r\n" + err_message += "\n\t -> %r\n" lprint(err_message, tuple(failed_files), report_warnings=1, report_errors=1) @@ -150,6 +159,11 @@ def draw(self, context): layout_box_row = layout_box_col.row(align=True) layout_box_row.prop(self, "scs_project_path_mode", toggle=True, text="Set Current Dir as Project Base", icon='SCREEN_BACK') + if operators.world.SCSPathsInitialization.is_running(): # report running path initialization operator + layout_box_row = layout_box_col.row(align=True) + layout_box_row.label("Paths initialization in progress...") + layout_box_row.label("", icon='TIME') + # import settings box2 = layout.box() col = box2.column(align=True) @@ -256,11 +270,9 @@ def execute(self, context): result = {"CANCELLED"} context.window.cursor_modal_restore() - import traceback - - traceback.print_exc() - lprint("E Unexpected %r accured during batch export, see stack trace above.", - (type(e).__name__,), + trace_str = traceback.format_exc().replace("\n", "\n\t ") + lprint("E Unexpected %r accured during batch export:\n\t %s", + (type(e).__name__, trace_str), report_errors=1, report_warnings=1) @@ -273,6 +285,20 @@ def draw(self, context): ui.shared.draw_export_panel(self.layout) +class SCSAddObject(bpy.types.Menu): + bl_idname = "INFO_MT_SCS_add_object" + bl_label = "SCS Object" + bl_description = "Creates menu for adding SCS objects." + + def draw(self, context): + self.layout.operator_enum("object.scs_add_object", "new_object_type") + + +def add_menu_func(self, context): + self.layout.menu("INFO_MT_SCS_add_object", text="SCS Object", icon_value=_icons.get_icon(_ICONS_consts.Types.scs_logo_orange)) + self.layout.separator() + + def menu_func_import(self, context): self.layout.operator(ImportSCS.bl_idname, text="SCS Formats (.pim)") @@ -288,7 +314,7 @@ def register(): bpy.utils.register_module(__name__) - # CUSTOM ICONS CLEANUP + # CUSTOM ICONS INITIALIZATION _icons.init() # PROPERTIES REGISTRATION @@ -358,17 +384,16 @@ def register(): # MENU REGISTRATION bpy.types.INFO_MT_file_import.append(menu_func_import) bpy.types.INFO_MT_file_export.append(menu_func_export) + bpy.types.INFO_MT_add.prepend(add_menu_func) def unregister(): bpy.utils.unregister_module(__name__) - # CUSTOM ICONS CLEANUP - _icons.cleanup() - # REMOVE MENU ENTRIES bpy.types.INFO_MT_file_export.remove(menu_func_export) bpy.types.INFO_MT_file_import.remove(menu_func_import) + bpy.types.INFO_MT_add.remove(add_menu_func) # REMOVE OPENGL HANDLERS _open_gl_callback.disable() diff --git a/addon/io_scs_tools/consts.py b/addon/io_scs_tools/consts.py index af73c1f..c1d40f3 100644 --- a/addon/io_scs_tools/consts.py +++ b/addon/io_scs_tools/consts.py @@ -22,6 +22,7 @@ Constants for data group of map and navigation curves """ +from math import pi from enum import Enum from zipfile import ZIP_STORED, ZIP_DEFLATED, ZIP_BZIP2 @@ -63,6 +64,31 @@ class TerrainPoints: vg_name_regex = "^" + vg_name_prefix.replace(".", "\\.") + "\d$" """Regex for matching terrain points vertex groups names on export.""" + class View3DReport: + """Constants related to 3D view report operator. + """ + # constants defining BT logo image and texts/positions of close/hide controls + BT_LOGO_IMG_NAME = ".scs_bt_logo.png" + BT_LOGO_AREA = (20, 217, 30, 50) + CLOSE_BTN_AREA = (230, 370, 26, 54) + CLOSE_BTN_TEXT = ( + "[Click] Close", # used when report text is shown + "[Click/ESC] Close" # used when report text is hidden (aka condensed mode) + ) + CLOSE_BTN_TEXT_POS = ( + (260, 45), # used when report text is shown + (240, 45) # used when report text is hidden (aka condensed mode) + ) + HIDE_BTN_AREA = (385, 530, 26, 54) + HIDE_BTN_TEXT = ( + "[Click/ESC] Hide", # used when report text is shown + "[Click] Show" # used when report text is hidden (aka condensed mode) + ) + HIDE_BTN_TEXT_POS = ( + (400, 45), # used when report text is shown + (415, 45) # used when report text is hidden (aka condensed mode) + ) + class Icons: """Constants related to loading of custom icons. @@ -93,6 +119,7 @@ class Types: loc_collider_convex = ".20_collider_convex.png" scs_root = ".21_scs_root_object.png" scs_logo = ".icon_scs_bt_logo.png" + scs_logo_orange = ".icon_scs_bt_logo_orange.png" @staticmethod def as_list(): @@ -105,7 +132,8 @@ def as_list(): Icons.Types.loc_prefab_node, Icons.Types.loc_prefab_sign, Icons.Types.loc_prefab_spawn, Icons.Types.loc_prefab_semaphore, Icons.Types.loc_prefab_navigation, Icons.Types.loc_prefab_map, Icons.Types.loc_prefab_trigger, Icons.Types.loc_collider_box, Icons.Types.loc_collider_sphere, Icons.Types.loc_collider_capsule, - Icons.Types.loc_collider_cylinder, Icons.Types.loc_collider_convex, Icons.Types.scs_root, Icons.Types.scs_logo] + Icons.Types.loc_collider_cylinder, Icons.Types.loc_collider_convex, Icons.Types.scs_root, + Icons.Types.scs_logo_orange, Icons.Types.scs_logo] class Part: @@ -301,6 +329,7 @@ class PSP: UNLOAD_MEDIUM_POS = 19 UNLOAD_HARD_POS = 20 UNLOAD_RIGID_POS = 21 + WEIGHT_CAT_POS = 22 class TST: """Constants representing type of traffic semaphores. @@ -314,6 +343,8 @@ class TST: BARRIER_DISTANCE = 6 TRAFFIC_LIGHT_BLOCKABLE = 7 BARRIER_GAS = 8 + TRAFFIC_LIGHT_VIRTUAL = 9 + BARRIER_AUTOMATIC = 10 class MPVF: """Constants represeting map point visual flags. @@ -323,6 +354,10 @@ class MPVF: ROAD_SIZE_2_LANE = 0x00000200 ROAD_SIZE_3_LANE = 0x00000300 ROAD_SIZE_4_LANE = 0x00000400 + ROAD_SIZE_2_LANE_SPLIT = 0x00000500 + ROAD_SIZE_3_LANE_SPLIT = 0x00000600 + ROAD_SIZE_4_LANE_SPLIT = 0x00000700 + ROAD_SIZE_3_LANE_ONE_WAY = 0x00000800 ROAD_SIZE_MANUAL = 0x00000D00 ROAD_SIZE_AUTO = 0x00000E00 ROAD_SIZE_MASK = 0x00000F00 @@ -334,6 +369,7 @@ class MPVF: ROAD_OFFSET_15 = 0x00005000 ROAD_OFFSET_20 = 0x00006000 ROAD_OFFSET_25 = 0x00007000 + ROAD_OFFSET_LANE = 0x00008000 ROAD_OFFSET_MASK = 0x0000F000 ROAD_EXT_VALUE_MASK = 0x000000FF ROAD_OVER = 0x00010000 @@ -389,3 +425,21 @@ class ConvHlpr: StoredZip = str(ZIP_STORED) DeflatedZip = str(ZIP_DEFLATED) Bzip2Zip = str(ZIP_BZIP2) + + +class SCSLigthing: + """Constants for scs lighting scene. Lighting scene is created from sun profile loaded from SII file. + """ + scene_name = ".scs_lighting" + """Name of lighting scene. It should be prefixed with dot to be partially hidden in scene selection theme.""" + + ambient_lamps = ( + (".scs_ambient_z+", (pi, 0, 0), 0.5), + (".scs_ambient_z-", (0, 0, 0), 1.1) + ) + """Ambient lamps definitions. Each lamp is defined as (name, direction, energy_factor). + There has to be 6 hemi lamps to point in each direction, which should reassemble ambient lights. + Energy factor in each of lamps tells percent of given light by that ambient lamp.""" + + diffuse_lamp_name = ".scs_diffuse" + specular_lamp_name = ".scs_specular" diff --git a/addon/io_scs_tools/exp/__init__.py b/addon/io_scs_tools/exp/__init__.py index f53ac6a..4da00b6 100644 --- a/addon/io_scs_tools/exp/__init__.py +++ b/addon/io_scs_tools/exp/__init__.py @@ -25,6 +25,7 @@ from io_scs_tools.exp import pip from io_scs_tools.exp import pis from io_scs_tools.exp import pix +from io_scs_tools.utils import name as _name_utils from io_scs_tools.utils import object as _object_utils from io_scs_tools.utils import path as _path_utils from io_scs_tools.utils import get_scs_globals as _get_scs_globals @@ -57,6 +58,20 @@ def batch_export(operator_instance, init_obj_list, menu_filepath=None): for root_object in game_objects_dict: + if not _name_utils.is_valid_scs_root_object_name(root_object.name): + lprint("E Rejecting Game Object with invalid SCS Root Object name: %r.\n\t " + "Only a-z, A-Z, 0-9 and \"._-\" characters can be used." % root_object.name) + scs_game_objects_rejected.append("> \"" + root_object.name + "\"") + continue + + game_object_list = game_objects_dict[root_object] + if len(game_object_list) == 0: + lprint("E Rejecting empty Game Object with SCS Root Object name: %r\n\t " + + "Game Object has to have at least one mesh object or model locator!", + (root_object.name,)) + scs_game_objects_rejected.append("> \"" + root_object.name + "\"") + continue + # update root object location to invoke update tagging on it and # then update scene to make sure all children objects will have all transforms up to date # NOTE: needed because Blender doesn't update objects on invisible layers on it's own @@ -64,8 +79,6 @@ def batch_export(operator_instance, init_obj_list, menu_filepath=None): for scene in bpy.data.scenes: scene.update() - game_object_list = game_objects_dict[root_object] - # GET CUSTOM FILE PATH custom_filepath = _path_utils.get_custom_scs_root_export_path(root_object) @@ -100,7 +113,7 @@ def batch_export(operator_instance, init_obj_list, menu_filepath=None): filepath_message ) else: - message = "No valid export path found! Please check \"SCS Project Base Path\" first." + message = "No valid export path found! Please check 'SCS Project Base Path' first." lprint('E ' + message) operator_instance.report({'ERROR'}, message.replace("\t", "").replace(" ", "")) return {'CANCELLED'} @@ -124,13 +137,15 @@ def batch_export(operator_instance, init_obj_list, menu_filepath=None): lprint("I " + message) if len(scs_game_objects_exported) + len(scs_game_objects_rejected) == 0: - message = "Nothing to export! Please set at least one 'SCS Root Object'." + message = "Nothing to export! Please setup at least one SCS Root Object." lprint('E ' + message) operator_instance.report({'ERROR'}, message) return {'CANCELLED'} else: - message = "No 'SCS Root Object' present or all of them were manually exluded from export in their settings.\n\t " \ - "(For more information, please refer to 'SCS Blender Tools' documentation.)" + message = "No Game Objects to export because:\n\t " \ + "1. Selection export is used and none of selected objects belongs to any SCS Game Object or\n\t " \ + "2. all of the SCS Root Objects were manually exluded from export or\n\t " \ + "3. there is no SCS Root Objects in the scene." lprint('E ' + message) operator_instance.report({'ERROR'}, message.replace("\n\t ", "\n")) return {'CANCELLED'} diff --git a/addon/io_scs_tools/exp/pia.py b/addon/io_scs_tools/exp/pia.py index 0914ec1..fe15eaf 100644 --- a/addon/io_scs_tools/exp/pia.py +++ b/addon/io_scs_tools/exp/pia.py @@ -21,6 +21,7 @@ import os import bpy +from collections import OrderedDict from mathutils import Vector, Matrix, Euler, Quaternion from io_scs_tools.utils import convert as _convert_utils from io_scs_tools.utils import get_scs_globals as _get_scs_globals @@ -99,7 +100,7 @@ def _get_bone_channels(scs_root_obj, armature, scs_animation, action, export_sca armature_mat = scs_root_obj.matrix_world.inverted() * armature.matrix_world invalid_data = False # flag to indicate invalid data state - curves_per_bone = {} # store all the curves we are interested in per bone names + curves_per_bone = OrderedDict() # store all the curves we are interested in per bone names for bone in armature.data.bones: for fcurve in action.fcurves: diff --git a/addon/io_scs_tools/exp/pim/exporter.py b/addon/io_scs_tools/exp/pim/exporter.py index 0b5778c..744f330 100644 --- a/addon/io_scs_tools/exp/pim/exporter.py +++ b/addon/io_scs_tools/exp/pim/exporter.py @@ -178,8 +178,15 @@ def execute(dirpath, root_object, armature_object, skeleton_filepath, mesh_objec mesh_pieces[pim_mat_name] = Piece(len(pim_pieces) + len(mesh_pieces), pim_materials[pim_mat_name]) nmap_uv_layer = pim_materials[pim_mat_name].get_nmap_uv_name() - if nmap_uv_layer: # if there is uv layer used for normal maps then calculate tangents on it - mesh.calc_tangents(uvmap=nmap_uv_layer) + # if there is uv layer used for normal maps and that uv layer exists on mesh then calculate tangents on it otherwise report warning + if nmap_uv_layer: + + if nmap_uv_layer in mesh.uv_layers: + mesh.calc_tangents(uvmap=nmap_uv_layer) + else: + lprint("W Unable to calculate normal map tangents for object %r,\n\t " + "as it's missing UV layer with name: %r, expect problems!", + (mesh_obj.name, nmap_uv_layer)) mesh_piece = mesh_pieces[pim_mat_name] """:type: Piece""" @@ -282,6 +289,12 @@ def execute(dirpath, root_object, armature_object, skeleton_filepath, mesh_objec # save to terrain points storage if present in correct vertex group for group in mesh.vertices[vert_i].groups: + # if current object doesn't have vertex group found in mesh data, then ignore that group + # This can happen if multiple objects are using same mesh and + # some of them have vertex groups, but others not. + if group.group >= len(mesh_obj.vertex_groups): + continue + curr_vg_name = mesh_obj.vertex_groups[group.group].name # if vertex group name doesn't match prescribed one ignore this vertex group diff --git a/addon/io_scs_tools/exp/pix.py b/addon/io_scs_tools/exp/pix.py index b0f0dd0..74e2d25 100644 --- a/addon/io_scs_tools/exp/pix.py +++ b/addon/io_scs_tools/exp/pix.py @@ -140,15 +140,15 @@ def export(dirpath, root_object, game_object_list): if len(mesh_objects) == 0: context.window.cursor_modal_restore() - lprint("E Animated SCS Game Object has to have at least one mesh object!\n\t " + - "SCS Game Object %r won't be exported!", + lprint("E Rejecting animated Game Object with SCS Root Object name %r.\n\t " + "Animated Game Object has to have at least one mesh object!", (root_object.name,)) return False if len(mesh_objects) == 0 and len(model_locators) == 0: context.window.cursor_modal_restore() - lprint("E SCS Game Object has to have at least one mesh object or model locator!\n\t " + - "SCS Game Object %r won't be exported!", + lprint("E Rejecting empty Game Object with SCS Root Object name: %r\n\t " + + "Game Object has to have at least one mesh object or model locator!", (root_object.name,)) return False @@ -200,12 +200,29 @@ def export(dirpath, root_object, game_object_list): # make sure to get relative path from PIA to PIS (animations may use custom export path) skeleton_filepath = _path_utils.get_skeleton_relative_filepath(armature_object, anim_dirpath, root_object.name) + exported_anims_names = {} # store exported animations paths, so we can report duplicates and overwrites + for scs_anim in root_object.scs_object_animation_inventory: if scs_anim.export: # check if export is disabled on animation itself # TODO: use bones transitional variable for safety checks - _pia.export(root_object, armature_object, scs_anim, anim_dirpath, skeleton_filepath) + export_success = _pia.export(root_object, armature_object, scs_anim, anim_dirpath, skeleton_filepath) + + if export_success: + + if scs_anim.name not in exported_anims_names: + exported_anims_names[scs_anim.name] = 1 + else: + exported_anims_names[scs_anim.name] += 1 + + for anim_name, export_count in exported_anims_names.items(): + + if export_count > 1: + + lprint("W Detected %s animation instances on SCS Root Object: %r with same name: %r.\n\t " + "Only last one stayed exported as it overwrote previous ones!", + (export_count, root_object.name, anim_name)) else: lprint("E Custom animations export path is not relative to SCS Project Base Path.\n\t " + diff --git a/addon/io_scs_tools/imp/pim.py b/addon/io_scs_tools/imp/pim.py index 24572e7..d90c4b4 100644 --- a/addon/io_scs_tools/imp/pim.py +++ b/addon/io_scs_tools/imp/pim.py @@ -588,7 +588,7 @@ def _create_5_piece( context.window_manager.progress_update(0.6) # Add the mesh as an object into the scene with this utility module. - obj = bpy_object_utils.object_data_add(context, mesh).object + obj = bpy_object_utils.object_data_add(context, mesh, use_active_layer=False).object obj.scs_props.object_identity = obj.name obj.location = (0.0, 0.0, 0.0) diff --git a/addon/io_scs_tools/internals/connections/wrappers/group.py b/addon/io_scs_tools/internals/connections/wrappers/group.py index ac17fb5..8dc9092 100644 --- a/addon/io_scs_tools/internals/connections/wrappers/group.py +++ b/addon/io_scs_tools/internals/connections/wrappers/group.py @@ -63,7 +63,7 @@ def init(): """ # create proper group if needed - if not _GROUP_NAME in bpy.data.groups: + if _GROUP_NAME not in bpy.data.groups: bpy.data.groups.new(_GROUP_NAME) # just make sure that data block won't get deleted after several savings of blend file @@ -268,6 +268,10 @@ def switch_to_stall(): NOTE: this will effect drawing only if optimized drawing is switched on """ + # if initialization wasn't triggered yet, then skip switching to stall and wait until it gets initialised + if _GROUP_NAME not in bpy.data.groups: + return + # if data was marked as updated if not _CACHE[_DATA_UP_TO_DATE]: # recalculate curves and lines whos locators were visiblly changed and force redraw diff --git a/addon/io_scs_tools/internals/containers/config.py b/addon/io_scs_tools/internals/containers/config.py index 62433b9..262264f 100644 --- a/addon/io_scs_tools/internals/containers/config.py +++ b/addon/io_scs_tools/internals/containers/config.py @@ -20,6 +20,7 @@ import bpy import os +import pickle from io_scs_tools.utils.printout import lprint from io_scs_tools.utils import path as _path_utils from io_scs_tools.utils import property as _property_utils @@ -29,6 +30,7 @@ from io_scs_tools.internals.containers import sii as _sii from io_scs_tools.internals.shader_presets import cache as _shader_presets_cache from io_scs_tools.internals.structure import SectionData as _SectionData +from io_scs_tools.operators.world import SCSPathsInitialization as _SCSPathsInitialization def update_item_in_file(item_pointer, new_value): @@ -83,11 +85,11 @@ def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_file :param shader_presets_filepath: Absolute or relative path to the file with Shader Presets :type shader_presets_filepath: str """ - # print('shader_presets_filepath: %r' % shader_presets_filepath) - if shader_presets_filepath.startswith("//"): # RELATIVE PATH - shader_presets_abs_path = _path_utils.get_abs_path(shader_presets_filepath) - else: - shader_presets_abs_path = shader_presets_filepath + shader_presets_abs_path = _path_utils.get_abs_path(shader_presets_filepath) + + if _shader_presets_cache.is_initilized(shader_presets_abs_path): + lprint("I Shader presets cache is up-to date, no update will happen!") + return # CLEAR INVENTORY AND CACHE scs_shader_presets_inventory.clear() @@ -103,6 +105,23 @@ def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_file # ADD ALL SHADER PRESET ITEMS FROM FILE INTO INVENTORY if presets_container: + # load all supported effects from dump file of python set or dictionary, where keys represent supported effects + # If file is not found then generate all combinations as before. + supported_effects_dict = None + supported_effects_path = os.path.join(_path_utils.get_addon_installation_paths()[0], "supported_effects.bin") + if os.path.isfile(supported_effects_path): + try: + supported_effects_dict = pickle.load(open(supported_effects_path, "rb")) + except PermissionError: + lprint("W Can't load supported effects file (persmission denied), please ensure read/write permissions for:\n\t %r\n\t " + "Without supported effects file invalid combinations of shader and flavors can be created!", + (os.path.dirname(supported_effects_path),), + report_warnings=1) + else: + lprint("W Supported effects file is missing! Make sure latest SCS Blender Tools is installed.\n\t " + "Without supported effects file invalid combinations of shader and flavors can be created!", + report_warnings=1) + # sort sections to shaders and flavors shaders = [] flavors = {} @@ -138,7 +157,7 @@ def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_file lprint("D Flavor used by shader preset, but not defined: %s", (flavor_type,)) continue - # create new flavor variant item + # create new flavor variant item (when flavor has more variants eg. "BLEND_ADD|BLEND_OVER") flavor_variant = flavor_item.variants.add() flavor_variant.name = flavors[flavor_type].get_prop_value("Name") flavor_variant.preset_name = new_shader_preset.name @@ -146,6 +165,16 @@ def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_file # modify and save section as string into cache for unique_name in unique_names: + new_unique_str = unique_name + "." + flavors[flavor_type].get_prop_value("Name") + full_effect_name = new_shader_preset.effect + new_unique_str + + # check if this shader-flavor combination can exists, if not skip it + if supported_effects_dict and full_effect_name not in supported_effects_dict: + lprint("S Marking none existing effect as dirty: %r", (full_effect_name,)) + is_dirty = True + else: + is_dirty = False + section = _shader_presets_cache.get_section(new_shader_preset, unique_name) for flavor_section in flavors[flavor_type].sections: @@ -165,12 +194,16 @@ def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_file else: section.sections.append(flavor_section) - new_unique_names.append(unique_name + "." + flavors[flavor_type].get_prop_value("Name")) - assert section.set_prop_value("Effect", new_shader_preset.effect + new_unique_names[-1]) - _shader_presets_cache.add_section(new_shader_preset, new_unique_names[-1], section) + new_unique_names.append(new_unique_str) + assert section.set_prop_value("Effect", new_shader_preset.effect + new_unique_str) + _shader_presets_cache.add_section(new_shader_preset, new_unique_str, section, is_dirty=is_dirty) unique_names.extend(new_unique_names) + # now as we built cache it's time to clean it up of dirty items (eg. none existing effect combinations) and + # set path from which this cache was initialized + _shader_presets_cache.set_initialized(shader_presets_abs_path) + update_item_in_file('Paths.ShaderPresetsFilePath', shader_presets_filepath) @@ -180,34 +213,32 @@ def update_trigger_actions_rel_path(scs_trigger_actions_inventory, trigger_actio :param trigger_actions_rel_path: Relative path to the directory with Trigger Action files :type trigger_actions_rel_path: str """ - trig_actions_path = _path_utils.get_abs_path(trigger_actions_rel_path) - if trig_actions_path: - if _get_scs_globals().trigger_actions_use_infixed: - trig_actions_paths = _path_utils.get_all_infixed_file_paths(trig_actions_path) - else: - trig_actions_paths = [trig_actions_path] + gathered_library_filepaths = _path_utils.get_abs_paths(trigger_actions_rel_path, + use_infixed_search=_get_scs_globals().trigger_actions_use_infixed) - # CLEAR INVENTORY - scs_trigger_actions_inventory.clear() + # CLEAR INVENTORY + scs_trigger_actions_inventory.clear() - for trig_actions_path in trig_actions_paths: + for trig_actions_path in gathered_library_filepaths: - trig_actions_container = _sii.get_data_from_file(trig_actions_path) - if trig_actions_container: + lprint("D Going to parse trigger actions file:\n\t %r", (trig_actions_path,)) - # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY - for item in trig_actions_container: - if item.type == 'trigger_action': - if item.id.startswith('trig_action.'): - if 'name' in item.props: - trg_action_name = item.props['name'] - else: - continue + trig_actions_container = _sii.get_data_from_file(trig_actions_path) + if trig_actions_container: - trig_item = scs_trigger_actions_inventory.add() - trig_item.name = trg_action_name + " : " + item.id[12:] - trig_item.item_id = item.id[12:] + # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY + for item in trig_actions_container: + if item.type == 'trigger_action': + if item.id.startswith('trig_action.'): + if 'name' in item.props: + trg_action_name = item.props['name'] + else: + continue + + trig_item = scs_trigger_actions_inventory.add() + trig_item.name = trg_action_name + " : " + item.id[12:] + trig_item.item_id = item.id[12:] if not readonly: update_item_in_file('Paths.TriggerActionsRelFilePath', trigger_actions_rel_path) @@ -219,47 +250,45 @@ def update_sign_library_rel_path(scs_sign_model_inventory, sign_library_rel_path :param sign_library_rel_path: Relative path to the directory with Sign files :type sign_library_rel_path: str """ - sign_library_filepath = _path_utils.get_abs_path(sign_library_rel_path) - if sign_library_filepath: - if _get_scs_globals().sign_library_use_infixed: - sign_library_filepaths = _path_utils.get_all_infixed_file_paths(sign_library_filepath) - else: - sign_library_filepaths = [sign_library_filepath] + gathered_library_filepaths = _path_utils.get_abs_paths(sign_library_rel_path, + use_infixed_search=_get_scs_globals().sign_library_use_infixed) - # CLEAR INVENTORY - scs_sign_model_inventory.clear() + # CLEAR INVENTORY + scs_sign_model_inventory.clear() - for sign_library_filepath in sign_library_filepaths: + for sign_library_filepath in gathered_library_filepaths: - sign_container = _sii.get_data_from_file(sign_library_filepath) - if sign_container: + lprint("D Going to parse sign library file:\n\t %r", (sign_library_filepath,)) - # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY - for item in sign_container: - if item.type == 'sign_model': - if item.id.startswith('sign.'): - if 'sign_name' in item.props: - sign_name = item.props['sign_name'] - else: - continue + sign_container = _sii.get_data_from_file(sign_library_filepath) + if sign_container: + + # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY + for item in sign_container: + if item.type == 'sign_model': + if item.id.startswith('sign.'): + if 'sign_name' in item.props: + sign_name = item.props['sign_name'] + else: + continue - sign_item = scs_sign_model_inventory.add() - sign_item.name = sign_name + " : " + item.id[5:] - sign_item.item_id = item.id[5:] + sign_item = scs_sign_model_inventory.add() + sign_item.name = sign_name + " : " + item.id[5:] + sign_item.item_id = item.id[5:] - if 'model_desc' in item.props: - sign_item.model_desc = item.props['model_desc'] + if 'model_desc' in item.props: + sign_item.model_desc = item.props['model_desc'] - if 'look_name' in item.props: - sign_item.look_name = item.props['look_name'] + if 'look_name' in item.props: + sign_item.look_name = item.props['look_name'] - if 'category' in item.props: - sign_item.category = item.props['category'] + if 'category' in item.props: + sign_item.category = item.props['category'] - if 'dynamic' in item.props: - if item.props['dynamic'] == 'true': - sign_item.dynamic = True + if 'dynamic' in item.props: + if item.props['dynamic'] == 'true': + sign_item.dynamic = True if not readonly: update_item_in_file('Paths.SignRelFilePath', sign_library_rel_path) @@ -272,37 +301,35 @@ def update_tsem_library_rel_path(scs_tsem_profile_inventory, tsem_library_rel_pa :param tsem_library_rel_path: Relative path to the directory with Traffic Semaphore Profile files :type tsem_library_rel_path: str """ - tsem_library_filepath = _path_utils.get_abs_path(tsem_library_rel_path) - if tsem_library_filepath: - if _get_scs_globals().tsem_library_use_infixed: - tsem_library_filepaths = _path_utils.get_all_infixed_file_paths(tsem_library_filepath) - else: - tsem_library_filepaths = [tsem_library_filepath] + gathered_library_filepaths = _path_utils.get_abs_paths(tsem_library_rel_path, + use_infixed_search=_get_scs_globals().tsem_library_use_infixed) - # CLEAR INVENTORY - scs_tsem_profile_inventory.clear() + # CLEAR INVENTORY + scs_tsem_profile_inventory.clear() - for tsem_library_filepath in tsem_library_filepaths: + for tsem_library_filepath in gathered_library_filepaths: - tsem_container = _sii.get_data_from_file(tsem_library_filepath) - if tsem_container: + lprint("D Going to parse tsem library file:\n\t %r", (tsem_library_filepath,)) - # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY - for item in tsem_container: - if item.type == 'tr_semaphore_profile': - if item.id.startswith('tr_sem_prof.'): - if 'name' in item.props: - tsem_name = item.props['name'] - else: - continue + tsem_container = _sii.get_data_from_file(tsem_library_filepath) + if tsem_container: + + # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY + for item in tsem_container: + if item.type == 'tr_semaphore_profile': + if item.id.startswith('tr_sem_prof.'): + if 'name' in item.props: + tsem_name = item.props['name'] + else: + continue - tsem_item = scs_tsem_profile_inventory.add() - tsem_item.name = tsem_name + " : " + item.id[12:] - tsem_item.item_id = item.id[12:] + tsem_item = scs_tsem_profile_inventory.add() + tsem_item.name = tsem_name + " : " + item.id[12:] + tsem_item.item_id = item.id[12:] - if 'model' in item.props: - tsem_item.model = item.props['model'][0] + if 'model' in item.props: + tsem_item.model = item.props['model'][0] if not readonly: update_item_in_file('Paths.TSemProfileRelFilePath', tsem_library_rel_path) @@ -314,35 +341,33 @@ def update_traffic_rules_library_rel_path(scs_traffic_rules_inventory, traffic_r :param traffic_rules_library_rel_path: Relative path to the directory with Traffic Rules files :type traffic_rules_library_rel_path: str """ - traffic_rules_library_filepath = _path_utils.get_abs_path(traffic_rules_library_rel_path) - if traffic_rules_library_filepath: - if _get_scs_globals().traffic_rules_library_use_infixed: - traffic_rules_library_filepaths = _path_utils.get_all_infixed_file_paths(traffic_rules_library_filepath) - else: - traffic_rules_library_filepaths = [traffic_rules_library_filepath] + gathered_library_filepaths = _path_utils.get_abs_paths(traffic_rules_library_rel_path, + use_infixed_search=_get_scs_globals().traffic_rules_library_use_infixed) + + # CLEAR INVENTORY + scs_traffic_rules_inventory.clear() - # CLEAR INVENTORY - scs_traffic_rules_inventory.clear() + for traffic_rules_library_filepath in gathered_library_filepaths: - for traffic_rules_library_filepath in traffic_rules_library_filepaths: + lprint("D Going to parse traffic rules file:\n\t %r", (traffic_rules_library_filepath,)) - trul_container = _sii.get_data_from_file(traffic_rules_library_filepath) - if trul_container: + trul_container = _sii.get_data_from_file(traffic_rules_library_filepath) + if trul_container: - # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY - for item in trul_container: - if item.type == 'traffic_rule_data': - if item.id.startswith('traffic_rule.'): - traffic_rule_item = scs_traffic_rules_inventory.add() - traffic_rule_item.name = item.id[13:] - # traffic_rule_item.item_id = item.id[13:] + # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY + for item in trul_container: + if item.type == 'traffic_rule_data': + if item.id.startswith('traffic_rule.'): + traffic_rule_item = scs_traffic_rules_inventory.add() + traffic_rule_item.name = item.id[13:] + # traffic_rule_item.item_id = item.id[13:] - if 'rule' in item.props: - traffic_rule_item.rule = item.props['rule'] + if 'rule' in item.props: + traffic_rule_item.rule = item.props['rule'] - if 'num_params' in item.props: - traffic_rule_item.num_params = str(item.props['num_params']) + if 'num_params' in item.props: + traffic_rule_item.num_params = str(item.props['num_params']) if not readonly: update_item_in_file('Paths.TrafficRulesRelFilePath', traffic_rules_library_rel_path) @@ -354,55 +379,69 @@ def update_hookup_library_rel_path(scs_hookup_inventory, hookup_library_rel_path :param hookup_library_rel_path: Relative path to the directory with Hookup files :type hookup_library_rel_path: str """ - abs_path = _path_utils.get_abs_path(hookup_library_rel_path, is_dir=True) - if abs_path: - - # CLEAR INVENTORY - scs_hookup_inventory.clear() - - # READ ALL "SII" FILES IN INVENTORY FOLDER - for root, dirs, files in os.walk(abs_path): - # print(' root: "%s"\n dirs: "%s"\n files: "%s"' % (root, dirs, files)) - for file in files: - if file.endswith(".sii"): - filepath = os.path.join(root, file) - # print(' filepath: "%s"' % str(filepath)) - hookup_container = _sii.get_data_from_file(filepath) - - # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY - if hookup_container: - for item in hookup_container: - # if item.type == 'sign_model': - if item.id.startswith('_'): - continue - else: - hookup_file = scs_hookup_inventory.add() - hookup_file.name = str(item.type + " : " + item.id) - hookup_file.item_id = item.id - - if 'model' in item.props: - # if model is defined as array ( appears if additional lod models are defined ) - # then use first none lod model - if isinstance(item.props['model'], type(list())): - hookup_file.model = item.props['model'][0] + + # CLEAR INVENTORY + scs_hookup_inventory.clear() + + taken_hoookup_ids = {} # temp dict for identifying unique hookups and preventing creation of duplicates (same type and id) + for abs_path in _path_utils.get_abs_paths(hookup_library_rel_path, is_dir=True): + + if abs_path: + + # READ ALL "SII" FILES IN INVENTORY FOLDER + for root, dirs, files in os.walk(abs_path): + + lprint("D Going to parse hookup directory:\n\t %r", (root,)) + + # print(' root: "%s"\n dirs: "%s"\n files: "%s"' % (root, dirs, files)) + for file in files: + if file.endswith(".sii"): + filepath = os.path.join(root, file) + # print(' filepath: "%s"' % str(filepath)) + hookup_container = _sii.get_data_from_file(filepath) + + # ADD ALL ITEMS FROM CONTAINER INTO INVENTORY + if hookup_container: + for item in hookup_container: + # if item.type == 'sign_model': + if item.id.startswith('_'): + continue + else: + typeid = str(item.type + " : " + item.id) + + # ignore taken type&ids + if typeid in taken_hoookup_ids: + continue else: - hookup_file.model = item.props['model'] + taken_hoookup_ids[typeid] = True + + hookup_file = scs_hookup_inventory.add() + hookup_file.name = typeid + hookup_file.item_id = item.id - if 'brand_idx' in item.props: - try: - hookup_file.brand_idx = int(item.props['brand_idx']) - except: - pass + if 'model' in item.props: + # if model is defined as array ( appears if additional lod models are defined ) + # then use first none lod model + if isinstance(item.props['model'], type(list())): + hookup_file.model = item.props['model'][0] + else: + hookup_file.model = item.props['model'] - if 'dir_type' in item.props: - hookup_file.dir_type = item.props['dir_type'] + if 'brand_idx' in item.props: + try: + hookup_file.brand_idx = int(item.props['brand_idx']) + except: + pass - if 'low_poly_only' in item.props: - if item.props['low_poly_only'] == 'true': - hookup_file.low_poly_only = True + if 'dir_type' in item.props: + hookup_file.dir_type = item.props['dir_type'] - if '.svn' in dirs: - dirs.remove('.svn') # ignore SVN + if 'low_poly_only' in item.props: + if item.props['low_poly_only'] == 'true': + hookup_file.low_poly_only = True + + if '.svn' in dirs: + dirs.remove('.svn') # ignore SVN if not readonly: update_item_in_file('Paths.HookupRelDirPath', hookup_library_rel_path) @@ -445,6 +484,60 @@ def update_matsubs_inventory(scs_matsubs_inventory, matsubs_library_rel_path, re update_item_in_file('Paths.MatSubsRelFilePath', matsubs_library_rel_path) +def update_sun_profiles_library_path(scs_sun_profiles_inventory, sun_profiles_library_path, readonly=False): + """The function deletes and populates again a list of sun profiles used for scs ligthning inside Blednder. + It also updates corresponding record in config file. + + :param scs_sun_profiles_inventory: sun profiles inventory from scs globals + :type scs_sun_profiles_inventory: bpy.types.CollectionProperty + :param sun_profiles_library_path: sun profiles library path (relative to SCS Project Base Path or absolute) + :type sun_profiles_library_path: str + :param readonly: flag indicating if path should be updated in config file or not + :type readonly: bool + """ + sun_profiles_lib_filepath = _path_utils.get_abs_path(sun_profiles_library_path) + + # CLEAR INVENTORY + scs_sun_profiles_inventory.clear() + + if sun_profiles_lib_filepath: + sun_profiles_container = _sii.get_data_from_file(sun_profiles_lib_filepath) + if sun_profiles_container: + + for item in sun_profiles_container: + if item.type == 'sun_profile': + + sun_profile_item = scs_sun_profiles_inventory.add() + sun_profile_item.name = item.id + + number_props = ("low_elevation", "high_elevation", "sun_direction", "ambient_hdr_coef", "diffuse_hdr_coef", + "specular_hdr_coef", "sun_color_hdr_coef", "env", "env_static_mod") + color_props = ("ambient", "diffuse", "specular", "sun_color") + + # fill in numeric sun profile props + for number_prop in number_props: + + item_value = item.get_prop_as_number(number_prop) + + if item_value is not None: + setattr(sun_profile_item, number_prop, item_value) + else: + lprint("E Property: %r could not be parsed! Sun profile loading is incomplete." % number_prop) + + # fill in color sun profile props + for color_prop in color_props: + + item_value = item.get_prop_as_color(color_prop) + + if item_value is not None: + setattr(sun_profile_item, color_prop, item_value) + else: + lprint("E Property: %r could not be parsed! Sun profile loading is incomplete." % color_prop) + + if not readonly: + update_item_in_file('Paths.SunProfilesFilePath', sun_profiles_library_path) + + def gather_default(): """Creates a new setting container for saving to the file.""" @@ -479,6 +572,7 @@ def fill_paths_section(): section.props.append(("HookupRelDirPath", _property_utils.get_by_type(bpy.types.GlobalSCSProps.hookup_library_rel_path))) section.props.append(("MatSubsRelFilePath", _property_utils.get_by_type(bpy.types.GlobalSCSProps.matsubs_library_rel_path))) section.props.append(("ConvertersPath", _property_utils.get_by_type(bpy.types.GlobalSCSProps.conv_hlpr_converters_path))) + section.props.append(("UseAlternativeBases", int(_property_utils.get_by_type(bpy.types.GlobalSCSProps.use_alternative_bases)))) return section def fill_import_section(): @@ -613,6 +707,32 @@ def get_config_filepath(): return scs_config_file +def engage_config_lock(): + """Engages configuration lock to prevent writing to config.txt. + + Should be always used in pair with release_config_lock. + + Should be used when applying multiple properties to scs globals at once, + as many of those propreties will try to update config file inside their update function. + But we don't want another config update as we are just applying properties from the config. + """ + _get_scs_globals().config_update_lock = True + + +def release_config_lock(use_paths_init_callback=False): + """Release configuration lock. + + Should be always used in pair with engage_config_lock. + + :param use_paths_init_callback: True if paths initialization is in progress and lock should be release when done; False release lock immidiately + :type use_paths_init_callback: bool + """ + if use_paths_init_callback: + _SCSPathsInitialization.append_callback(release_config_lock) + else: + _get_scs_globals().config_update_lock = False + + def apply_settings(): """Applies all the settings to the active scene.""" @@ -634,6 +754,7 @@ def apply_settings(): traffic_rules_library_rel_path = _property_utils.get_by_type(bpy.types.GlobalSCSProps.traffic_rules_library_rel_path, scs_globals) hookup_library_rel_path = _property_utils.get_by_type(bpy.types.GlobalSCSProps.hookup_library_rel_path, scs_globals) matsubs_library_rel_path = _property_utils.get_by_type(bpy.types.GlobalSCSProps.matsubs_library_rel_path, scs_globals) + sun_profiles_library_path = _property_utils.get_by_type(bpy.types.GlobalSCSProps.sun_profiles_lib_path, scs_globals) conv_hlpr_converters_path = _property_utils.get_by_type(bpy.types.GlobalSCSProps.conv_hlpr_converters_path, scs_globals) # NOTE: as dump level is written in same section as config type @@ -641,7 +762,9 @@ def apply_settings(): # so it has to be saved into variable and applied only if global settings are loaded from config file dump_level = scs_globals.dump_level - scs_globals.config_update_lock = True + # lock update now, as we don't want any properties update functions to trigger rewrite of config file + # which would lead to unwanted recursion + engage_config_lock() config_container = _pix.get_data_from_file(get_config_filepath(), " ") @@ -679,8 +802,12 @@ def apply_settings(): hookup_library_rel_path = prop[1] elif prop[0] == "MatSubsRelFilePath": matsubs_library_rel_path = prop[1] + elif prop[0] == "SunProfilesFilePath": + sun_profiles_library_path = prop[1] elif prop[0] == "ConvertersPath": conv_hlpr_converters_path = prop[1] + elif prop[0] == "UseAlternativeBases": + scs_globals.use_alternative_bases = prop[1] else: lprint('W Unrecognised item "%s" has been found in setting file! Skipping...', (str(prop[0]),)) elif section.type == "Import": @@ -815,16 +942,36 @@ def apply_settings(): # now as last apply all of the file paths # NOTE: applying paths is crucial for libraries - # (they are reloaded/initiated in property update functions) - scs_globals.scs_project_path = scs_project_path - scs_globals.shader_presets_filepath = shader_presets_filepath - scs_globals.trigger_actions_rel_path = trigger_actions_rel_path - scs_globals.sign_library_rel_path = sign_library_rel_path - scs_globals.tsem_library_rel_path = tsem_library_rel_path - scs_globals.traffic_rules_library_rel_path = traffic_rules_library_rel_path - scs_globals.hookup_library_rel_path = hookup_library_rel_path - scs_globals.matsubs_library_rel_path = matsubs_library_rel_path - scs_globals.conv_hlpr_converters_path = conv_hlpr_converters_path - - scs_globals.config_update_lock = False + # (they are reloaded/initiated in property update functions). + if bpy.app.background: # if blender runs without UI then apply libraries directly as async operator is UI depended + + scs_globals.scs_project_path = scs_project_path + scs_globals.shader_presets_filepath = shader_presets_filepath + scs_globals.trigger_actions_rel_path = trigger_actions_rel_path + scs_globals.sign_library_rel_path = sign_library_rel_path + scs_globals.tsem_library_rel_path = tsem_library_rel_path + scs_globals.traffic_rules_library_rel_path = traffic_rules_library_rel_path + scs_globals.hookup_library_rel_path = hookup_library_rel_path + scs_globals.matsubs_library_rel_path = matsubs_library_rel_path + scs_globals.sun_profiles_lib_path = sun_profiles_library_path + scs_globals.conv_hlpr_converters_path = conv_hlpr_converters_path + + else: # if blender is started normally use asynchronous operator to reload libraries + + bpy.ops.world.scs_paths_initialization('INVOKE_DEFAULT', paths_list=[ + {"name": "project base path", "attr": "scs_project_path", "path": scs_project_path}, + {"name": "shader presets", "attr": "shader_presets_filepath", "path": shader_presets_filepath}, + {"name": "trigger actions library", "attr": "trigger_actions_rel_path", "path": trigger_actions_rel_path}, + {"name": "sign library", "attr": "sign_library_rel_path", "path": sign_library_rel_path}, + {"name": "traffic semaphore library", "attr": "tsem_library_rel_path", "path": tsem_library_rel_path}, + {"name": "traffic rules library", "attr": "traffic_rules_library_rel_path", "path": traffic_rules_library_rel_path}, + {"name": "hookups library", "attr": "hookup_library_rel_path", "path": hookup_library_rel_path}, + {"name": "material substance library", "attr": "matsubs_library_rel_path", "path": matsubs_library_rel_path}, + {"name": "sun profiles library", "attr": "sun_profiles_lib_path", "path": sun_profiles_library_path}, + {"name": "converters file path", "attr": "conv_hlpr_converters_path", "path": conv_hlpr_converters_path}, + ]) + + # release lock as properties are applied + release_config_lock(use_paths_init_callback=not bpy.app.background) + return True diff --git a/addon/io_scs_tools/internals/icons/__init__.py b/addon/io_scs_tools/internals/icons/__init__.py index e5795f4..50f7e92 100644 --- a/addon/io_scs_tools/internals/icons/__init__.py +++ b/addon/io_scs_tools/internals/icons/__init__.py @@ -34,38 +34,34 @@ def init(): """Initialization function for getting hold of preview collection variable with already created custom icon objects. """ + # create new preview only once. + # NOTE: We removed previews cleanup from code + # because it was crashing Blender when rapidly enabling/disabling BT addon. + # So instead of always creating new preview collection we rather reuse existing and + # only load icons again with force reload flag, to ensure icons are always reloaded when init is called. if CUSTOM_ICONS not in _preview_collections: pcoll = previews.new() - - # load icons - tools_paths = _path.get_addon_installation_paths() - if len(tools_paths) > 0: - - for icon_type in _ICON_consts.Types.as_list(): - - # create path to current icon "ui/icons/icon_type" - icon_path = os.path.join(tools_paths[0], 'ui' + os.sep + 'icons' + os.sep + icon_type) - if os.path.isfile(icon_path): - if icon_type not in pcoll: - pcoll.load(icon_type, icon_path, 'IMAGE') - else: - lprint("W Icon %r is missing. Please try to install addon again!", (icon_type,)) - _preview_collections[CUSTOM_ICONS] = pcoll + else: -def cleanup(): - """Release custom icons internal data. This results in deleting of preview collections entries - and preview collections dictionary itself. - """ + pcoll = _preview_collections[CUSTOM_ICONS] + print("INFO\t - Icon collection is already in python memory, re-using it!") - if CUSTOM_ICONS in _preview_collections: + # load icons + tools_paths = _path.get_addon_installation_paths() + if len(tools_paths) > 0: - for pcoll in _preview_collections.values(): - previews.remove(pcoll) + for icon_type in _ICON_consts.Types.as_list(): - _preview_collections.clear() + # create path to current icon "ui/icons/icon_type" + icon_path = os.path.join(tools_paths[0], 'ui' + os.sep + 'icons' + os.sep + icon_type) + if os.path.isfile(icon_path): + if icon_type not in pcoll: + pcoll.load(icon_type, icon_path, 'IMAGE', force_reload=True) + else: + lprint("W Icon %r is missing. Please try to install addon again!", (icon_type,)) def get_icon(icon_type): diff --git a/addon/io_scs_tools/internals/open_gl/core.py b/addon/io_scs_tools/internals/open_gl/core.py index 7e2c9fa..e75a8c7 100644 --- a/addon/io_scs_tools/internals/open_gl/core.py +++ b/addon/io_scs_tools/internals/open_gl/core.py @@ -20,16 +20,18 @@ import bpy import blf -from bgl import (glColor3f, glPointSize, glLineWidth, glEnable, glDisable, glClear, glBegin, glEnd, glVertex3f, - GL_DEPTH_TEST, GL_DEPTH_BUFFER_BIT, GL_POLYGON) +from bgl import (glColor3f, glPointSize, glLineWidth, glEnable, glDisable, glClear, glBegin, glEnd, glVertex3f, glBindTexture, glTexCoord2f, + GL_DEPTH_TEST, GL_DEPTH_BUFFER_BIT, GL_POLYGON, GL_TEXTURE_2D, GL_BLEND, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) from mathutils import Vector from io_scs_tools.consts import PrefabLocators as _PL_consts +from io_scs_tools.consts import Operators as _OP_consts from io_scs_tools.internals import preview_models as _preview_models from io_scs_tools.internals.open_gl import locators as _locators from io_scs_tools.internals.open_gl import primitive as _primitive from io_scs_tools.internals.open_gl.storage import terrain_points as _terrain_points_storage from io_scs_tools.internals.connections.wrappers import group as _connections_group_wrapper from io_scs_tools.operators.wm import Show3DViewReport as _Show3DViewReportOperator +from io_scs_tools.utils import info as _info_utils from io_scs_tools.utils import math as _math_utils from io_scs_tools.utils import object as _object_utils from io_scs_tools.utils import get_scs_globals as _get_scs_globals @@ -414,42 +416,50 @@ def _draw_3dview_report(region): :param region: region of 3D viewport :type region: bpy.types.Region """ + pos = region.height - 65 + if _Show3DViewReportOperator.has_lines(): - blf.size(0, 15, 72) - pos = region.height - 40 + glEnable(GL_TEXTURE_2D) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glBindTexture(GL_TEXTURE_2D, _Show3DViewReportOperator.get_scs_logo_img_bindcode()) - # draw Close control - glColor3f(.2, .2, .2) + # draw BT logo glBegin(GL_POLYGON) - glVertex3f(20, pos + 2, 0) - glVertex3f(94, pos + 2, 0) - glVertex3f(94, pos - 22, 0) - glVertex3f(20, pos - 22, 0) + glColor3f(1, 1, 1) + glTexCoord2f(0, 1) + glVertex3f(_OP_consts.View3DReport.BT_LOGO_AREA[0], region.height - _OP_consts.View3DReport.BT_LOGO_AREA[2], 0) + glTexCoord2f(1, 1) + glVertex3f(_OP_consts.View3DReport.BT_LOGO_AREA[1], region.height - _OP_consts.View3DReport.BT_LOGO_AREA[2], 0) + glTexCoord2f(1, 0) + glVertex3f(_OP_consts.View3DReport.BT_LOGO_AREA[1], region.height - _OP_consts.View3DReport.BT_LOGO_AREA[3], 0) + glTexCoord2f(0, 0) + glVertex3f(_OP_consts.View3DReport.BT_LOGO_AREA[0], region.height - _OP_consts.View3DReport.BT_LOGO_AREA[3], 0) glEnd() - glColor3f(1, 1, 1) - blf.position(0, 25, pos - 15, 0) - blf.draw(0, "[X] Close") + glDisable(GL_TEXTURE_2D) - glColor3f(.2, .2, .2) - glBegin(GL_POLYGON) - glVertex3f(100, pos + 2, 0) - glVertex3f(250, pos + 2, 0) - glVertex3f(250, pos - 22, 0) - glVertex3f(100, pos - 22, 0) - glEnd() + # draw version string + blf.size(0, 11, 72) + glColor3f(.952, .635, .062) + blf.position(0, 20, pos, 0) + blf.draw(0, _info_utils.get_combined_ver_str(only_version_numbers=True)) + pos -= 20 - # draw Show/Hide control and actual reports if exists - glColor3f(1, 1, 1) - blf.position(0, 105, pos - 15, 0) + # draw actual operator title and message if shown if _Show3DViewReportOperator.is_shown(): - blf.draw(0, "[+] Show | [ ] Hide") - blf.enable(0, blf.SHADOW) blf.size(0, 12, 72) glColor3f(1, 1, 1) - pos -= 40 + blf.shadow(0, 5, 0, 0, 0, 1) + + if _Show3DViewReportOperator.get_title() != "": + blf.position(0, 20, pos, 0) + blf.draw(0, _Show3DViewReportOperator.get_title()) + pos -= 15 + + blf.enable(0, blf.SHADOW) for line in _Show3DViewReportOperator.get_lines(): # finish printing if running out of space @@ -466,6 +476,43 @@ def _draw_3dview_report(region): blf.draw(0, line) pos -= 15 + blf.disable(0, blf.SHADOW) - else: - blf.draw(0, "[ ] Show | [+] Hide") + + # draw control buttons if controls are enabled + if _Show3DViewReportOperator.has_controls(): + + # draw close button + glColor3f(.4, .4, .4) + glBegin(GL_POLYGON) + glVertex3f(_OP_consts.View3DReport.CLOSE_BTN_AREA[0], region.height - _OP_consts.View3DReport.CLOSE_BTN_AREA[2], 0) + glVertex3f(_OP_consts.View3DReport.CLOSE_BTN_AREA[1], region.height - _OP_consts.View3DReport.CLOSE_BTN_AREA[2], 0) + glVertex3f(_OP_consts.View3DReport.CLOSE_BTN_AREA[1], region.height - _OP_consts.View3DReport.CLOSE_BTN_AREA[3], 0) + glVertex3f(_OP_consts.View3DReport.CLOSE_BTN_AREA[0], region.height - _OP_consts.View3DReport.CLOSE_BTN_AREA[3], 0) + glEnd() + + # draw hide button + glBegin(GL_POLYGON) + glVertex3f(_OP_consts.View3DReport.HIDE_BTN_AREA[0], region.height - _OP_consts.View3DReport.HIDE_BTN_AREA[2], 0) + glVertex3f(_OP_consts.View3DReport.HIDE_BTN_AREA[1], region.height - _OP_consts.View3DReport.HIDE_BTN_AREA[2], 0) + glVertex3f(_OP_consts.View3DReport.HIDE_BTN_AREA[1], region.height - _OP_consts.View3DReport.HIDE_BTN_AREA[3], 0) + glVertex3f(_OP_consts.View3DReport.HIDE_BTN_AREA[0], region.height - _OP_consts.View3DReport.HIDE_BTN_AREA[3], 0) + glEnd() + + # gather texts and positions + close_btn_text_pos = _OP_consts.View3DReport.CLOSE_BTN_TEXT_POS[int(not _Show3DViewReportOperator.is_shown())] + close_btn_text = _OP_consts.View3DReport.CLOSE_BTN_TEXT[int(not _Show3DViewReportOperator.is_shown())] + + hide_btn_text_pos = _OP_consts.View3DReport.HIDE_BTN_TEXT_POS[int(not _Show3DViewReportOperator.is_shown())] + hide_btn_text = _OP_consts.View3DReport.HIDE_BTN_TEXT[int(not _Show3DViewReportOperator.is_shown())] + + blf.size(0, 15, 72) + + # draw close button text + glColor3f(1, 1, 1) + blf.position(0, close_btn_text_pos[0], region.height - close_btn_text_pos[1], 0) + blf.draw(0, close_btn_text) + + # draw hide button text + blf.position(0, hide_btn_text_pos[0], region.height - hide_btn_text_pos[1], 0) + blf.draw(0, hide_btn_text) diff --git a/addon/io_scs_tools/internals/persistent/file_load.py b/addon/io_scs_tools/internals/persistent/file_load.py index 5135a46..35e72ea 100644 --- a/addon/io_scs_tools/internals/persistent/file_load.py +++ b/addon/io_scs_tools/internals/persistent/file_load.py @@ -20,8 +20,8 @@ import bpy from bpy.app.handlers import persistent -from io_scs_tools import get_tools_version from io_scs_tools.internals import looks as _looks +from io_scs_tools.operators.world import SCSPathsInitialization as _SCSPathsInitialization from io_scs_tools.utils import material as _material_utils from io_scs_tools.utils import object as _object_utils from io_scs_tools.utils import info as _info_utils @@ -34,100 +34,153 @@ def post_load(scene): # get Blender Tools version from last blend file load last_load_bt_ver = _get_scs_globals().last_load_bt_version - scs_roots = None + if _info_utils.cmp_ver_str(last_load_bt_ver, "0.6") <= 0: + + # try to add apply fixed function as callback, if failed execute fixes right now + if not _SCSPathsInitialization.append_callback(apply_fixes_for_0_6): + apply_fixes_for_0_6() + + if _info_utils.cmp_ver_str(last_load_bt_ver, "1.4") <= 0: + + # try to add apply fixed function as callback, if failed execute fixes right now + if not _SCSPathsInitialization.append_callback(apply_fixes_for_1_4): + apply_fixes_for_1_4() + + # as last update "last load" Blender Tools version to current + _get_scs_globals().last_load_bt_version = _info_utils.get_tools_version() + +def apply_fixes_for_0_6(): """ Applies fixes for v0.6 or less: 1. fixes reflection textures tga's for tobjs as TOBJ load is now supported and unlock that textures 2. calls update on all set textures to correct paths for them 3. tries to fix active shader preset name for materials, because of new flavor system """ - if _info_utils.cmp_ver_str(last_load_bt_ver, "0.6") <= 0: - print("INFO\t- Applying fixes for version <= 0.6") + print("INFO\t- Applying fixes for version <= 0.6") + + scs_roots = None + + for material in bpy.data.materials: - for material in bpy.data.materials: + # ignore materials not related to blender tools + if material.scs_props.mat_effect_name == "": + continue - # ignore materials not related to blender tools - if material.scs_props.mat_effect_name == "": - continue + for tex_type in material.scs_props.get_texture_types().keys(): - for tex_type in material.scs_props.get_texture_types().keys(): + texture_attr_str = "shader_texture_" + tex_type + if texture_attr_str in material.scs_props.keys(): - texture_attr_str = "shader_texture_" + tex_type - if texture_attr_str in material.scs_props.keys(): + # 1. fix reflection textures + if tex_type == "reflection": - # 1. fix reflection textures - if tex_type == "reflection": + is_building_ref = material.scs_props[texture_attr_str].endswith("/bulding_ref.tga") + is_generic_s = material.scs_props[texture_attr_str].endswith("material/environment/generic_s.tga") + is_glass_interior = material.scs_props.active_shader_preset_name == "glass - interior" + is_dif_spec_weight_add_env = material.scs_props.active_shader_preset_name == "dif.spec.weight.add.env" + is_truckpaint = material.scs_props.active_shader_preset_name.startswith("truckpaint") - is_building_ref = material.scs_props[texture_attr_str].endswith("/bulding_ref.tga") - is_generic_s = material.scs_props[texture_attr_str].endswith("material/environment/generic_s.tga") - is_glass_interior = material.scs_props.active_shader_preset_name == "glass - interior" - is_dif_spec_weight_add_env = material.scs_props.active_shader_preset_name == "dif.spec.weight.add.env" - is_truckpaint = material.scs_props.active_shader_preset_name.startswith("truckpaint") + # fix paths + if is_building_ref: + material.scs_props[texture_attr_str] = material.scs_props[texture_attr_str][:-4] + material.scs_props[texture_attr_str + "_locked"] = False + elif is_generic_s: + if is_glass_interior: + material.scs_props[texture_attr_str] = "//material/environment/interior_reflection" + elif is_dif_spec_weight_add_env: + material.scs_props[texture_attr_str] = "//material/environment/generic_reflection" + else: + material.scs_props[texture_attr_str] = "//material/environment/vehicle_reflection" - # fix paths - if is_building_ref: - material.scs_props[texture_attr_str] = material.scs_props[texture_attr_str][:-4] + # unlock reflection textures everywhere except on truckpaint shader + if not is_truckpaint: material.scs_props[texture_attr_str + "_locked"] = False - elif is_generic_s: - if is_glass_interior: - material.scs_props[texture_attr_str] = "//material/environment/interior_reflection" - elif is_dif_spec_weight_add_env: - material.scs_props[texture_attr_str] = "//material/environment/generic_reflection" - else: - material.scs_props[texture_attr_str] = "//material/environment/vehicle_reflection" - - # unlock reflection textures everywhere except on truckpaint shader - if not is_truckpaint: - material.scs_props[texture_attr_str + "_locked"] = False - - # acquire roots on demand only once - scs_roots = _object_utils.gather_scs_roots(bpy.data.objects) if not scs_roots else scs_roots - - # propagate reflection texture change on all of the looks. - # NOTE: We can afford write through because old BT had all reflection textures locked - # meaning user had to use same texture on all looks - # NOTE#2: Printouts like: - # "Look with ID: X doesn't have entry for material 'X' in SCS Root 'X', - # property 'shader_texture_reflection' won't be updated!" - # are expected here, because we don't use any safety check, - # if material is used on the mesh objects inside scs root - for scs_root in scs_roots: - _looks.write_through(scs_root, material, texture_attr_str) - - # 2. trigger update function for path reload and reload of possible missing textures - update_func = getattr(material.scs_props, "update_" + texture_attr_str, None) - if update_func: - update_func(material) - - # ignore already properly set materials - if material.scs_props.active_shader_preset_name in _get_shader_presets_inventory(): - continue - - # 3. try to recover "active_shader_preset_name" from none flavor times Blender Tools - material_textures = {} - if "scs_shader_attributes" in material and "textures" in material["scs_shader_attributes"]: - for texture in material["scs_shader_attributes"]["textures"].values(): - tex_id = texture["Tag"].split(":")[1] - tex_value = texture["Value"] - material_textures[tex_id] = tex_value - - (preset_name, preset_section) = _material_utils.find_preset(material.scs_props.mat_effect_name, material_textures) - if preset_name: - material.scs_props.active_shader_preset_name = preset_name - - # acquire roots on demand only once - scs_roots = _object_utils.gather_scs_roots(bpy.data.objects) if not scs_roots else scs_roots - - # make sure to fix active preset shader name in all looks - # NOTE: Printouts like: - # "Look with ID: X doesn't have entry for material 'X' in SCS Root 'X', - # property 'active_shader_preset_name' won't be updated!" - # are expected here, because we don't use any safety check, - # if material is used on the mesh objects inside scs root - for scs_root in scs_roots: - _looks.write_through(scs_root, material, "active_shader_preset_name") - # as last update "last load" Blender Tools version to current - _get_scs_globals().last_load_bt_version = get_tools_version() + # acquire roots on demand only once + scs_roots = _object_utils.gather_scs_roots(bpy.data.objects) if not scs_roots else scs_roots + + # propagate reflection texture change on all of the looks. + # NOTE: We can afford write through because old BT had all reflection textures locked + # meaning user had to use same texture on all looks + # NOTE#2: Printouts like: + # "Look with ID: X doesn't have entry for material 'X' in SCS Root 'X', + # property 'shader_texture_reflection' won't be updated!" + # are expected here, because we don't use any safety check, + # if material is used on the mesh objects inside scs root + for scs_root in scs_roots: + _looks.write_through(scs_root, material, texture_attr_str) + + # 2. trigger update function for path reload and reload of possible missing textures + update_func = getattr(material.scs_props, "update_" + texture_attr_str, None) + if update_func: + update_func(material) + + # ignore already properly set materials + if material.scs_props.active_shader_preset_name in _get_shader_presets_inventory(): + continue + + # 3. try to recover "active_shader_preset_name" from none flavor times Blender Tools + material_textures = {} + if "scs_shader_attributes" in material and "textures" in material["scs_shader_attributes"]: + for texture in material["scs_shader_attributes"]["textures"].values(): + tex_id = texture["Tag"].split(":")[1] + tex_value = texture["Value"] + material_textures[tex_id] = tex_value + + (preset_name, preset_section) = _material_utils.find_preset(material.scs_props.mat_effect_name, material_textures) + if preset_name: + material.scs_props.active_shader_preset_name = preset_name + + # acquire roots on demand only once + scs_roots = _object_utils.gather_scs_roots(bpy.data.objects) if not scs_roots else scs_roots + + # make sure to fix active preset shader name in all looks + # NOTE: Printouts like: + # "Look with ID: X doesn't have entry for material 'X' in SCS Root 'X', + # property 'active_shader_preset_name' won't be updated!" + # are expected here, because we don't use any safety check, + # if material is used on the mesh objects inside scs root + for scs_root in scs_roots: + _looks.write_through(scs_root, material, "active_shader_preset_name") + + +def apply_fixes_for_1_4(): + """ + Applies fixes for v1.4 or less: + 1. reload all materials to fixup any new materials nodes eg. normal map node + 2. remove any obosolete hidden normal map materials: ".scs_nmap_X" + """ + + print("INFO\t- Applying fixes for version <= 1.4") + + # 1. reload all materials + bpy.ops.material.scs_reload_nodes('INVOKE_DEFAULT') + + # 2. remove all obsolete ".scs_nmap_" + str(i) materials, as of 2.78 we are using new normal maps node + i = 1 + while ".scs_nmap_" + str(i) in bpy.data.materials: + + material = bpy.data.materials[".scs_nmap_" + str(i)] + + # remove and clear if possible + if material.users == 0: + + textures = {} + # gather all used textures in this material + for j, tex_slot in enumerate(material.texture_slots): + if tex_slot and tex_slot.texture: + textures[j] = tex_slot.texture + + # remove textures from texture slots first and check if texture can be cleared + for slot_i in textures.keys(): + material.texture_slots.clear(slot_i) + + if textures[slot_i].users == 0: + bpy.data.textures.remove(textures[slot_i], do_unlink=True) + + # as last delete actually nmap material + bpy.data.materials.remove(material, do_unlink=True) + + i += 1 diff --git a/addon/io_scs_tools/internals/persistent/file_save.py b/addon/io_scs_tools/internals/persistent/file_save.py index eabf07e..6c11543 100644 --- a/addon/io_scs_tools/internals/persistent/file_save.py +++ b/addon/io_scs_tools/internals/persistent/file_save.py @@ -34,14 +34,19 @@ def pre_save(scene): scs_globals.scs_traffic_rules_inventory.clear() scs_globals.scs_tsem_profile_inventory.clear() scs_globals.scs_trigger_actions_inventory.clear() + scs_globals.sun_profiles_inventory.clear() - # clear unused materials, this has to be done because of usage of same material inside nodes + # clear unused materials with user count 1, this has to be done because of usage of same material inside nodes + materials_to_remove = set() for material in bpy.data.materials: if material.node_tree and material.users == 1: for node in material.node_tree.nodes: if node.type in ("MATERIAL_EXT", "MATERIAL"): if node.material == material: - material.user_clear() + materials_to_remove.add(material.name) + + for mat_name in materials_to_remove: + bpy.data.materials.remove(bpy.data.materials[mat_name], do_unlink=True) # make sure to save actions used in at least one scs game object for obj in bpy.data.objects: @@ -86,3 +91,8 @@ def post_save(scene): scs_globals.trigger_actions_rel_path, readonly ) + _config_container.update_sun_profiles_library_path( + scs_globals.sun_profiles_inventory, + scs_globals.sun_profiles_lib_path, + readonly + ) diff --git a/addon/io_scs_tools/internals/persistent/initialization.py b/addon/io_scs_tools/internals/persistent/initialization.py index a1ef3fa..1022377 100644 --- a/addon/io_scs_tools/internals/persistent/initialization.py +++ b/addon/io_scs_tools/internals/persistent/initialization.py @@ -21,12 +21,12 @@ import bpy import os from bpy.app.handlers import persistent -from io_scs_tools import get_tools_version from io_scs_tools.internals import preview_models as _preview_models from io_scs_tools.internals.callbacks import open_gl as _open_gl_callback from io_scs_tools.internals.containers import config as _config_container from io_scs_tools.internals.connections.wrappers import group as _connections_group_wrapper from io_scs_tools.utils import get_scs_globals as _get_scs_globals +from io_scs_tools.utils import info as _info_utils from io_scs_tools.utils.printout import lprint @@ -50,7 +50,7 @@ def initialise_scs_dict(scene): # SCREEN CHECK... if bpy.context.screen: - lprint("I Initialization of SCS scene, BT version: " + get_tools_version()) + lprint("I Initialization of SCS scene, BT version: " + _info_utils.get_tools_version()) # NOTE: covers: start-up, reload, enable/disable and it should be immediately removed # from handlers as soon as it's executed for the first time @@ -60,6 +60,11 @@ def initialise_scs_dict(scene): # INITIALIZE CUSTOM CONNECTIONS DRAWING SYSTEM _connections_group_wrapper.init() + # release lock as user might saved blender file during engaged lock. + # If that happens config lock property gets saved to blend file and if user opens that file again, + # lock will be still engaged and no settings could be applied without releasing lock here. + _config_container.release_config_lock() + # USE SETTINGS FROM CONFIG... # NOTE: Reapplying the settings from config file to the currently opened Blender file datablock. # The thing is, that every Blend file holds its own copy of SCS Global Settings from the machine on which it got saved. @@ -78,3 +83,29 @@ def initialise_scs_dict(scene): # ADD DRAW HANDLERS _open_gl_callback.enable(mode=_get_scs_globals().drawing_mode) + + # as last notify user if his Blender version is outdated + if not _info_utils.is_blender_able_to_run_tools(): + + message = "Your Blender version %s is outdated, all SCS Blender Tools functionalities were internally disabled.\n\t " \ + "Please update Blender before continue, minimal required version for SCS Blender Tools is: %s!" + message = message % (_info_utils.get_blender_version()[0], _info_utils.get_required_blender_version()) + + # first report error with blender tools printing system + lprint("E " + message) + + # then disable add-on as it's not usable in the case Blender is out-dated + bpy.ops.wm.addon_disable('INVOKE_DEFAULT', module="io_scs_tools") + + # and as last show warning message in the form of popup menu for user to see info about outdated Blender + # As we don't have access to our 3D view report operator anymore, + # we have to register our ShowWarningMessage class back and invoke it. + from io_scs_tools.operators.wm import ShowWarningMessage + bpy.utils.register_class(ShowWarningMessage) + + bpy.ops.wm.show_warning_message('INVOKE_DEFAULT', + is_modal=True, + title="SCS Blender Tools Initialization Problem", + message="\n\n" + message.replace("\t ", "") + "\n\n", # some nasty formatting for better visibility + width=580, # this is minimal width to properly fit in given message + height=bpy.context.window.height if bpy.context and bpy.context.window else 200) diff --git a/addon/io_scs_tools/internals/preview_models/__init__.py b/addon/io_scs_tools/internals/preview_models/__init__.py index 771a4fa..e26bb1e 100644 --- a/addon/io_scs_tools/internals/preview_models/__init__.py +++ b/addon/io_scs_tools/internals/preview_models/__init__.py @@ -94,7 +94,7 @@ def unlink(preview_model): _cache.delete_entry(preview_model.name) bpy.context.scene.objects.unlink(preview_model) - bpy.data.objects.remove(preview_model) + bpy.data.objects.remove(preview_model, do_unlink=True) bpy.context.scene.scs_cached_num_objects = len(bpy.context.scene.objects) @@ -166,7 +166,7 @@ def unload(locator): _cache.delete_entry(child.name) bpy.context.scene.objects.unlink(child) - bpy.data.objects.remove(child) + bpy.data.objects.remove(child, do_unlink=True) # update scene children count to prevent delete to be triggered bpy.context.scene.scs_cached_num_objects = len(bpy.context.scene.objects) diff --git a/addon/io_scs_tools/internals/shader_presets/cache.py b/addon/io_scs_tools/internals/shader_presets/cache.py index 39deac8..3e5f04a 100644 --- a/addon/io_scs_tools/internals/shader_presets/cache.py +++ b/addon/io_scs_tools/internals/shader_presets/cache.py @@ -20,8 +20,12 @@ import pickle +from os.path import getmtime, isfile +__PATH_DICT_KEY = "initialized_filepath" +__cache_presets_path = {} # storing last initialized shader presets file path, to avoid multiple initializing of same path __cache_dict = {} # storing all combinations of shader presets and it's flavors +__dirty_items = [] # storing dirty combinations of shader presets and it's flavors that can be cleared with cleanup function def clear(): @@ -34,9 +38,24 @@ def clear(): __cache_dict[key].clear() __cache_dict.clear() + __cache_presets_path.clear() + __dirty_items.clear() -def add_section(inventory_item, flavors_str, section): +def cleanup(): + """Cleanup dirty sections from cache. + + Any sections added with dirty flag, will now be removed from cache. Additional dirty items list is also cleared, + so if this function is called two times in a row, second time will be for nothing. + """ + + for effect, item_name, flavor_str in __dirty_items: + del __cache_dict[effect][item_name][flavor_str] + + __dirty_items.clear() + + +def add_section(inventory_item, flavors_str, section, is_dirty=False): """Adds section for current shader presets inventory item to the cache. :param inventory_item: shader presets item for which given section should be stored @@ -45,8 +64,8 @@ def add_section(inventory_item, flavors_str, section): :type flavors_str: str :param section: Shader section that should be stored :type section: io_scs_tools.internals.structure.SectionData - :return: - :rtype: + :param is_dirty: mark this section as dirty, set to true when inserting section only for time beeing of cache creation + :type is_dirty: bool """ if inventory_item.effect not in __cache_dict: @@ -57,6 +76,26 @@ def add_section(inventory_item, flavors_str, section): __cache_dict[inventory_item.effect][inventory_item.name][flavors_str] = pickle.dumps(section) + if is_dirty: + __dirty_items.append((inventory_item.effect, inventory_item.name, flavors_str)) + + +def has_section(inventory_item, flavors_str): + """Is shader data section for given inventory item and flavor string existing in shader presets cache? + + :param inventory_item: shader presets item for which should contain section with given flavors combination + :type inventory_item: io_scs_tools.properties.world.ShaderPresetsInventoryItem + :param flavors_str: flavors part of effect name + :type flavors_str: str + :return: True if section exists; otherwise False + :rtype: bool + """ + return ( + inventory_item.effect in __cache_dict and + inventory_item.name in __cache_dict[inventory_item.effect] and + flavors_str in __cache_dict[inventory_item.effect][inventory_item.name] + ) + def get_section(inventory_item, flavors_str): """Get section from shader presets cache for given inventory item and flavor string @@ -100,3 +139,33 @@ def find_sections(base_effect, flavors_str): found_sections.append(pickle.loads(stored_preset[flavors_str])) return found_sections + + +def set_initialized(path): + """Cleanup dirty entries and set shader presets cache as initialized for given path. + Should be called once all possible sections were added to cache for given path. + + :param path: path for which this cache was built + :type path: str + """ + + cleanup() + + __cache_presets_path[__PATH_DICT_KEY] = (path, getmtime(path)) + + +def is_initilized(path): + """Tells if shader preset cache was initilized for given path. + + It also takes in consideration if shader presets file on given path was modified after + cache was set as initilized. + + :param path: + :type path: + :return: True if cache was built upon given path; False if cache wasn't set as initilized for given path; + :rtype: bool + """ + return (__PATH_DICT_KEY in __cache_presets_path and + __cache_presets_path[__PATH_DICT_KEY][0] == path and + isfile(__cache_presets_path[__PATH_DICT_KEY][0]) and + __cache_presets_path[__PATH_DICT_KEY][1] >= getmtime(path)) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py index 6a0cc0b..43e9a28 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py @@ -21,6 +21,7 @@ from mathutils import Color from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.std_node_groups import compose_lighting from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input from io_scs_tools.internals.shaders.flavors import alpha_test from io_scs_tools.internals.shaders.flavors import blend_over @@ -42,6 +43,7 @@ class Dif: VCOLOR_MULT_NODE = "VertexColorMultiplier" VCOLOR_SCALE_NODE = "VertexColorScale" OUT_MAT_NODE = "InputMaterial" + COMPOSE_LIGHTING_NODE = "ComposeLighting" OUTPUT_NODE = "Output" @staticmethod @@ -128,10 +130,16 @@ def init(node_tree): out_mat_n.inputs['Reflectivity'].default_value = 1.0 out_mat_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1800) + compose_lighting_n = node_tree.nodes.new("ShaderNodeGroup") + compose_lighting_n.name = Dif.COMPOSE_LIGHTING_NODE + compose_lighting_n.label = Dif.COMPOSE_LIGHTING_NODE + compose_lighting_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2000) + compose_lighting_n.node_tree = compose_lighting.get_node_group() + output_n = node_tree.nodes.new("ShaderNodeOutput") output_n.name = Dif.OUTPUT_NODE output_n.label = Dif.OUTPUT_NODE - output_n.location = (start_pos_x + + pos_x_shift * 8, start_pos_y + 1800) + output_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y + 1800) # links creation node_tree.links.new(base_tex_n.inputs['Vector'], geometry_n.outputs['UV']) @@ -148,7 +156,10 @@ def init(node_tree): node_tree.links.new(out_mat_n.inputs['Color'], diff_mult_n.outputs['Color']) node_tree.links.new(out_mat_n.inputs['Spec'], spec_col_n.outputs['Color']) - node_tree.links.new(output_n.inputs['Color'], out_mat_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], diff_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Material Color'], out_mat_n.outputs['Color']) + + node_tree.links.new(output_n.inputs['Color'], compose_lighting_n.outputs['Composed Color']) node_tree.links.new(output_n.inputs['Alpha'], out_mat_n.outputs['Alpha']) @staticmethod @@ -166,8 +177,6 @@ def set_material(node_tree, material): # make sure to reset to lambert always as flat flavor might use fresnel diffuse shader material.diffuse_shader = "LAMBERT" - material.emit = 0.02 - @staticmethod def set_add_ambient(node_tree, factor): """Set ambient factor to shader. @@ -178,12 +187,7 @@ def set_add_ambient(node_tree, factor): :type factor: float """ - if factor == 0: - factor = 0.1 - - diffuse_col = Color(node_tree.nodes[Dif.DIFF_COL_NODE].outputs['Color'].default_value[:3]) - # NOTE: because emit works upon diffuse light we need to fake factors if diffuse drops - node_tree.nodes[Dif.OUT_MAT_NODE].material.emit = (factor / 10) * (1 / diffuse_col.v) + node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs["AddAmbient"].default_value = factor @staticmethod def set_diffuse(node_tree, color): @@ -198,18 +202,8 @@ def set_diffuse(node_tree, color): color = _convert_utils.to_node_color(color) node_tree.nodes[Dif.DIFF_COL_NODE].outputs['Color'].default_value = color - - # in the case of flat flavor (diffuse shader is set to FRESNEL) - # intensity of diffuse has to be 1 for correct results - if node_tree.nodes[Dif.OUT_MAT_NODE].material.diffuse_shader == "FRESNEL": - node_tree.nodes[Dif.OUT_MAT_NODE].material.diffuse_intensity = 1 - else: - node_tree.nodes[Dif.OUT_MAT_NODE].material.diffuse_intensity = Color(color[:3]).v * 0.7 - - # fix emit color representing ambient. - # NOTE: because emit works upon diffuse light we need to fake factors if diffuse drops - ambient = node_tree.nodes[Dif.OUT_MAT_NODE].material.scs_props.shader_attribute_add_ambient - node_tree.nodes[Dif.OUT_MAT_NODE].material.emit = (ambient / 10) * (1 / Color(color[:3]).v) + # fix intensity each time if user might changed it by hand directly on material + node_tree.nodes[Dif.OUT_MAT_NODE].material.diffuse_intensity = 0.7 @staticmethod def set_specular(node_tree, color): @@ -453,6 +447,7 @@ def set_flat_flavor(node_tree, switch_on): _FLAT_FAC_MULT_NODE = "FlatFlavorMult" out_mat_n = node_tree.nodes[Dif.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[Dif.OUTPUT_NODE] diff_mult_n = node_tree.nodes[Dif.DIFF_MULT_NODE] @@ -474,6 +469,7 @@ def set_flat_flavor(node_tree, switch_on): node_tree.links.new(flat_mult_n.inputs['Color1'], diff_mult_n.outputs['Color']) node_tree.links.new(out_mat_n.inputs['Color'], flat_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Material Color'], flat_mult_n.outputs['Color']) else: @@ -487,6 +483,7 @@ def set_flat_flavor(node_tree, switch_on): node_tree.nodes.remove(node_tree.nodes[_FLAT_FAC_MULT_NODE]) node_tree.links.new(out_mat_n.inputs['Color'], diff_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Material Color'], diff_mult_n.outputs['Color']) @staticmethod def set_paint_flavor(node_tree, switch_on): diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py index d6c40d6..166b1f7 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py @@ -47,7 +47,7 @@ def init(node_tree): Dif.init(node_tree) output_n = node_tree.nodes[Dif.OUTPUT_NODE] - out_mat_n = node_tree.nodes[Dif.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] base_tex_n = node_tree.nodes[Dif.BASE_TEX_NODE] # move existing @@ -57,12 +57,12 @@ def init(node_tree): lum_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") lum_mix_n.name = DifLum.LUM_MIX_NODE lum_mix_n.label = DifLum.LUM_MIX_NODE - lum_mix_n.location = (out_mat_n.location.x + pos_x_shift, out_mat_n.location.y + 200) + lum_mix_n.location = (compose_lighting_n.location.x + pos_x_shift, compose_lighting_n.location.y - 100) lum_mix_n.blend_type = "MIX" # links creation node_tree.links.new(lum_mix_n.inputs['Fac'], base_tex_n.outputs['Value']) - node_tree.links.new(lum_mix_n.inputs['Color1'], out_mat_n.outputs['Color']) + node_tree.links.new(lum_mix_n.inputs['Color1'], compose_lighting_n.outputs['Composed Color']) node_tree.links.new(lum_mix_n.inputs['Color2'], base_tex_n.outputs['Color']) node_tree.links.new(output_n.inputs['Color'], lum_mix_n.outputs['Color']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py index 1c3e115..270f666 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py @@ -49,7 +49,7 @@ def init(node_tree): DifSpec.init(node_tree) output_n = node_tree.nodes[DifSpec.OUTPUT_NODE] - out_mat_n = node_tree.nodes[DifSpec.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] base_tex_n = node_tree.nodes[DifSpec.BASE_TEX_NODE] spec_col_n = node_tree.nodes[DifSpec.SPEC_COL_NODE] @@ -63,13 +63,13 @@ def init(node_tree): lum_boost_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") lum_boost_mix_n.name = lum_boost_mix_n.label = DifLumSpec.LUM_BOOST_MIX_NODE - lum_boost_mix_n.location = (out_mat_n.location.x, out_mat_n.location.y + 300) + lum_boost_mix_n.location = (compose_lighting_n.location.x, compose_lighting_n.location.y + 200) lum_boost_mix_n.blend_type = "MULTIPLY" lum_boost_mix_n.inputs["Fac"].default_value = 1.0 lum_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") lum_mix_n.name = lum_mix_n.label = DifLumSpec.LUM_MIX_NODE - lum_mix_n.location = (out_mat_n.location.x + pos_x_shift, out_mat_n.location.y + 200) + lum_mix_n.location = (compose_lighting_n.location.x + pos_x_shift, compose_lighting_n.location.y + 100) lum_mix_n.blend_type = "MIX" # links creation @@ -77,7 +77,7 @@ def init(node_tree): node_tree.links.new(lum_boost_mix_n.inputs['Color2'], base_tex_n.outputs['Color']) node_tree.links.new(lum_mix_n.inputs['Fac'], base_tex_n.outputs['Color']) - node_tree.links.new(lum_mix_n.inputs['Color1'], out_mat_n.outputs['Color']) + node_tree.links.new(lum_mix_n.inputs['Color1'], compose_lighting_n.outputs['Composed Color']) node_tree.links.new(lum_mix_n.inputs['Color2'], lum_boost_mix_n.outputs['Color']) node_tree.links.new(output_n.inputs['Color'], lum_mix_n.outputs['Color']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env/__init__.py index 30b1222..a73ac15 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env/__init__.py @@ -44,7 +44,7 @@ def init(node_tree): DifSpec.SPEC_COL_NODE, DifSpec.BASE_TEX_NODE, DifSpec.OUT_MAT_NODE, - DifSpec.OUTPUT_NODE) + DifSpec.COMPOSE_LIGHTING_NODE) @staticmethod def set_indenv_flavor(node_tree, switch_on): diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_nmap.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_nmap.py index 01573c5..b94421c 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_nmap.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_nmap.py @@ -21,7 +21,7 @@ import bpy from io_scs_tools.internals.shaders.flavors import nmap -DET_NMAP_MAT_NODE = "DetailNormalMapMat" +DET_NMAP_NODE = "DetailNormalMapMat" DET_NMAP_TEX_NODE = "DetailNMapTex" DET_NMAP_SCALE_GNODE = "DetailNMapScaleGroup" DET_NMAP_UV_SCALE_NODE = "DetailNMapUVScale" @@ -59,12 +59,12 @@ def __create_nodes__(node_tree, location, uv_scale_from, det_nmap_strength_from, det_nmap_tex_n.name = det_nmap_tex_n.label = DET_NMAP_TEX_NODE det_nmap_tex_n.location = (location[0] - 185, location[1] - 600) - det_nmap_mat_n = node_tree.nodes.new("ShaderNodeMaterial") - det_nmap_mat_n.parent = frame - det_nmap_mat_n.name = det_nmap_mat_n.label = DET_NMAP_MAT_NODE - det_nmap_mat_n.location = (location[0], location[1] - 800) - det_nmap_mat_n.use_diffuse = False - det_nmap_mat_n.use_specular = False + det_nmap_n = node_tree.nodes.new("ShaderNodeNormalMap") + det_nmap_n.parent = frame + det_nmap_n.name = det_nmap_n.label = DET_NMAP_NODE + det_nmap_n.location = (location[0], location[1] - 800) + det_nmap_n.space = "TANGENT" + det_nmap_n.inputs["Strength"].default_value = 1 det_nmap_scale_gn = node_tree.nodes.new("ShaderNodeGroup") det_nmap_scale_gn.parent = frame @@ -118,29 +118,32 @@ def __create_nodes__(node_tree, location, uv_scale_from, det_nmap_strength_from, node_tree.links.new(det_nmap_tex_n.inputs['Vector'], det_nmap_uv_scale_n.outputs['Color']) # pass 3 + node_tree.links.new(det_nmap_n.inputs['Color'], det_nmap_tex_n.outputs['Color']) + + # pass 4 node_tree.links.new(det_nmap_scale_gn.inputs['NMap Tex Color'], det_nmap_tex_n.outputs['Color']) node_tree.links.new(det_nmap_scale_gn.inputs['Original Normal'], nodes[nmap.NMAP_GEOM_NODE].outputs['Normal']) - node_tree.links.new(det_nmap_scale_gn.inputs['Modified Normal'], det_nmap_mat_n.outputs['Normal']) + node_tree.links.new(det_nmap_scale_gn.inputs['Modified Normal'], det_nmap_n.outputs['Normal']) - # pass 4 + # pass 5 node_tree.links.new(det_nmap_strength_n.inputs['Color1'], det_nmap_strength_from) node_tree.links.new(det_nmap_strength_n.inputs['Color2'], det_nmap_scale_gn.outputs['Normal']) - # pass 5 + # pass 6 node_tree.links.new(det_nmap_mix_n.inputs['Color1'], nodes[nmap.NMAP_SCALE_GNODE].outputs['Normal']) node_tree.links.new(det_nmap_mix_n.inputs['Color2'], det_nmap_strength_n.outputs['Color']) - # pass 6 + # pass 7 node_tree.links.new(det_nmap_sep_n.inputs['Image'], det_nmap_mix_n.outputs['Color']) node_tree.links.new(nmap_sep_n.inputs['Image'], nodes[nmap.NMAP_SCALE_GNODE].outputs['Normal']) - # pass 7 + # pass 8 node_tree.links.new(nmap_det_nmap_combine_n.inputs['R'], det_nmap_sep_n.outputs['R']) node_tree.links.new(nmap_det_nmap_combine_n.inputs['G'], det_nmap_sep_n.outputs['G']) node_tree.links.new(nmap_det_nmap_combine_n.inputs['B'], nmap_sep_n.outputs['B']) - # pass 8 + # pass 9 node_tree.links.new(nmap_normalize_n.inputs[0], nmap_det_nmap_combine_n.outputs['Image']) node_tree.links.new(normal_to, nmap_normalize_n.outputs['Vector']) @@ -202,41 +205,6 @@ def set_detail_texture(node_tree, texture): # assign texture to texture node first node_tree.nodes[DET_NMAP_TEX_NODE].texture = texture - # search possible existing materials and use it - material = None - i = 1 - while ".scs_nmap_" + str(i) in bpy.data.materials: - - curr_mat = bpy.data.materials[".scs_nmap_" + str(i)] - - # grab only material without any users and clear all texture slots - if curr_mat.users == 0: - material = curr_mat - - for i in range(0, len(material.texture_slots)): - material.texture_slots.clear(i) - - i += 1 - - # if none is found create new one - if not material: - material = bpy.data.materials.new(".scs_nmap_" + str(i)) - - # finally set texture and it's properties to material - tex_slot = material.texture_slots.add() - tex_slot.texture_coords = "UV" - tex_slot.use_map_color_diffuse = False - tex_slot.use_map_normal = True - tex_slot.texture = texture - tex_slot.normal_map_space = "TANGENT" - - node_tree.nodes[DET_NMAP_MAT_NODE].material = material - - # if uv_layer property is set use it - if "uv_layer" in node_tree.nodes[DET_NMAP_MAT_NODE]: - - set_detail_uv(node_tree, node_tree.nodes[DET_NMAP_MAT_NODE]["uv_layer"]) - node_tree.nodes.active = old_active @@ -249,6 +217,7 @@ def set_uv(node_tree, uv_layer): :type uv_layer: str """ nmap.set_uv(node_tree, uv_layer) + node_tree.nodes[DET_NMAP_NODE].uv_map = uv_layer def set_detail_uv(node_tree, uv_layer): @@ -272,41 +241,16 @@ def delete(node_tree, preserve_node=False): :type preserve_node: bool """ - if DET_NMAP_MAT_NODE in node_tree.nodes: - nmap_mat_n = node_tree.nodes[DET_NMAP_MAT_NODE] - material = nmap_mat_n.material - - # remove and clear if possible - if material and material.users == 1: - - textures = {} - # gather all used textures in this material - for i, tex_slot in enumerate(material.texture_slots): - if tex_slot and tex_slot.texture: - textures[i] = tex_slot.texture - - # remove textures from texture slots first and check if texture can be cleared - for slot_i in textures.keys(): - material.texture_slots.clear(slot_i) - - if textures[slot_i].users <= 1: - textures[slot_i].user_clear() - - # as last delete actually nmap material - node_tree.nodes[DET_NMAP_MAT_NODE].material = None - material.user_clear() - bpy.data.materials.remove(material) - - if not preserve_node: - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_UV_SCALE_NODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_TEX_NODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_MAT_NODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_SCALE_GNODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_STRENGTH_NODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_MIX_NODE]) - node_tree.nodes.remove(node_tree.nodes[DET_NMAP_SEPARATE_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_SEPARATE_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_DET_NMAP_COMBINE_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_NORMALIZE_NODE]) + if DET_NMAP_NODE in node_tree.nodes and not preserve_node: + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_UV_SCALE_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_SCALE_GNODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_STRENGTH_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_MIX_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_SEPARATE_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_SEPARATE_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_DET_NMAP_COMBINE_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_NORMALIZE_NODE]) nmap.delete(node_tree, preserve_node=preserve_node) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/add_env.py index f35c509..78001b0 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/add_env.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/add_env.py @@ -44,4 +44,4 @@ def init(node_tree): DifSpecMultDifSpec.SPEC_COL_NODE, DifSpecMultDifSpec.BASE_TEX_NODE, DifSpecMultDifSpec.OUT_MAT_NODE, - DifSpecMultDifSpec.OUTPUT_NODE) + DifSpecMultDifSpec.COMPOSE_LIGHTING_NODE) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py index 9f9fe50..86233ae 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py @@ -50,4 +50,4 @@ def init(node_tree): DifSpecOclu.SPEC_COL_NODE, DifSpecOclu.BASE_TEX_NODE, DifSpecOclu.OUT_MAT_NODE, - DifSpecOclu.OUTPUT_NODE) + DifSpecOclu.COMPOSE_LIGHTING_NODE) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight/__init__.py index b495f37..949bbf3 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight/__init__.py @@ -47,12 +47,14 @@ def init(node_tree): DifSpec.init(node_tree) out_mat_n = node_tree.nodes[DifSpec.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[DifSpec.OUTPUT_NODE] vcol_mult_n = node_tree.nodes[DifSpec.VCOLOR_MULT_NODE] spec_mult_n = node_tree.nodes[DifSpec.SPEC_MULT_NODE] # move existing out_mat_n.location.x += pos_x_shift + compose_lighting_n.location.x += pos_x_shift output_n.location.x += pos_x_shift # node creation diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py index 63e4924..24618b9 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py @@ -44,7 +44,7 @@ def init(node_tree): DifSpecWeight.SPEC_COL_NODE, DifSpecWeight.BASE_TEX_NODE, DifSpecWeight.OUT_MAT_NODE, - DifSpecWeight.OUTPUT_NODE) + DifSpecWeight.COMPOSE_LIGHTING_NODE) @staticmethod def set_reflection_texture(node_tree, texture): diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py index e959ac1..e38436a 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py @@ -64,6 +64,7 @@ def init(node_tree): vcol_scale_n = node_tree.nodes[DifSpec.VCOLOR_SCALE_NODE] vcol_multi_n = node_tree.nodes[DifSpec.VCOLOR_MULT_NODE] out_mat_n = node_tree.nodes[DifSpec.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[DifSpec.OUTPUT_NODE] # delete existing @@ -73,6 +74,7 @@ def init(node_tree): spec_multi_n.location.x += pos_x_shift spec_multi_n.location.y += 100 out_mat_n.location.x += pos_x_shift + compose_lighting_n.location.x += pos_x_shift output_n.location.x += pos_x_shift # node creation diff --git a/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py index 31fb150..470f3ed 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py @@ -21,6 +21,7 @@ from mathutils import Color from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.std_node_groups import compose_lighting from io_scs_tools.internals.shaders.eut2.std_node_groups import add_env from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input from io_scs_tools.utils import convert as _convert_utils @@ -55,11 +56,11 @@ class Glass: OUT_MAT_NODE = "InputMaterial" TINT_VAL_SUBTRACT_NODE = "TintValueSubtract" - OUT_ADD_SPEC_NODE = "OutputAddSpec" + COMPOSE_LIGHTING_NODE = "ComposeLighting" OUT_ADD_SPEC_A_NODE = "OutputAddSpecAlpha" MAX_TINT_VAL_OR_OPACITY_NODE = "MaxTintValueOpacity" - OUT_ADD_REFL_NODE = "OutputAddRefl" + OUT_ADD_SPEC_NODE = "OutputAddSpec" OUT_ADD_TINT_MAX_A_NODE = "OutputAddTintMaximum" OUTPUT_NODE = "Output" @@ -85,13 +86,14 @@ def init(node_tree): pos_x_shift = 185 # node creation - # geometry + # vertex colors vcol_group_n = node_tree.nodes.new("ShaderNodeGroup") vcol_group_n.name = Glass.VCOL_GROUP_NODE vcol_group_n.label = Glass.VCOL_GROUP_NODE vcol_group_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1650) vcol_group_n.node_tree = vcolor_input.get_node_group() + # geometry geometry_n = node_tree.nodes.new("ShaderNodeGeometry") geometry_n.name = Glass.GEOM_NODE geometry_n.label = Glass.GEOM_NODE @@ -224,12 +226,11 @@ def init(node_tree): tint_val_subtract_n.inputs[0].default_value = 1.0 # pass 6 - out_add_spec_n = node_tree.nodes.new("ShaderNodeMixRGB") - out_add_spec_n.name = Glass.OUT_ADD_SPEC_NODE - out_add_spec_n.label = Glass.OUT_ADD_SPEC_NODE - out_add_spec_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2050) - out_add_spec_n.blend_type = "ADD" - out_add_spec_n.inputs['Fac'].default_value = 1 + compose_lighting_n = node_tree.nodes.new("ShaderNodeGroup") + compose_lighting_n.name = Glass.COMPOSE_LIGHTING_NODE + compose_lighting_n.label = Glass.COMPOSE_LIGHTING_NODE + compose_lighting_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2400) + compose_lighting_n.node_tree = compose_lighting.get_node_group() out_add_spec_a_n = node_tree.nodes.new("ShaderNodeMath") out_add_spec_a_n.name = Glass.OUT_ADD_SPEC_A_NODE @@ -244,12 +245,12 @@ def init(node_tree): max_tint_n.operation = "MAXIMUM" # pass 7 - out_add_refl_n = node_tree.nodes.new("ShaderNodeMixRGB") - out_add_refl_n.name = Glass.OUT_ADD_REFL_NODE - out_add_refl_n.label = Glass.OUT_ADD_REFL_NODE - out_add_refl_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y + 2200) - out_add_refl_n.blend_type = "ADD" - out_add_refl_n.inputs['Fac'].default_value = 1 + out_add_spec_n = node_tree.nodes.new("ShaderNodeMixRGB") + out_add_spec_n.name = Glass.OUT_ADD_SPEC_NODE + out_add_spec_n.label = Glass.OUT_ADD_SPEC_NODE + out_add_spec_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y + 2200) + out_add_spec_n.blend_type = "ADD" + out_add_spec_n.inputs['Fac'].default_value = 1 out_add_tint_max_a_n = node_tree.nodes.new("ShaderNodeMath") out_add_tint_max_a_n.name = Glass.OUT_ADD_TINT_MAX_A_NODE @@ -309,8 +310,9 @@ def init(node_tree): node_tree.links.new(tint_val_subtract_n.inputs[1], vcol_group_n.outputs['Vertex Color Alpha']) # pass 6 - node_tree.links.new(out_add_spec_n.inputs['Color1'], out_mat_n.outputs['Spec']) - node_tree.links.new(out_add_spec_n.inputs['Color2'], tint_diff_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], tint_diff_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Material Color'], tint_diff_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Env Color'], add_env_gn.outputs['Environment Addition Color']) node_tree.links.new(out_add_spec_a_n.inputs[0], out_mat_n.outputs['Spec']) node_tree.links.new(out_add_spec_a_n.inputs[1], add_env_gn.outputs['Environment Addition Color']) @@ -319,14 +321,14 @@ def init(node_tree): node_tree.links.new(max_tint_n.inputs[1], tint_opacity_n.outputs[0]) # pass 7 - node_tree.links.new(out_add_refl_n.inputs['Color1'], add_env_gn.outputs['Environment Addition Color']) - node_tree.links.new(out_add_refl_n.inputs['Color2'], out_add_spec_n.outputs['Color']) + node_tree.links.new(out_add_spec_n.inputs['Color1'], compose_lighting_n.outputs['Composed Color']) + node_tree.links.new(out_add_spec_n.inputs['Color2'], out_mat_n.outputs['Spec']) node_tree.links.new(out_add_tint_max_a_n.inputs[0], out_add_spec_a_n.outputs[0]) node_tree.links.new(out_add_tint_max_a_n.inputs[1], max_tint_n.outputs[0]) # output - node_tree.links.new(output_n.inputs['Color'], out_add_refl_n.outputs['Color']) + node_tree.links.new(output_n.inputs['Color'], out_add_spec_n.outputs['Color']) node_tree.links.new(output_n.inputs['Alpha'], out_add_tint_max_a_n.outputs[0]) @staticmethod @@ -342,7 +344,6 @@ def set_material(node_tree, material): material.use_transparency = True material.transparency_method = "Z_TRANSPARENCY" node_tree.nodes[Glass.OUT_MAT_NODE].material = material - material.emit = 0.02 @staticmethod def set_add_ambient(node_tree, factor): @@ -354,12 +355,7 @@ def set_add_ambient(node_tree, factor): :type factor: float """ - if factor == 0: - factor = 0.1 - - diffuse_col = Color(node_tree.nodes[Glass.DIFF_COL_NODE].outputs['Color'].default_value[:3]) - # NOTE: because emit works upon diffuse light we need to fake factors if diffuse drops - node_tree.nodes[Glass.OUT_MAT_NODE].material.emit = (factor / 10) * (1 / diffuse_col.v) + node_tree.nodes[Glass.COMPOSE_LIGHTING_NODE].inputs["AddAmbient"].default_value = factor @staticmethod def set_diffuse(node_tree, color): @@ -374,12 +370,7 @@ def set_diffuse(node_tree, color): color = _convert_utils.to_node_color(color) node_tree.nodes[Glass.DIFF_COL_NODE].outputs['Color'].default_value = color - node_tree.nodes[Glass.OUT_MAT_NODE].material.diffuse_intensity = Color(color[:3]).v * 0.7 - - # fix emit color representing ambient. - # NOTE: because emit works upon diffuse light we need to fake factors if diffuse drops - ambient = node_tree.nodes[Glass.OUT_MAT_NODE].material.scs_props.shader_attribute_add_ambient - node_tree.nodes[Glass.OUT_MAT_NODE].material.emit = (ambient / 10) * (1 / Color(color[:3]).v) + node_tree.nodes[Glass.OUT_MAT_NODE].material.diffuse_intensity = 0.7 @staticmethod def set_specular(node_tree, color): diff --git a/addon/io_scs_tools/internals/shaders/eut2/lamp/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/lamp/__init__.py index a694560..ba1d7e1 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/lamp/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/lamp/__init__.py @@ -49,6 +49,8 @@ def init(node_tree, init_dif_spec=True, start_pos_x=0, start_pos_y=0): :param node_tree: node tree on which this shader should be created :type node_tree: bpy.types.NodeTree + :param init_dif_spec should dif spec be initilized, True by default + :type init_dif_spec bool :param start_pos_x: x start position :type start_pos_x: int :param start_pos_y: y start position @@ -61,7 +63,7 @@ def init(node_tree, init_dif_spec=True, start_pos_x=0, start_pos_y=0): if init_dif_spec: DifSpec.init(node_tree) - out_mat_n = node_tree.nodes[DifSpec.OUT_MAT_NODE] + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[DifSpec.OUTPUT_NODE] # move existing @@ -100,7 +102,7 @@ def init(node_tree, init_dif_spec=True, start_pos_x=0, start_pos_y=0): node_tree.links.new(lampmask_mixr_gn.inputs["UV Vector"], sec_geom_n.outputs["UV"]) node_tree.links.new(out_add_lampmask_n.inputs["Color1"], lampmask_mixr_gn.outputs["Lampmask Addition Color"]) - node_tree.links.new(out_add_lampmask_n.inputs["Color2"], out_mat_n.outputs["Color"]) + node_tree.links.new(out_add_lampmask_n.inputs["Color2"], compose_lighting_n.outputs["Composed Color"]) node_tree.links.new(output_n.inputs["Color"], out_add_lampmask_n.outputs["Color"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/lamp/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/lamp/add_env.py index 55190a8..a2b3a3e 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/lamp/add_env.py +++ b/addon/io_scs_tools/internals/shaders/eut2/lamp/add_env.py @@ -42,7 +42,7 @@ def init(node_tree): out_add_lampmask_n = node_tree.nodes[Lamp.OUT_ADD_LAMPMASK_NODE] out_add_lampmask_n.location.y -= 300 - out_add_refl_n = node_tree.nodes[DifSpecAddEnv.OUT_ADD_REFL_NODE] + compose_lighting_n = node_tree.nodes[DifSpecAddEnv.COMPOSE_LIGHTING_NODE] # links fixing - node_tree.links.new(out_add_lampmask_n.inputs["Color2"], out_add_refl_n.outputs["Color"]) + node_tree.links.new(out_add_lampmask_n.inputs["Color2"], compose_lighting_n.outputs["Composed Color"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py index d779945..f88c559 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py @@ -132,7 +132,10 @@ def set_material(node_tree, material): node_tree.nodes[Reflective.OUT_MAT_NODE].use_specular = False material.use_transparency = True material.transparency_method = "MASK" - material.emit = 0.02 + + # make sure diffuse intensity and emit factors are properly set on material + material.diffuse_intensity = 0.7 + material.emit = 0 @staticmethod def set_base_texture(node_tree, texture): diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env.py index 173e49f..6cb0f69 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env.py @@ -30,6 +30,8 @@ REFL_TEX_MULT_NODE = "ReflectionTexMultiplier" REFL_TEX_COL_MULT_NODE = "ReflTexColorMultiplier" TEX_FRESNEL_MULT_NODE = "TextureFresnelMultiplier" +GLOBAL_ENV_FACTOR_NODE = "GlobalEnvFactor" +GLOBAL_ENV_MULT_NODE = "GlobalEnvMultiplier" FRESNEL_GNODE = "FresnelGroup" REFL_NORMAL_GNODE = "ReflNormalGroup" @@ -41,12 +43,41 @@ def get_node_group(): :rtype: bpy.types.NodeGroup """ - if ADD_ENV_G not in bpy.data.node_groups: + if __group_needs_recreation__(): __create_node_group__() return bpy.data.node_groups[ADD_ENV_G] +def set_global_env_factor(value): + """Sets global environment factor multiplication to the node group. + + NOTE: We are using global factor as we can not determinate if texture is generated one or static one and + based on that decide how much of envirnoment should be applied to result. + So for closest result to game this factor should be "env_static_mod" variable from "sun_profile" multiplied + with "env" variable from "sun_profile". + This way static cubemaps will be used as in game, however even generated cubemaps well mostl work well, + becuase expected values of that multiplication is from 0 to 1.0. + + :param value: global enironment factor (should come from sun profile) + :type value: float + """ + + get_node_group().nodes[GLOBAL_ENV_FACTOR_NODE].outputs[0].default_value = value + + +def __group_needs_recreation__(): + """Tells if group needs recreation. + + :return: True group isn't up to date and has to be (re)created; False if group doesn't need to be (re)created + :rtype: bool + """ + # current checks: + # 1. group existence in blender data block + # 2. existence of GLOBAL_ENV_FACTOR_NODE which was added in BT version 1.5 + return ADD_ENV_G not in bpy.data.node_groups or GLOBAL_ENV_FACTOR_NODE not in bpy.data.node_groups[ADD_ENV_G].nodes + + def __create_node_group__(): """Creates add env group. @@ -60,27 +91,38 @@ def __create_node_group__(): pos_x_shift = 185 - add_env_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=ADD_ENV_G) - - # inputs defining - add_env_g.inputs.new("NodeSocketFloat", "Fresnel Scale") - add_env_g.inputs.new("NodeSocketFloat", "Fresnel Bias") - add_env_g.inputs.new("NodeSocketVector", "Normal Vector") - add_env_g.inputs.new("NodeSocketVector", "View Vector") - add_env_g.inputs.new("NodeSocketFloat", "Apply Fresnel") - add_env_g.inputs.new("NodeSocketColor", "Reflection Texture Color") - add_env_g.inputs.new("NodeSocketFloat", "Base Texture Alpha") - add_env_g.inputs.new("NodeSocketColor", "Env Factor Color") - add_env_g.inputs.new("NodeSocketColor", "Specular Color") + if ADD_ENV_G not in bpy.data.node_groups: # creation + + add_env_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=ADD_ENV_G) + + # inputs defining + add_env_g.inputs.new("NodeSocketFloat", "Fresnel Scale") + add_env_g.inputs.new("NodeSocketFloat", "Fresnel Bias") + add_env_g.inputs.new("NodeSocketVector", "Normal Vector") + add_env_g.inputs.new("NodeSocketVector", "View Vector") + add_env_g.inputs.new("NodeSocketFloat", "Apply Fresnel") + add_env_g.inputs.new("NodeSocketColor", "Reflection Texture Color") + add_env_g.inputs.new("NodeSocketFloat", "Base Texture Alpha") + add_env_g.inputs.new("NodeSocketColor", "Env Factor Color") + add_env_g.inputs.new("NodeSocketColor", "Specular Color") + + # outputs defining + add_env_g.outputs.new("NodeSocketColor", "Environment Addition Color") + + else: # recreation + + add_env_g = bpy.data.node_groups[ADD_ENV_G] + + # delete all old nodes and links as they will be recreated now with actual version + add_env_g.nodes.clear() + + # node creation input_n = add_env_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, start_pos_y) - # outputs defining - add_env_g.outputs.new("NodeSocketColor", "Environment Addition Color") output_n = add_env_g.nodes.new("NodeGroupOutput") - output_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y) + output_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y) - # node creation refl_normal_gn = add_env_g.nodes.new("ShaderNodeGroup") refl_normal_gn.name = REFL_NORMAL_GNODE refl_normal_gn.label = REFL_NORMAL_GNODE @@ -128,6 +170,18 @@ def __create_node_group__(): tex_fresnel_mult_n.blend_type = "MULTIPLY" tex_fresnel_mult_n.inputs['Fac'].default_value = 1 + global_env_factor_n = add_env_g.nodes.new("ShaderNodeValue") + global_env_factor_n.name = GLOBAL_ENV_FACTOR_NODE + global_env_factor_n.label = GLOBAL_ENV_FACTOR_NODE + global_env_factor_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y - 200) + + global_env_mult_n = add_env_g.nodes.new("ShaderNodeMixRGB") + global_env_mult_n.name = GLOBAL_ENV_MULT_NODE + global_env_mult_n.label = GLOBAL_ENV_MULT_NODE + global_env_mult_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y) + global_env_mult_n.blend_type = "MULTIPLY" + global_env_mult_n.inputs['Fac'].default_value = 1 + # geometry links add_env_g.links.new(refl_normal_gn.inputs['View Vector'], input_n.outputs['View Vector']) add_env_g.links.new(refl_normal_gn.inputs['Normal Vector'], input_n.outputs['Normal Vector']) @@ -157,5 +211,9 @@ def __create_node_group__(): add_env_g.links.new(tex_fresnel_mult_n.inputs['Color1'], refl_tex_col_mult_n.outputs['Color']) add_env_g.links.new(tex_fresnel_mult_n.inputs['Color2'], fresnel_gn.outputs['Fresnel Factor']) + # pass 4 + add_env_g.links.new(global_env_mult_n.inputs['Color1'], tex_fresnel_mult_n.outputs['Color']) + add_env_g.links.new(global_env_mult_n.inputs['Color2'], global_env_factor_n.outputs['Value']) + # output pass - add_env_g.links.new(output_n.inputs['Environment Addition Color'], tex_fresnel_mult_n.outputs['Color']) + add_env_g.links.new(output_n.inputs['Environment Addition Color'], global_env_mult_n.outputs['Color']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting.py new file mode 100644 index 0000000..f8e5441 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting.py @@ -0,0 +1,139 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2016: SCS Software + +import bpy +from io_scs_tools.consts import Material as _MAT_consts +from io_scs_tools.utils import convert as _convert_utils + +COMPOSE_LIGHTING_G = _MAT_consts.node_group_prefix + "ComposeLighting" + +# D = lighting diffuse +# A = AddAmbient * SunProfileAmbient +# DL = D + A +# SL = lighting specular +# DC = attributes diffuse +# DS = attributes specular +# R = (DC * DL) + (SC * SL) --> R = DC * D + SC * SL + DC * A +# +# instead of implementing original formula we can just add (DC * A) to combined (DC * D + SC * SL) +# which is output of blender material. Additionally lest step is adding environment if specified. + +_ADD_AMBIENT_COL_NODE = "AdditionalAmbientColor" # sun profile ambient color +_MULT_A_NODE = "A=AddAmbient*SunProfileAmbient" +_MULT_DCA_NODE = "DC*A" +_MIX_MATERIAL_DCA_NODE = "Result=MaterialOut+DC*A" +_MIX_FINAL_NODE = "Result=Result+Env" + + +def get_node_group(): + """Gets node group for calcualtion of final lighting color. + + :return: node group which calculates finall shader output color + :rtype: bpy.types.NodeGroup + """ + + if COMPOSE_LIGHTING_G not in bpy.data.node_groups: + __create_node_group__() + + return bpy.data.node_groups[COMPOSE_LIGHTING_G] + + +def set_additional_ambient_col(color): + """Sets ambient color which should be used when material is using additional ambient. + + :param color: ambient color from sun profile (not converted to srgb) + :type color: list + """ + + color = _convert_utils.linear_to_srgb(color) + color = _convert_utils.to_node_color(color) + + get_node_group().nodes[_ADD_AMBIENT_COL_NODE].outputs[0].default_value = color + + +def __create_node_group__(): + """Creates compose lighting group. + + Inputs: AddAmbient, Diffuse Color, Material Color, Env Color + Outputs: Composed Color + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + compose_light_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=COMPOSE_LIGHTING_G) + + # inputs defining + compose_light_g.inputs.new("NodeSocketFloat", "AddAmbient") + compose_light_g.inputs.new("NodeSocketColor", "Diffuse Color") + compose_light_g.inputs.new("NodeSocketColor", "Material Color") + compose_light_g.inputs.new("NodeSocketColor", "Env Color") + input_n = compose_light_g.nodes.new("NodeGroupInput") + input_n.location = (start_pos_x - pos_x_shift, start_pos_y) + + # outputs defining + compose_light_g.outputs.new("NodeSocketColor", "Composed Color") + output_n = compose_light_g.nodes.new("NodeGroupOutput") + output_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y) + + # nodes creation + add_ambient_col_n = compose_light_g.nodes.new("ShaderNodeRGB") + add_ambient_col_n.name = add_ambient_col_n.label = _ADD_AMBIENT_COL_NODE + add_ambient_col_n.location = (start_pos_x + pos_x_shift * 1, start_pos_y + 400) + add_ambient_col_n.outputs[0].default_value = (0.0,) * 4 + + mult_a_n = compose_light_g.nodes.new("ShaderNodeMixRGB") + mult_a_n.name = mult_a_n.label = _MULT_A_NODE + mult_a_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 300) + mult_a_n.blend_type = "MULTIPLY" + mult_a_n.inputs["Fac"].default_value = 1 + + mult_dca_n = compose_light_g.nodes.new("ShaderNodeMixRGB") + mult_dca_n.name = mult_dca_n.label = _MULT_DCA_NODE + mult_dca_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 200) + mult_dca_n.blend_type = "MULTIPLY" + mult_dca_n.inputs["Fac"].default_value = 1 + + mix_material_dca_n = compose_light_g.nodes.new("ShaderNodeVectorMath") + mix_material_dca_n.name = mix_material_dca_n.label = _MIX_MATERIAL_DCA_NODE + mix_material_dca_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 100) + mix_material_dca_n.operation = "ADD" + + mix_final_n = compose_light_g.nodes.new("ShaderNodeVectorMath") + mix_final_n.name = mix_final_n.label = _MIX_FINAL_NODE + mix_final_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y) + mix_final_n.operation = "ADD" + + # links creation + compose_light_g.links.new(mult_a_n.inputs["Color1"], add_ambient_col_n.outputs["Color"]) + compose_light_g.links.new(mult_a_n.inputs["Color2"], input_n.outputs["AddAmbient"]) + + compose_light_g.links.new(mult_dca_n.inputs["Color1"], mult_a_n.outputs["Color"]) + compose_light_g.links.new(mult_dca_n.inputs["Color2"], input_n.outputs["Diffuse Color"]) + + compose_light_g.links.new(mix_material_dca_n.inputs[0], mult_dca_n.outputs["Color"]) + compose_light_g.links.new(mix_material_dca_n.inputs[1], input_n.outputs["Material Color"]) + + compose_light_g.links.new(mix_final_n.inputs[0], mix_material_dca_n.outputs["Vector"]) + compose_light_g.links.new(mix_final_n.inputs[1], input_n.outputs["Env Color"]) + + compose_light_g.links.new(output_n.inputs["Composed Color"], mix_final_n.outputs["Vector"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input.py index c148111..418109f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input.py @@ -31,7 +31,7 @@ _VCOL_GAMMA_CORR_A_N = "VertexColorAGamma" _VCOL_SATURATE_N = "VertexColorSaturation" _ALPHA_TO_BW_N = "VertexColAToBW" -_VCOL_NORM_N = "VertexColorNormalize" +_VCOL_NORM_N = "VertexColorNormalize" # some kind of HDR compensation _VCOL_NORM_A_N = "VertexColorAlphaNormalize" diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py index e456418..50ae4cb 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py @@ -27,7 +27,6 @@ class StdAddEnv: REFL_TEX_NODE = "ReflectionTex" ENV_COLOR_NODE = "EnvFactorColor" ADD_ENV_GROUP_NODE = "AddEnvGroup" - OUT_ADD_REFL_NODE = "OutputAddRefl" @staticmethod def get_name(): @@ -35,7 +34,7 @@ def get_name(): return __name__ @staticmethod - def add(node_tree, geom_n_name, spec_col_n_name, base_tex_n_name, out_mat_n_name, output_n_name): + def add(node_tree, geom_n_name, spec_col_n_name, base_tex_n_name, out_mat_n_name, output_n_name, output_n_socket_name="Env Color"): """Add add env pass to node tree with links. :param node_tree: node tree on which this shader should be created @@ -63,9 +62,6 @@ def add(node_tree, geom_n_name, spec_col_n_name, base_tex_n_name, out_mat_n_name out_mat_n = node_tree.nodes[out_mat_n_name] output_n = node_tree.nodes[output_n_name] - # move existing - output_n.location.x += pos_x_shift - # node creation refl_tex_n = node_tree.nodes.new("ShaderNodeTexture") refl_tex_n.name = refl_tex_n.label = StdAddEnv.REFL_TEX_NODE @@ -84,12 +80,6 @@ def add(node_tree, geom_n_name, spec_col_n_name, base_tex_n_name, out_mat_n_name add_env_gn.inputs['Fresnel Bias'].default_value = 0.2 add_env_gn.inputs['Base Texture Alpha'].default_value = 0.5 - out_add_refl_n = node_tree.nodes.new("ShaderNodeMixRGB") - out_add_refl_n.name = out_add_refl_n.label = StdAddEnv.OUT_ADD_REFL_NODE - out_add_refl_n.location = (output_n.location.x - pos_x_shift, start_pos_y + 1950) - out_add_refl_n.blend_type = "ADD" - out_add_refl_n.inputs['Fac'].default_value = 1 - # geometry links node_tree.links.new(add_env_gn.inputs['Normal Vector'], geometry_n.outputs['Normal']) node_tree.links.new(add_env_gn.inputs['View Vector'], geometry_n.outputs['View']) @@ -107,15 +97,7 @@ def add(node_tree, geom_n_name, spec_col_n_name, base_tex_n_name, out_mat_n_name if add_env_gn and base_tex_n: node_tree.links.new(add_env_gn.inputs['Base Texture Alpha'], base_tex_n.outputs['Value']) - # output pass - node_tree.links.new(out_add_refl_n.inputs['Color1'], add_env_gn.outputs['Environment Addition Color']) - node_tree.links.new(out_add_refl_n.inputs['Color2'], out_mat_n.outputs['Color']) - - # try to find first color in output node - if "Color" in output_n.inputs: - node_tree.links.new(output_n.inputs['Color'], out_add_refl_n.outputs['Color']) - elif "Color1" in output_n.inputs: - node_tree.links.new(output_n.inputs['Color1'], out_add_refl_n.outputs['Color']) + node_tree.links.new(output_n.inputs[output_n_socket_name], add_env_gn.outputs['Environment Addition Color']) @staticmethod def set_reflection_texture(node_tree, texture): diff --git a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py index 1a7188f..dc9af33 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py @@ -80,18 +80,18 @@ def init(node_tree): vcol_scale_n = node_tree.nodes[DifSpecAddEnv.VCOLOR_SCALE_NODE] opacity_n = node_tree.nodes[DifSpecAddEnv.OPACITY_NODE] out_mat_n = node_tree.nodes[DifSpecAddEnv.OUT_MAT_NODE] - out_add_refl_n = node_tree.nodes[DifSpecAddEnv.OUT_ADD_REFL_NODE] + compose_lighting_n = node_tree.nodes[DifSpecAddEnv.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[DifSpecAddEnv.OUTPUT_NODE] # move existing add_refl_gn.location.x += pos_x_shift * 3 spec_mult_n.location.x += pos_x_shift * 3 out_mat_n.location.x += pos_x_shift * 2 - out_add_refl_n.location.x += pos_x_shift * 2 + compose_lighting_n.location.x += pos_x_shift * 2 output_n.location.x += pos_x_shift * 2 - # set fresnel factor to 0 - node_tree.nodes[DifSpecAddEnv.ADD_ENV_GROUP_NODE].inputs['Apply Fresnel'].default_value = 0.0 + # set fresnel factor to 1 + node_tree.nodes[DifSpecAddEnv.ADD_ENV_GROUP_NODE].inputs['Apply Fresnel'].default_value = 1.0 # node creation - level 3 env_vcol_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") @@ -155,6 +155,7 @@ def init(node_tree): # make links - output node_tree.links.new(out_mat_n.inputs['Color'], paint_mult_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], paint_mult_n.outputs['Color']) @staticmethod def init_colormask_or_airbrush(node_tree): @@ -320,6 +321,11 @@ def set_paintjob_uv(node_tree, uv_layer): :type uv_layer: str """ + # as this functions should be called from airbrush or colormask derivatives + # make sure to skip execution if for some historical reasons this is called from stock truckpaint + if Truckpaint.PAINT_TEX_NODE not in node_tree.nodes: + return + if uv_layer is None or uv_layer == "": uv_layer = _MESH_consts.none_uv diff --git a/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py index 00ea7cc..c77b442 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py @@ -31,6 +31,7 @@ class Water(Dif, StdAddEnv): MIX_FACTOR_GNODE = "WaterMixFactor" NEAR_MIX_NODE = "NearMix" HORIZON_MIX_NODE = "HorizonMix" + ADD_REFL_MIX_NODE = "AddRefl" NEAR_HORIZON_MIX_NODE = "NearHorizonLerpMix" LAYER0_MAT_NODE = "Layer0Tex" @@ -67,6 +68,7 @@ def init(node_tree): output_n = node_tree.nodes[Dif.OUTPUT_NODE] # delete existing + node_tree.nodes.remove(node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE]) node_tree.nodes.remove(node_tree.nodes[Dif.BASE_TEX_NODE]) node_tree.nodes.remove(node_tree.nodes[Dif.OPACITY_NODE]) node_tree.nodes.remove(node_tree.nodes[Dif.DIFF_MULT_NODE]) @@ -126,9 +128,15 @@ def init(node_tree): horizon_mix_n.blend_type = "MULTIPLY" horizon_mix_n.inputs['Fac'].default_value = 1.0 + add_refl_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + add_refl_mix_n.name = add_refl_mix_n.label = Water.ADD_REFL_MIX_NODE + add_refl_mix_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 2000) + add_refl_mix_n.blend_type = "ADD" + add_refl_mix_n.inputs['Fac'].default_value = 1.0 + near_horizon_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") near_horizon_mix_n.name = near_horizon_mix_n.label = Water.NEAR_HORIZON_MIX_NODE - near_horizon_mix_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 1700) + near_horizon_mix_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1700) near_horizon_mix_n.blend_type = "MIX" # add environment pass and normal maps @@ -137,7 +145,8 @@ def init(node_tree): Dif.SPEC_COL_NODE, "", Water.NEAR_MIX_NODE, - Water.NEAR_HORIZON_MIX_NODE) + Water.ADD_REFL_MIX_NODE, + "Color1") # links creation # pass 1 @@ -146,6 +155,7 @@ def init(node_tree): # pass 2 node_tree.links.new(normal_normalize_n.inputs[0], lay0_lay1_normal_mix_n.outputs['Color']) + node_tree.links.new(vcol_mult_n.inputs['Color2'], diff_col_n.outputs['Color']) # pass 3 node_tree.links.new(near_mix_n.inputs['Color1'], vcol_mult_n.outputs['Color']) @@ -155,16 +165,20 @@ def init(node_tree): node_tree.links.new(horizon_mix_n.inputs['Color2'], horizon_col_n.outputs['Color']) # pass 4 - node_tree.links.new(vcol_mult_n.inputs['Color2'], diff_col_n.outputs['Color']) + node_tree.links.new(add_refl_mix_n.inputs['Color2'], near_mix_n.outputs['Color']) # pass 5 node_tree.links.new(near_horizon_mix_n.inputs['Fac'], mix_factor_gn.outputs['Mix Factor']) + node_tree.links.new(near_horizon_mix_n.inputs['Color1'], add_refl_mix_n.outputs['Color']) node_tree.links.new(near_horizon_mix_n.inputs['Color2'], horizon_mix_n.outputs['Color']) # material pass node_tree.links.new(out_mat_n.inputs['Color'], near_horizon_mix_n.outputs['Color']) node_tree.links.new(out_mat_n.inputs['Normal'], normal_normalize_n.outputs['Vector']) + # output pass + node_tree.links.new(output_n.inputs['Color'], out_mat_n.outputs['Color']) + @staticmethod def set_aux0(node_tree, aux_property): """Set near distance, far distance and scramble factor. diff --git a/addon/io_scs_tools/internals/shaders/eut2/window/night.py b/addon/io_scs_tools/internals/shaders/eut2/window/night.py index 93d982c..3df5aff 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/window/night.py +++ b/addon/io_scs_tools/internals/shaders/eut2/window/night.py @@ -57,7 +57,7 @@ def init(node_tree): base_tex_n = node_tree.nodes[DifSpecAddEnv.BASE_TEX_NODE] diff_mult_n = node_tree.nodes[DifSpecAddEnv.DIFF_MULT_NODE] out_mat_n = node_tree.nodes[DifSpecAddEnv.OUT_MAT_NODE] - out_add_refl_n = node_tree.nodes[DifSpecAddEnv.OUT_ADD_REFL_NODE] + compose_lighting_n = node_tree.nodes[DifSpecAddEnv.COMPOSE_LIGHTING_NODE] output_n = node_tree.nodes[DifSpecAddEnv.OUTPUT_NODE] # move existing @@ -132,10 +132,11 @@ def init(node_tree): node_tree.links.new(out_mat_n.inputs["Spec"], spec_col_n.outputs["Color"]) # post pass 1 - node_tree.links.new(out_add_refl_n.inputs["Color2"], diff_mult_n.outputs["Color"]) + node_tree.links.new(compose_lighting_n.inputs["Diffuse Color"], diff_mult_n.outputs["Color"]) + node_tree.links.new(compose_lighting_n.inputs["Material Color"], diff_mult_n.outputs["Color"]) # post pass 2 - node_tree.links.new(out_add_spec_n.inputs["Color1"], out_add_refl_n.outputs["Color"]) + node_tree.links.new(out_add_spec_n.inputs["Color1"], compose_lighting_n.outputs["Composed Color"]) node_tree.links.new(out_add_spec_n.inputs["Color2"], out_mat_n.outputs["Spec"]) # output pass diff --git a/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py b/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py index 0240130..aaaa3b3 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py +++ b/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py @@ -19,19 +19,18 @@ # Copyright (C) 2015: SCS Software import bpy - from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.flavors.nmap import scale_ng NMAP_FLAVOR_FRAME_NODE = "TSNMap Flavor" -NMAP_MAT_NODE = "NormalMapMat" +NMAP_NODE = "NormalMapMat" NMAP_GEOM_NODE = "NMapGeom" NMAP_TEX_NODE = "NMapTex" NMAP_SCALE_GNODE = "NMapScaleGroup" def __create_nodes__(node_tree, location=None, normal_to=None): - """Create node for alpha test. + """Create node for normal maps. :param node_tree: node tree on which normal map will be used :type node_tree: bpy.types.NodeTree @@ -39,14 +38,14 @@ def __create_nodes__(node_tree, location=None, normal_to=None): # try to recover location if not location: - if NMAP_MAT_NODE in node_tree.nodes: - location = node_tree.nodes[NMAP_MAT_NODE].location + if NMAP_NODE in node_tree.nodes: + location = node_tree.nodes[NMAP_NODE].location # try to recover normal to link node socket if not normal_to: - if NMAP_MAT_NODE in node_tree.nodes: + if NMAP_NODE in node_tree.nodes: for link in node_tree.links: - if link.from_node == node_tree.nodes[NMAP_MAT_NODE] and link.from_socket.name == "Normal": + if link.from_node == node_tree.nodes[NMAP_NODE] and link.from_socket.name == "Normal": normal_to = link.to_socket frame = node_tree.nodes.new("NodeFrame") @@ -61,11 +60,11 @@ def __create_nodes__(node_tree, location=None, normal_to=None): nmap_tex_n.parent = frame nmap_tex_n.name = nmap_tex_n.label = NMAP_TEX_NODE - nmap_mat_n = node_tree.nodes.new("ShaderNodeMaterial") - nmap_mat_n.parent = frame - nmap_mat_n.name = nmap_mat_n.label = NMAP_MAT_NODE - nmap_mat_n.use_diffuse = False - nmap_mat_n.use_specular = False + nmap_n = node_tree.nodes.new("ShaderNodeNormalMap") + nmap_n.parent = frame + nmap_n.name = nmap_n.label = NMAP_NODE + nmap_n.space = "TANGENT" + nmap_n.inputs["Strength"].default_value = 1 nmap_scale_gn = node_tree.nodes.new("ShaderNodeGroup") nmap_scale_gn.parent = frame @@ -76,7 +75,7 @@ def __create_nodes__(node_tree, location=None, normal_to=None): if location: nmap_geom_n.location = (location[0] - 185 * 3, location[1]) nmap_tex_n.location = (location[0] - 185 * 2, location[1]) - nmap_mat_n.location = (location[0] - 185, location[1] - 200) + nmap_n.location = (location[0] - 185, location[1] - 200) nmap_scale_gn.location = (location[0], location[1]) # links creation @@ -84,9 +83,11 @@ def __create_nodes__(node_tree, location=None, normal_to=None): node_tree.links.new(nodes[NMAP_TEX_NODE].inputs["Vector"], nodes[NMAP_GEOM_NODE].outputs["UV"]) + node_tree.links.new(nodes[NMAP_NODE].inputs["Color"], nodes[NMAP_TEX_NODE].outputs["Color"]) + node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["NMap Tex Color"], nodes[NMAP_TEX_NODE].outputs["Color"]) node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["Original Normal"], nodes[NMAP_GEOM_NODE].outputs["Normal"]) - node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["Modified Normal"], nodes[NMAP_MAT_NODE].outputs["Normal"]) + node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["Modified Normal"], nodes[NMAP_NODE].outputs["Normal"]) node_tree.links.new(normal_to, nodes[NMAP_SCALE_GNODE].outputs["Normal"]) @@ -131,41 +132,6 @@ def set_texture(node_tree, texture): # assign texture to texture node first node_tree.nodes[NMAP_TEX_NODE].texture = texture - # search possible existing materials and use it - material = None - i = 1 - while ".scs_nmap_" + str(i) in bpy.data.materials: - - curr_mat = bpy.data.materials[".scs_nmap_" + str(i)] - - # grab only material without any users and clear all texture slots - if curr_mat.users == 0: - material = curr_mat - - for j in range(0, len(material.texture_slots)): - material.texture_slots.clear(j) - - i += 1 - - # if none is found create new one - if not material: - material = bpy.data.materials.new(".scs_nmap_" + str(i)) - - # finally set texture and it's properties to material - tex_slot = material.texture_slots.add() - tex_slot.texture_coords = "UV" - tex_slot.use_map_color_diffuse = False - tex_slot.use_map_normal = True - tex_slot.texture = texture - tex_slot.normal_map_space = "TANGENT" - - node_tree.nodes[NMAP_MAT_NODE].material = material - - # if uv_layer property is set use it - if "uv_layer" in node_tree.nodes[NMAP_MAT_NODE]: - - set_uv(node_tree, node_tree.nodes[NMAP_MAT_NODE]["uv_layer"]) - node_tree.nodes.active = old_active @@ -185,14 +151,9 @@ def set_uv(node_tree, uv_layer): if NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: __create_nodes__(node_tree) - # set uv layer also to texture node + # set uv layer to texture node and normal map node node_tree.nodes[NMAP_GEOM_NODE].uv_layer = uv_layer - - # backup uv layer for usage on texture set - node_tree.nodes[NMAP_MAT_NODE]["uv_layer"] = uv_layer - - if node_tree.nodes[NMAP_MAT_NODE].material: - node_tree.nodes[NMAP_MAT_NODE].material.texture_slots[0].uv_layer = uv_layer + node_tree.nodes[NMAP_NODE].uv_map = uv_layer def delete(node_tree, preserve_node=False): @@ -204,34 +165,9 @@ def delete(node_tree, preserve_node=False): :type preserve_node: bool """ - if NMAP_MAT_NODE in node_tree.nodes: - nmap_mat_n = node_tree.nodes[NMAP_MAT_NODE] - material = nmap_mat_n.material - - # remove and clear if possible - if material and material.users == 1: - - textures = {} - # gather all used textures in this material - for i, tex_slot in enumerate(material.texture_slots): - if tex_slot and tex_slot.texture: - textures[i] = tex_slot.texture - - # remove textures from texture slots first and check if texture can be cleared - for slot_i in textures.keys(): - material.texture_slots.clear(slot_i) - - if textures[slot_i].users <= 1: - textures[slot_i].user_clear() - - # as last delete actually nmap material - node_tree.nodes[NMAP_MAT_NODE].material = None - material.user_clear() - bpy.data.materials.remove(material) - - if not preserve_node: - node_tree.nodes.remove(node_tree.nodes[NMAP_GEOM_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_TEX_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_MAT_NODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_SCALE_GNODE]) - node_tree.nodes.remove(node_tree.nodes[NMAP_FLAVOR_FRAME_NODE]) + if NMAP_NODE in node_tree.nodes and not preserve_node: + node_tree.nodes.remove(node_tree.nodes[NMAP_GEOM_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_NODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_SCALE_GNODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_FLAVOR_FRAME_NODE]) diff --git a/addon/io_scs_tools/internals/shaders/shader.py b/addon/io_scs_tools/internals/shaders/shader.py index 7537b27..8065f37 100644 --- a/addon/io_scs_tools/internals/shaders/shader.py +++ b/addon/io_scs_tools/internals/shaders/shader.py @@ -232,34 +232,17 @@ def __setup_nodes__(material, effect, attr_dict, tex_dict, uvs_dict, flavors_dic def __clean_node_tree__(node_tree): - """Cleans material node tree of any nodes, custom properties and even cleanup any unused dummy normal map materials. + """Cleans material node tree of any nodes, custom properties. :param node_tree: node tree on which any shader nodes should be deleted :type node_tree: bpy.types.NodeTree """ - mats_to_remove = [] - for node in node_tree.nodes: - if node.type == "MATERIAL" and node.material and node.material.name.startswith(".scs_nmap_"): - mats_to_remove.append(node.material.name) - # clean nodes and custom props node_tree.nodes.clear() for key in node_tree.keys(): del node_tree[key] - # clean unused dummy normal map materials - mat_i = 0 - while mat_i < len(mats_to_remove): - - mat_name = mats_to_remove[mat_i] - - if mat_name in bpy.data.materials and bpy.data.materials[mat_name].users == 0: - lprint("D Removing unused nmap material: %s", (mat_name,)) - bpy.data.materials.remove(bpy.data.materials[mat_name]) - - mat_i += 1 - def __get_shader__(effect, report_not_found, mat_name): """Get shader from effect name. If it doesn't find any suitable shader it returns "dif.spec" shader from euro trucks 2 library. diff --git a/addon/io_scs_tools/internals/structure.py b/addon/io_scs_tools/internals/structure.py index c052196..e08efef 100755 --- a/addon/io_scs_tools/internals/structure.py +++ b/addon/io_scs_tools/internals/structure.py @@ -18,6 +18,8 @@ # Copyright (C) 2013-2014: SCS Software +from io_scs_tools.utils import convert as _convert_utils + class SectionData(object): """SCS Section data structure (PIX files): @@ -107,3 +109,56 @@ def __init__(self, data_type, data_id): self.type = data_type self.id = data_id self.props = {} + + def get_prop_as_number(self, prop_name): + """Gets given property as float value. If property is there + it tries to conver it's value to float. + + :param prop_name: name of property that should hold float value + :type prop_name: str + :return: float or int if property exists and can be parsed, none otherwise + :rtype: float | int | None + """ + + if prop_name not in self.props: + return None + + prop_value = self.props[prop_name] + + if isinstance(prop_value, list): # if property is array take first item + prop_value = prop_value[0] + + if not isinstance(prop_value, str): + return None + + return _convert_utils.string_to_number(prop_value) + + def get_prop_as_color(self, prop_name): + """Gets given property as color. If property is there + it tries to convert it's value of float vector array of length 3. + + :param prop_name: name of the property that should hold color values + :type prop_name: str + :return: list of 3 floats if property exists and can be parsed, none otherwise + :rtype: list[float] | None + """ + + if prop_name not in self.props: + return None + + prop_value = self.props[prop_name] + + if isinstance(prop_value, list): # if property is array take first item + prop_value = prop_value[0] + + if not isinstance(prop_value, list): + return None + + if not len(prop_value) == 3: + return None + + for i, key in enumerate(prop_value): + convert_res = _convert_utils.string_to_number(key) + prop_value[i] = convert_res + + return prop_value diff --git a/addon/io_scs_tools/operators/__init__.py b/addon/io_scs_tools/operators/__init__.py index 658d6f8..1fc5c82 100644 --- a/addon/io_scs_tools/operators/__init__.py +++ b/addon/io_scs_tools/operators/__init__.py @@ -23,3 +23,4 @@ from io_scs_tools.operators import object from io_scs_tools.operators import scene from io_scs_tools.operators import wm +from io_scs_tools.operators import world diff --git a/addon/io_scs_tools/operators/material.py b/addon/io_scs_tools/operators/material.py index fdbf37e..642730c 100644 --- a/addon/io_scs_tools/operators/material.py +++ b/addon/io_scs_tools/operators/material.py @@ -110,7 +110,7 @@ def execute(self, context): if ng_name not in bpy.data.node_groups: continue - bpy.data.node_groups.remove(bpy.data.node_groups[ng_name]) + bpy.data.node_groups.remove(bpy.data.node_groups[ng_name], do_unlink=True) # 4. finally set preset to material again, which will update nodes and possible input interface changes for mat in bpy.data.materials: @@ -520,6 +520,14 @@ def execute(self, context): if is_in_middle or is_on_end: flavors_suffix += "." + flavor_variant.name + # if desired combination doesn't exists, abort switching and notify user + if not _shader_presets_cache.has_section(preset, flavors_suffix): + message = "Enabling %r flavor aborted! Wanted shader combination: %r is not supported!" % (self.flavor_name, + preset.effect + flavors_suffix) + lprint("E " + message) + self.report({'WARNING'}, message) + return {'FINISHED'} + # finally set new shader data to material section = _shader_presets_cache.get_section(preset, flavors_suffix) context.material.scs_props.mat_effect_name = preset.effect + flavors_suffix diff --git a/addon/io_scs_tools/operators/mesh.py b/addon/io_scs_tools/operators/mesh.py index 613c269..4a62e8a 100644 --- a/addon/io_scs_tools/operators/mesh.py +++ b/addon/io_scs_tools/operators/mesh.py @@ -21,7 +21,6 @@ import bmesh import bpy from bpy.props import StringProperty, FloatProperty - from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.consts import LampTools as _LT_consts from io_scs_tools.consts import VertexColorTools as _VCT_consts @@ -314,67 +313,3 @@ def execute(self, context): vertex_col_data.color = (0.5,) * 3 return {'FINISHED'} - - -class NormalMap: - """ - Wrapper class for better navigation in file - """ - - class EnsureActiveUV(bpy.types.Operator): - bl_label = "Ensure Active UV" - bl_idname = "mesh.scs_ensure_active_uv" - bl_description = "Sets Nmap texture mapping as active on all meshes using this material." \ - "(Needed because Blender calculates tangents upon active UV)" - - mat_name = StringProperty( - default="", - options={'HIDDEN'}, - ) - - uv_layer = StringProperty( - default="", - options={'HIDDEN'}, - ) - - def execute(self, context): - - if self.mat_name != "" and self.uv_layer != "": - - changed_count = 0 - unchanged_count = 0 - recognized_count = 0 - for obj in bpy.data.objects: - - if obj.type == "MESH" and obj.data: - - for mat_slot in obj.material_slots: - if mat_slot.material and mat_slot.material.name == self.mat_name: - - # find uv layer which is used for normal map and mark it as active - for i, uv_tex in enumerate(obj.data.uv_textures): - if uv_tex.name == self.uv_layer: - - if obj.data.uv_textures.active_index != i: - obj.data.uv_textures.active_index = i - changed_count += 1 - else: - unchanged_count += 1 - - break - - recognized_count += 1 - break - - if recognized_count > 0: - if recognized_count == unchanged_count: - self.report({"INFO"}, "All meshes already have proper active UV.") - elif recognized_count == unchanged_count + changed_count: - self.report({"INFO"}, "Active UV changed on %i objects." % changed_count) - else: - self.report({"ERROR"}, "UV layers out of sync on some objects. Active UVs changed on %i from %i objects!" % - (changed_count, recognized_count)) - else: - self.report({"INFO"}, "None object is using this material. No changes made.") - - return {'FINISHED'} diff --git a/addon/io_scs_tools/operators/object.py b/addon/io_scs_tools/operators/object.py index 6b92cb1..96a0679 100755 --- a/addon/io_scs_tools/operators/object.py +++ b/addon/io_scs_tools/operators/object.py @@ -27,12 +27,15 @@ from io_scs_tools.consts import Part as _PART_consts from io_scs_tools.consts import Variant as _VARIANT_consts from io_scs_tools.consts import Operators as _OP_consts +from io_scs_tools.consts import Icons as _ICONS_consts from io_scs_tools.internals import inventory as _inventory from io_scs_tools.internals import looks as _looks +from io_scs_tools.internals.icons import get_icon as _get_icon from io_scs_tools.internals.connections.wrappers import group as _connection_group_wrapper from io_scs_tools.internals.open_gl.storage import terrain_points as _terrain_points_storage from io_scs_tools.operators.bases.selection import Selection as _BaseSelectionOperator from io_scs_tools.operators.bases.view import View as _BaseViewOperator +from io_scs_tools.properties.object import ObjectSCSTools as _ObjectSCSTools from io_scs_tools.utils.printout import lprint from io_scs_tools.utils import object as _object_utils from io_scs_tools.utils import name as _name_utils @@ -2077,6 +2080,115 @@ def execute(self, context): return {'FINISHED'} +class AddObject(bpy.types.Operator): + """Creates and links locator or SCS Root to the scenes.""" + bl_label = "Add SCS Object" + bl_idname = "object.scs_add_object" + bl_description = "Create SCS object of choosen type at 3D coursor position \n" \ + "(when locator is created it will also be parented to SCS Root, if currently active)." + + # create function for retrieving items so custom icons can be used + def new_object_type_items(self, context): + return [ + ( + 'Root Object', "Root Object", "Creates SCS Root Object add parents any selected objects to it.", + _get_icon(_ICONS_consts.Types.scs_root), 0 + ), + ( + 'Prefab Locator', "Prefab Locator", "Creates prefab locator + creates parent to SCS Root if currently active.", + _get_icon(_ICONS_consts.Types.loc_prefab), 1 + ), + ( + 'Model Locator', "Model Locator", "Creates model locator + creates parent to SCS Root if currently active.", + _get_icon(_ICONS_consts.Types.loc_model), 2 + ), + ( + 'Collision Locator', "Collision Locator", "Creates collision locator + creates parent to SCS Root if currently active.", + _get_icon(_ICONS_consts.Types.loc_collider), 3 + ), + ] + + new_object_type = bpy.props.EnumProperty( + items=new_object_type_items + ) + + prefab_type = bpy.props.EnumProperty( + name="Prefab Locator Type", + description="Defines type of new prefab locator.", + items=_ObjectSCSTools.locator_prefab_type_items + ) + + collider_type = bpy.props.EnumProperty( + name="Collision Locator Type", + description="Defines type of new collision locator.", + items=_ObjectSCSTools.locator_collider_type_items + ) + + def draw(self, context): + col = self.layout.column() + + # draw invoke props dialog depending on which type of locator user selected + if self.new_object_type == "Prefab Locator": + + col.label(text="Prefab Locator Type:") + col.prop(self, "prefab_type", text="") + + elif self.new_object_type == "Collision Locator": + + col.label(text="Collision Locator Type:") + col.prop(self, "collider_type", text="") + + return + + def execute(self, context): + lprint("D " + self.bl_label + "...") + + # save active object for later parenting of locator + active_obj = context.active_object + + if self.new_object_type == "Root Object": + + bpy.ops.object.create_scs_root_object() + + else: + + if self.new_object_type == "Prefab Locator": + new_loc = _object_utils.create_locator_empty("pl", context.scene.cursor_location, data_type="Prefab", blend_coords=True) + new_loc.scs_props.locator_prefab_type = self.prefab_type + elif self.new_object_type == "Model Locator": + new_loc = _object_utils.create_locator_empty("ml", context.scene.cursor_location, data_type="Model", blend_coords=True) + else: + new_loc = _object_utils.create_locator_empty("cl", context.scene.cursor_location, data_type="Collision", blend_coords=True) + new_loc.scs_props.locator_collider_type = self.collider_type + + # if previous active object was SCS root then automatically parent new locator to it + if active_obj and _object_utils.get_scs_root(active_obj) == active_obj: + + # 1. deselect all + bpy.ops.object.select_all(action='DESELECT') + + # 2. prepare selection for parenting: select new locator, scs root and set scs root as active object + new_loc.select = True + active_obj.select = True + context.scene.objects.active = active_obj + + # 3. execute parenting (selected -> active) + bpy.ops.object.parent_set(type='OBJECT', keep_transform=True) + + # 4. switch active to new locator so user can continue working on it + context.scene.objects.active = new_loc + + return {'FINISHED'} + + def invoke(self, context, event): + + # get extra input from user to decide which type of prefab or collision locator should be created + if self.new_object_type in ("Prefab Locator", "Collision Locator"): + return context.window_manager.invoke_props_dialog(self, width=150) + + return self.execute(context) + + class BlankOperator(bpy.types.Operator): """Blank Operator.""" bl_label = "Blank Operator" diff --git a/addon/io_scs_tools/operators/scene.py b/addon/io_scs_tools/operators/scene.py index 952b3a1..3b99611 100644 --- a/addon/io_scs_tools/operators/scene.py +++ b/addon/io_scs_tools/operators/scene.py @@ -214,9 +214,9 @@ def execute_export(self, context, disable_local_view): import traceback - traceback.print_exc() - lprint("E Unexpected %r accured during batch export, see stack trace above.", - (type(e).__name__,), + trace_str = traceback.format_exc().replace("\n", "\n\t ") + lprint("E Unexpected %r accured during batch export:\n\t %s", + (type(e).__name__, trace_str), report_errors=1, report_warnings=1) @@ -591,6 +591,38 @@ def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} + class SunProfilesLibraryPath(bpy.types.Operator): + """Operator for setting relative path to Material Substance shader files.""" + bl_label = "Select Sun Profiles Library File" + bl_idname = "scene.select_sun_profiles_lib_path" + bl_description = "Open a file browser" + + # always set default display type so blender won't be using "last used" + display_type = get_filebrowser_display_type() + + filepath = StringProperty( + name="Sun Profiles Library File", + description="Sun Profiles library relative/absolute file path", + subtype='FILE_PATH', + ) + filter_glob = StringProperty(default="*.sii", options={'HIDDEN'}) + + def execute(self, context): + """Set Material Substance library filepath.""" + scs_globals = _get_scs_globals() + + if _path_utils.startswith(self.filepath, scs_globals.scs_project_path): + self.filepath = _path_utils.relative_path(scs_globals.scs_project_path, self.filepath) + + scs_globals.sun_profiles_lib_path = self.filepath + return {'FINISHED'} + + def invoke(self, context, event): + """Invoke a file path selector.""" + self.filepath = _path_utils.get_abs_path(_get_scs_globals().sun_profiles_lib_path) + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + class DirSelectorInsideBase(bpy.types.Operator): """Operator for setting relative or absolute path to Global Export file.""" bl_label = "Select Directory" @@ -652,6 +684,27 @@ def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} + class ReloadLibraryPath(bpy.types.Operator): + """Operator for reloading given library.""" + bl_label = "Reload" + bl_idname = "scene.scs_reload_library" + bl_description = "Reloads library and updates it with any possible new entries." + + library_path_attr = StringProperty() + + def execute(self, context): + scs_globals = _get_scs_globals() + + # if given library path attribute exists in globals then just rewrite it, + # as this will trigger property update function which will take care of reloading library + if hasattr(scs_globals, self.library_path_attr): + setattr(scs_globals, self.library_path_attr, getattr(scs_globals, self.library_path_attr)) + self.report({'INFO'}, "Library updated!") + else: + self.report({'ERROR'}, "Unknown library, update aborted!") + + return {'FINISHED'} + class Animation: """ @@ -1063,11 +1116,14 @@ def get_platform_depended_game_path(): personal = winreg.QueryValueEx(key, "Personal") game_path = str(personal[0]) - except OSError as e: + except OSError: import traceback - lprint("E Error while looking for My Documents in registry: %r", type(e).__name__) - traceback.print_exc() + trace_str = traceback.format_exc().replace("\n", "\n\t ") + lprint("E Error while looking for My Documents in registry:\n\t %s", + (trace_str,), + report_errors=1, + report_warnings=1) return game_path @@ -1220,8 +1276,7 @@ def execute(self, context): bpy.ops.text.select_all(override) bpy.ops.text.copy(override) - text.user_clear() - bpy.data.texts.remove(text) + bpy.data.texts.remove(text, do_unlink=True) self.report({'INFO'}, "Blender Tools log copied to clipboard!") return {'FINISHED'} diff --git a/addon/io_scs_tools/operators/wm.py b/addon/io_scs_tools/operators/wm.py index af08e1e..f34ccde 100644 --- a/addon/io_scs_tools/operators/wm.py +++ b/addon/io_scs_tools/operators/wm.py @@ -20,9 +20,11 @@ import bpy -from bpy.props import StringProperty, BoolProperty +from bgl import GL_NEAREST +from bpy.props import StringProperty, BoolProperty, IntProperty +from io_scs_tools.consts import Operators as _OP_consts from io_scs_tools.utils import view3d as _view3d_utils -from io_scs_tools.utils import info as _info_utils +from io_scs_tools.utils import path as _path_utils class ShowWarningMessage(bpy.types.Operator): @@ -37,6 +39,8 @@ class ShowWarningMessage(bpy.types.Operator): icon = StringProperty(default="ERROR") title = StringProperty(default="UNKWOWN") message = StringProperty(default="NO MESSAGE") + width = IntProperty(default=300) + height = IntProperty(default=20) @staticmethod def popup_draw(self, context): @@ -49,7 +53,7 @@ def draw(self, context): row.label(" ") col = row.column() - col.label(self.title, icon="ERROR") + col.label(self.title, icon=self.icon) lines = self.message.split("\n") for line in lines: col.label(line) @@ -66,7 +70,7 @@ def invoke(self, context, event): ShowWarningMessage.static_message = self.message if self.is_modal: - return context.window_manager.invoke_props_dialog(self) + return context.window_manager.invoke_props_dialog(self, width=self.width, height=self.height) else: return self.execute_popup(context) @@ -78,14 +82,29 @@ class Show3DViewReport(bpy.types.Operator): __static_is_shown = True """Used for indicating collapsed state of reporter.""" + __static_hide_controls = False + """Used for indicating controls visibility. Controls can be hidden if user shouldn't be able to abort report operator.""" __static_running_instances = 0 """Used for indication of already running operator.""" + __static_title = "" + """Used for saving BT version inside class to be able to retrieve it on open gl draw.""" __static_message_l = [] """Used for saving message inside class to be able to retrieve it on open gl draw.""" - - title = StringProperty(default="UNKWOWN") - message = StringProperty(default="NO MESSAGE") + __static_progress_message_l = [] + """Used for saving progress message inside class to be able to retrieve it on open gl draw.""" + + esc_abort = 0 + """Used for staging ESC key press in operator: + 0 - no ESC request + 1 - ESC was pressed + 2 - ESC was released, ESC is finally captured + """ + + title = StringProperty(default="") + message = StringProperty(default="") abort = BoolProperty(default=False) + hide_controls = BoolProperty(default=False) + is_progress_message = BoolProperty(default=False) @staticmethod def has_lines(): @@ -94,7 +113,41 @@ def has_lines(): :return: True if there is any lines; False otherwise :rtype: bool """ - return len(Show3DViewReport.__static_message_l) > 0 + return len(Show3DViewReport.__static_message_l) > 0 or len(Show3DViewReport.__static_progress_message_l) > 0 + + @staticmethod + def has_controls(): + """Tells if report operator currently has enabled controls. + + :return: True if controls are enabled; False otherwise + :rtype: bool + """ + return not Show3DViewReport.__static_hide_controls + + @staticmethod + def get_scs_logo_img_bindcode(): + """Loads image to blender data block, loads it to gl memory and gets bindcode address that can be used in + bgl module for image drawing. + + :return: bindcode of scs bt logo image + :rtype: int + """ + + if _OP_consts.View3DReport.BT_LOGO_IMG_NAME not in bpy.data.images: + + img_path = _path_utils.get_addon_installation_paths()[0] + "/ui/icons/" + _OP_consts.View3DReport.BT_LOGO_IMG_NAME + img = bpy.data.images.load(img_path, check_existing=True) + + else: + + img = bpy.data.images[_OP_consts.View3DReport.BT_LOGO_IMG_NAME] + + # ensure that image is loaded in GPU memory aka has proper bindcode, + # we have to that each time because if operator is shown for long time blender might free it on his own + if img.bindcode[0] == 0: + img.gl_load(0, GL_NEAREST, GL_NEAREST) + + return img.bindcode[0] @staticmethod def get_lines(): @@ -103,7 +156,19 @@ def get_lines(): :return: message in lines :rtype: list[str] """ - return Show3DViewReport.__static_message_l + lines = [] + lines.extend(Show3DViewReport.__static_progress_message_l) + lines.extend(Show3DViewReport.__static_message_l) + return lines + + @staticmethod + def get_title(): + """Get title as string. + + :return: title which should be carrying info about Blender Tools version + :rtype: str + """ + return Show3DViewReport.__static_title @staticmethod def is_shown(): @@ -114,15 +179,59 @@ def is_shown(): """ return Show3DViewReport.__static_is_shown - def __del__(self): - Show3DViewReport.__static_running_instances -= 1 + @staticmethod + def is_in_btn_area(x, y, btn_area): + """Tells if given x and y coordinates are inside given area. + + :param x: x + :type x: int + :param y: y + :type y: int + :param btn_area: tuple holding x and y borders (min_x, max_x, min_y, max_y) + :type btn_area: tuple + :return: True if x and y are inside button area; False otherwise + :rtype: bool + """ + return (btn_area[0] < x < btn_area[1] and + btn_area[2] < y < btn_area[3]) def __init__(self): Show3DViewReport.__static_running_instances += 1 + def __del__(self): + if Show3DViewReport.__static_running_instances > 0: + + Show3DViewReport.__static_running_instances -= 1 + + # if user disables add-on, destructor is called again, so cleanup static variables + if Show3DViewReport.__static_running_instances <= 0: + + Show3DViewReport.__static_title = "" + Show3DViewReport.__static_message_l.clear() + Show3DViewReport.__static_progress_message_l.clear() + def modal(self, context, event): - if event.type == "LEFTMOUSE" and event.value in {'PRESS'}: + # if operator doesn't have controls, then it can not be cancelled by user, + # so we should simply pass trough + if not Show3DViewReport.has_controls(): + return {'PASS_THROUGH'} + + # handle ESC press + # NOTE: do it in stages to prevent canceling operator while user tries to abort + # current action in blender (for example user entered scaling and then aborted with ESC) + if event.type == "ESC" and event.value == "PRESS" and self.esc_abort == 0: + self.esc_abort = 1 + elif event.type == "ESC" and event.value == "RELEASE" and self.esc_abort == 1: + self.esc_abort = 2 + elif event.type != "MOUSEMOVE": + self.esc_abort = 0 + + if (event.type == "LEFTMOUSE" and event.value in {'PRESS'}) or self.esc_abort == 2: + + # make sure to reset ESC abort state to 0 so next press will be properly handled + if self.esc_abort == 2: + self.esc_abort = 0 for area in context.screen.areas: if area.type != 'VIEW_3D': @@ -135,11 +244,31 @@ def modal(self, context, event): curr_x = event.mouse_x - region.x curr_y = region.height - (event.mouse_y - region.y) - if 20 < curr_x < 95 and 38 < curr_y < 62: # close + # if mouse cursor is over 3D view and ESC was pressed then switch to hide mode or exit operator + if event.type == "ESC" and area.x < event.mouse_x < area.x + area.width and area.y < event.mouse_y < area.y + area.height: + + # if shown first hide extended view and then if hidden it can be closed + # NOTE: there is two stage exit on ESC because user might hit ESC unintentionally. + # Plus in case some transformation was in progress (like translation) ESC will cancel it and + # in worst case only hide 3d view logging operator, if stage ESC handling fails to capture that + if Show3DViewReport.__static_is_shown: + + Show3DViewReport.__static_is_shown = False + + _view3d_utils.tag_redraw_all_view3d() + return {'RUNNING_MODAL'} + + else: + + self.cancel(context) + return {'FINISHED'} + + # also exit/cancel operator if Close button area was clicked + if Show3DViewReport.is_in_btn_area(curr_x, curr_y, _OP_consts.View3DReport.CLOSE_BTN_AREA): # close self.cancel(context) return {'FINISHED'} - if 100 < curr_x < 250 and 38 < curr_y < 62: # show/hide + if Show3DViewReport.is_in_btn_area(curr_x, curr_y, _OP_consts.View3DReport.HIDE_BTN_AREA): # show/hide Show3DViewReport.__static_is_shown = not Show3DViewReport.__static_is_shown @@ -150,7 +279,16 @@ def modal(self, context, event): def cancel(self, context): + # free BT logo image resources + if _OP_consts.View3DReport.BT_LOGO_IMG_NAME in bpy.data.images: + + img = bpy.data.images[_OP_consts.View3DReport.BT_LOGO_IMG_NAME] + img.gl_free() + + bpy.data.images.remove(img, do_unlink=True) + Show3DViewReport.__static_message_l.clear() + Show3DViewReport.__static_progress_message_l.clear() _view3d_utils.tag_redraw_all_view3d() @@ -158,17 +296,28 @@ def invoke(self, context, event): # if abort is requested just cancel operator if self.abort: - self.cancel(context) - return {'FINISHED'} - # reset messages array and shown state + if self.is_progress_message: + Show3DViewReport.__static_progress_message_l.clear() + + if len(Show3DViewReport.__static_message_l) > 0: + Show3DViewReport.__static_hide_controls = False + else: + self.cancel(context) + else: + self.cancel(context) + + return {'CANCELLED'} + + # reset flags in static variables Show3DViewReport.__static_is_shown = True - Show3DViewReport.__static_message_l.clear() + Show3DViewReport.__static_hide_controls = self.hide_controls - # add blender tools version in the header - Show3DViewReport.__static_message_l.append(_info_utils.get_combined_ver_str()) + # assign title to static variable + Show3DViewReport.__static_title = self.title # split message by new lines + message_l = [] for line in self.message.split("\n"): # remove tabulator simulated new lines from warnings and errors, written like: "\n\t " @@ -177,14 +326,20 @@ def invoke(self, context, event): # remove tabulator simulated empty space before warning or error line of summaries e.g "\t > " line = line.replace("\t ", "") - Show3DViewReport.__static_message_l.append(line) + message_l.append(line) - # if report operator is already running don't add new modal handler - if Show3DViewReport.__static_running_instances == 1: - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} + # properly assign parsed message list depending on progress flag + if self.is_progress_message: + Show3DViewReport.__static_progress_message_l = message_l else: - return {'FINISHED'} + Show3DViewReport.__static_message_l = message_l + + # if report operator is already running don't add new modal handler + if Show3DViewReport.__static_running_instances > 1: + return {'CANCELLED'} + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} class ShowDeveloperErrors(bpy.types.Operator): diff --git a/addon/io_scs_tools/operators/world.py b/addon/io_scs_tools/operators/world.py new file mode 100644 index 0000000..dafc48f --- /dev/null +++ b/addon/io_scs_tools/operators/world.py @@ -0,0 +1,481 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2013-2014: SCS Software + +import bpy +from math import pi +from time import time +from bpy.props import BoolProperty, IntProperty, StringProperty, CollectionProperty +from io_scs_tools.internals.shaders.eut2.std_node_groups import add_env as _add_env_node_group +from io_scs_tools.internals.shaders.eut2.std_node_groups import compose_lighting as _compose_lighting +from io_scs_tools.consts import SCSLigthing as _LIGHTING_consts +from io_scs_tools.utils import convert as _convert_utils +from io_scs_tools.utils import view3d as _view3d_utils +from io_scs_tools.utils import get_scs_globals as _get_scs_globals +from io_scs_tools.utils.printout import lprint + + +class UseSunProfile(bpy.types.Operator): + bl_label = "Use Sun Profile" + bl_idname = "world.scs_use_sun_profile" + bl_description = "Setups lighting according to this sun profile." + + __static_last_profile_i = -1 + """Static variable saving index of sun profile currently used in lighting scene. + Variable is used when operator is called with sun profile index -1, + to determinate from which sun profile light data should be taken while seting up lighting scene.""" + + sun_profile_index = IntProperty(default=-1) + skip_background_set = BoolProperty(default=False) + + def execute(self, context): + lprint('D ' + self.bl_label + "...") + + scs_globals = _get_scs_globals() + + # get sun profile index depending on input + if self.sun_profile_index == -1: + + # if sun profile index is not given (equal to -1) then try to retrieve last used profile index + # from static variable; otherwise use currently selected from list in UI + if UseSunProfile.__static_last_profile_i != -1: + sun_profile_i = UseSunProfile.__static_last_profile_i + else: + sun_profile_i = scs_globals.sun_profiles_inventory_active + + else: + sun_profile_i = self.sun_profile_index + + # validate sun profile index + if sun_profile_i < 0 or sun_profile_i > len(scs_globals.sun_profiles_inventory): + lprint("E Using active sun profile failed!") + return {'FINISHED'} + + # update static variable for sun profile index + UseSunProfile.__static_last_profile_i = sun_profile_i + + sun_profile_item = scs_globals.sun_profiles_inventory[sun_profile_i] + """:type: io_scs_tools.properties.world.GlobalSCSProps.SunProfileInventoryItem""" + + # convert elevation into rotation direction vector for diffuse and specular lamps + # NOTE: this is not exact as in game but should be sufficient for representation + elevation_avg = (sun_profile_item.low_elevation + sun_profile_item.high_elevation) / 2 + elevation_direction = 1 if sun_profile_item.sun_direction == 1 else -1 + directed_lamps_rotation = ( + 0, + elevation_direction * (pi / 2 - (pi * elevation_avg / 180)), # elevation + pi * scs_globals.lighting_scene_east_direction / 180 # current BT user defined east direction + ) + + # 1. create lighting scene + if _LIGHTING_consts.scene_name not in bpy.data.scenes: + lighting_scene = bpy.data.scenes.new(_LIGHTING_consts.scene_name) + else: + lighting_scene = bpy.data.scenes[_LIGHTING_consts.scene_name] + + lighting_scene.layers = [True] * 20 + + # 2. create ambient lights + _compose_lighting.set_additional_ambient_col(sun_profile_item.ambient * sun_profile_item.ambient_hdr_coef) + for ambient_lamp_name, ambient_lamp_direction, ambient_lamp_factor in _LIGHTING_consts.ambient_lamps: + + if ambient_lamp_name in bpy.data.lamps: + lamp_data = bpy.data.lamps[ambient_lamp_name] + else: + lamp_data = bpy.data.lamps.new(ambient_lamp_name, "HEMI") + + lamp_data.type = "HEMI" + lamp_data.use_specular = False + lamp_data.use_diffuse = True + lamp_data.energy = sun_profile_item.ambient_hdr_coef * ambient_lamp_factor + lamp_data.color = sun_profile_item.ambient + + if ambient_lamp_name in bpy.data.objects: + lamp_obj = bpy.data.objects[ambient_lamp_name] + lamp_obj.data = lamp_data + else: + lamp_obj = bpy.data.objects.new(ambient_lamp_name, lamp_data) + + lamp_obj.hide = True + lamp_obj.rotation_euler = ambient_lamp_direction + lamp_obj.rotation_euler[2] = directed_lamps_rotation[2] + lamp_obj.layers = [True] * 20 # enable it on all layers + + if lamp_obj.name not in lighting_scene.objects: + lighting_scene.objects.link(lamp_obj) + + # 3. create sun lamp for diffuse and setup it's direction according elevation average + if _LIGHTING_consts.diffuse_lamp_name in bpy.data.lamps: + lamp_data = bpy.data.lamps[_LIGHTING_consts.diffuse_lamp_name] + else: + lamp_data = bpy.data.lamps.new(_LIGHTING_consts.diffuse_lamp_name, "SUN") + + lamp_data.type = "SUN" + lamp_data.use_specular = False + lamp_data.use_diffuse = True + lamp_data.energy = sun_profile_item.diffuse_hdr_coef + lamp_data.color = _convert_utils.linear_to_srgb(sun_profile_item.diffuse) + + if _LIGHTING_consts.diffuse_lamp_name in bpy.data.objects: + lamp_obj = bpy.data.objects[_LIGHTING_consts.diffuse_lamp_name] + lamp_obj.data = lamp_data + else: + lamp_obj = bpy.data.objects.new(_LIGHTING_consts.diffuse_lamp_name, lamp_data) + + lamp_obj.hide = True + lamp_obj.rotation_euler = directed_lamps_rotation + lamp_obj.layers = [True] * 20 # enable it on all layers + + if lamp_obj.name not in lighting_scene.objects: + lighting_scene.objects.link(lamp_obj) + + # 4. create sun lamp for specular and setup it's direction according elevation average + if _LIGHTING_consts.specular_lamp_name in bpy.data.lamps: + lamp_data = bpy.data.lamps[_LIGHTING_consts.specular_lamp_name] + else: + lamp_data = bpy.data.lamps.new(_LIGHTING_consts.specular_lamp_name, "SUN") + + lamp_data.type = "SUN" + lamp_data.use_specular = True + lamp_data.use_diffuse = False + lamp_data.energy = sun_profile_item.specular_hdr_coef + lamp_data.color = _convert_utils.linear_to_srgb(sun_profile_item.specular) + + if _LIGHTING_consts.specular_lamp_name in bpy.data.objects: + lamp_obj = bpy.data.objects[_LIGHTING_consts.specular_lamp_name] + lamp_obj.data = lamp_data + else: + lamp_obj = bpy.data.objects.new(_LIGHTING_consts.specular_lamp_name, lamp_data) + + lamp_obj.hide = True + lamp_obj.rotation_euler = directed_lamps_rotation + lamp_obj.layers = [True] * 20 # enable it on all layers + + if lamp_obj.name not in lighting_scene.objects: + lighting_scene.objects.link(lamp_obj) + + # 5. search for AddEnv node group and setup coefficient for environment accordingly + _add_env_node_group.set_global_env_factor(sun_profile_item.env * sun_profile_item.env_static_mod) + + # 6. set lighting scene as background scene of current scene if skipping wasn't requested in input params + if not self.skip_background_set: + + if context.scene and context.scene != lighting_scene: + context.scene.background_set = lighting_scene + else: + lprint("E Lighting scene created but not used, as currently there is no active scene!") + + return {'FINISHED'} + + +class RemoveSCSLightingFromScene(bpy.types.Operator): + bl_label = "Switch Off SCS Lighting in Current Scene" + bl_idname = "world.scs_disable_lighting_in_scene" + bl_description = "Disables SCS lighting in current scene, giving you ability to setup your own lighting." + + def execute(self, context): + lprint('D ' + self.bl_label + "...") + + old_scene = None + if context.scene and context.scene.background_set and context.scene.background_set.name == _LIGHTING_consts.scene_name: + old_scene = context.scene + old_scene.background_set = None + + # if scs lighting scene is not present in blend file anymore, we are done + if _LIGHTING_consts.scene_name not in bpy.data.scenes: + return {'FINISHED'} + + # otherwise check if current scene was the last using SCS lighting, + # if so then make sure to cleanup lamp objects and delete the scene + used_counter = 0 + for scene in bpy.data.scenes: + + if scene.name == _LIGHTING_consts.scene_name: + continue + + if scene.background_set == bpy.data.scenes[_LIGHTING_consts.scene_name]: + used_counter += 1 + + if used_counter == 0: + + # firstly remove the lighting scene + override = { + 'window': context.window, + 'screen': context.screen, + 'blend_data': context.blend_data, + 'scene': bpy.data.scenes[_LIGHTING_consts.scene_name], + 'region': None, + 'area': None, + 'edit_object': None, + 'active_object': None, + 'selected_objects': None, + } + bpy.ops.scene.delete(override, 'INVOKE_DEFAULT') + + # now gather all lamp objects names + lamp_names = [_LIGHTING_consts.diffuse_lamp_name, _LIGHTING_consts.specular_lamp_name] + for lamp_name, lamp_dir, lamp_factor in _LIGHTING_consts.ambient_lamps: + lamp_names.append(lamp_name) + + # lastly delete blend objects and lamps + for lamp_name in lamp_names: + + if lamp_name in bpy.data.objects: + bpy.data.objects.remove(bpy.data.objects[lamp_name], do_unlink=True) + + if lamp_name in bpy.data.lamps: + bpy.data.lamps.remove(bpy.data.lamps[lamp_name], do_unlink=True) + + # while we delete one scene blender might/will select first available as current, + # so we have to force our screen to be using old scene again + if old_scene and context.screen: + context.screen.scene = old_scene + + return {'FINISHED'} + + +class SCSPathsInitialization(bpy.types.Operator): + bl_label = "" + bl_description = "Initializes SCS Blender Tools filepaths asynchronously with proper reporting in 3D view." + bl_idname = "world.scs_paths_initialization" + + DUMP_LEVEL = 3 + """Constant for log level index according in SCS Globals, on which operator should printout extended report.""" + + # Static running variables + __timer = None + """Timer instance variable. We use timer to initilize paths gradually one by one.""" + __last_time = None + """Used for time tracking on each individual path initialization.""" + __path_in_progress = False + """Used as flag for indicating path being processed. So another execute method call shouldn't be triggered.""" + __static_paths_count = 0 + """Static variable holding number of all paths that had to be processed. Used for reporting progress eg. 'X of Y paths done'.""" + __static_paths_done = 0 + """Static variable holding number of already processed paths. Used for reporting progress eg. 'X of Y paths done'.""" + __static_running_instances = 0 + """Used for indication of already running operator.""" + + # Static data storage + __static_message = "" + """Static variable holding printout extended message. This message used only if dump level is high enough.""" + __static_paths_list = [] + """Static variable holding CollectionProperty with entries of Filepath class, defining paths that needs initialization. + Processed paths are removed on the fly. + """ + __static_callbacks = [] + """Static variable holding list of callbacks that will be executed once operator is finished or cancelled. + """ + + class Filepath(bpy.types.PropertyGroup): + """ + Entry for file paths collection that should be set on SCS globals asynchronously inside SCSToolsInitialization operator. + """ + name = StringProperty(description="Friendly name used for printouts.") + attr = StringProperty(description="Name of the property in SCSGlobalsProps class.") + path = StringProperty(description="Actual file/dir path that should be applied to the property.") + + paths_list = CollectionProperty( + type=Filepath, + description= + """ + List of paths that should be initialized, used only for passing filepaths data to operator. + During operator invoke this list extends static paths list. Accessing paths via static variable + is needed for multiple operator invoking from different places. As this enables us having common + up to date list for processing paths in order and none of it stays unproperly processed. + """ + ) + + @staticmethod + def is_running(): + """Tells if paths initialization is still in progress. + + :return: True if scs paths initialization is still in progress; False if none instances are running + :rtype: bool + """ + return SCSPathsInitialization.__static_running_instances > 0 + + @staticmethod + def append_callback(callback): + """Appends given callback function to callback list. Callbacks are called once paths initialization is done. + If operator is not running then False is returned and callback is not added to the list! + NOTE: there is no check if given callback is already in list. + + :param callback: callback function without arguments + :type callback: object + :return: True if operator is running and callback is added to the list properly; False if callback won't be added and executed + :rtype: bool + """ + if SCSPathsInitialization.is_running(): + SCSPathsInitialization.__static_callbacks.append(callback) + return True + + return False + + def __init__(self): + SCSPathsInitialization.__static_running_instances += 1 + + def __del__(self): + if SCSPathsInitialization.__static_running_instances > 0: + + SCSPathsInitialization.__static_running_instances -= 1 + + # if user disables add-on, destructor is called again, so cleanup timer and static variables + if SCSPathsInitialization.__static_running_instances <= 0: + + SCSPathsInitialization.__static_message = "" + SCSPathsInitialization.__static_paths_list = [] + SCSPathsInitialization.__static_callbacks = [] + + if bpy.context and bpy.context.window_manager and self.__timer: + wm = bpy.context.window_manager + wm.event_timer_remove(self.__timer) + + def execute(self, context): + + # do not proceed if list is already empty + if len(SCSPathsInitialization.__static_paths_list) <= 0: + return {'FINISHED'} + + self.__path_in_progress = True + + # update message with current path and apply it + SCSPathsInitialization.__static_message += "Initializing " + SCSPathsInitialization.__static_paths_list[0].name + "..." + setattr(_get_scs_globals(), SCSPathsInitialization.__static_paths_list[0].attr, SCSPathsInitialization.__static_paths_list[0].path) + SCSPathsInitialization.__static_paths_list.remove(0) # remove just processed item + SCSPathsInitialization.__static_message += " Done in %.2f s!\n" % (time() - self.__last_time) + SCSPathsInitialization.__static_paths_done += 1 + + # when executing last one, also print out hiding message + if len(SCSPathsInitialization.__static_paths_list) == 0: + SCSPathsInitialization.__static_message += "SCS Blender Tools are ready!" + _view3d_utils.tag_redraw_all_view3d() + + self.__last_time = time() # reset last time for next path + + self.__path_in_progress = False + + # if debug then report whole progress message otherwise print out condensed message + if int(_get_scs_globals().dump_level) >= self.DUMP_LEVEL: + message = SCSPathsInitialization.__static_message + hide_controls = False + else: + message = "Paths and libraries initialization %s/%s ..." % (SCSPathsInitialization.__static_paths_done, + SCSPathsInitialization.__static_paths_count) + hide_controls = True + bpy.ops.wm.show_3dview_report('INVOKE_DEFAULT', message=message, hide_controls=hide_controls, is_progress_message=True) + + return {'FINISHED'} + + def cancel(self, context): + + # reset static variables + SCSPathsInitialization.__static_message = "" + SCSPathsInitialization.__static_paths_list.clear() + + wm = context.window_manager + wm.event_timer_remove(self.__timer) + + # report finished progress to 3d view report operator + if int(_get_scs_globals().dump_level) < self.DUMP_LEVEL: + bpy.ops.wm.show_3dview_report('INVOKE_DEFAULT', abort=True, is_progress_message=True) + + # when done, tag everything for redraw in the case some UI components + # are reporting status of this operator + _view3d_utils.tag_redraw_all_regions() + + # as last invoke any callbacks and afterwards delete them + while len(SCSPathsInitialization.__static_callbacks) > 0: + + callback = SCSPathsInitialization.__static_callbacks[0] + + callback() + SCSPathsInitialization.__static_callbacks.remove(callback) + + def modal(self, context, event): + + if event.type == "TIMER": # process timer event + + if len(SCSPathsInitialization.__static_paths_list) <= 0: # once no more paths to process abort it + + # NOTE: canceling has to be done in timer event. + # Otherwise finishing operator with status 'FINISHED' eats event and + # stops event in this operator and cancels action which user wanted to do. + self.cancel(context) + lprint("I Paths initialization done, deleting operator!") + return {'FINISHED'} + + if not self.__path_in_progress: # if not in progress then trigger execute and process next + + self.execute(context) + + return {'PASS_THROUGH'} + + def invoke(self, context, event): + + SCSPathsInitialization.__static_paths_done = 0 # reset done paths counter as everything starts here + + # if another instance is running update static paths list with paths passed to this instance + if SCSPathsInitialization.__static_running_instances > 1: + + # now fill up new paths to static inventory + for filepath_prop in self.paths_list: + + # search for identical path in not yet processed paths list + old_item = None + for item in SCSPathsInitialization.__static_paths_list: + if item.attr == filepath_prop.attr: + old_item = item + break + + # if old item is found just reuse it instead of adding new item to list + item = old_item if old_item else SCSPathsInitialization.__static_paths_list.add() + item.name = filepath_prop.name + item.attr = filepath_prop.attr + item.path = filepath_prop.path + + # update paths counter to the current paths list length + SCSPathsInitialization.__static_paths_count = len(SCSPathsInitialization.__static_paths_list) + + # cancel this instance if another instance is still running + # (it can happen that previous operator was done until now as each operator runs asynchronously, + # in that case we have to continue otherwise latest paths won't be ) + if SCSPathsInitialization.__static_running_instances > 0: + + SCSPathsInitialization.__static_message = "Restarting initialization...\n\n" + bpy.ops.wm.show_3dview_report('INVOKE_DEFAULT', message=SCSPathsInitialization.__static_message, + hide_controls=True, is_progress_message=True) + + lprint("D Restarting initialization...!") + return {'CANCELLED'} + + SCSPathsInitialization.__static_message = "Starting initialization...\n" + bpy.ops.wm.show_3dview_report('INVOKE_DEFAULT', message=SCSPathsInitialization.__static_message, + hide_controls=True, is_progress_message=True) + + SCSPathsInitialization.__static_paths_list = self.paths_list + SCSPathsInitialization.__static_paths_count = len(self.paths_list) + + wm = context.window_manager + self.__timer = wm.event_timer_add(0.2, context.window) + self.__last_time = time() + + wm.modal_handler_add(self) + lprint("I Paths initialization started...") + return {'RUNNING_MODAL'} diff --git a/addon/io_scs_tools/properties/dynamic/scene.py b/addon/io_scs_tools/properties/dynamic/scene.py index c5eba14..acff7e4 100644 --- a/addon/io_scs_tools/properties/dynamic/scene.py +++ b/addon/io_scs_tools/properties/dynamic/scene.py @@ -39,10 +39,17 @@ def _set_num_objects(self, value): def _get_active_scs_root(self): if "scs_cached_active_scs_root" not in self: - _set_active_scs_root(self, self.objects.active.name) + + # if there is no active object set empty string + if self.objects.active: + name = self.objects.active.name + else: + name = "" + + _set_active_scs_root(self, name) return self["scs_cached_active_scs_root"] def _set_active_scs_root(self, value): - self["scs_cached_active_scs_root"] = value \ No newline at end of file + self["scs_cached_active_scs_root"] = value diff --git a/addon/io_scs_tools/properties/material.py b/addon/io_scs_tools/properties/material.py index b70334a..ade4efd 100644 --- a/addon/io_scs_tools/properties/material.py +++ b/addon/io_scs_tools/properties/material.py @@ -482,7 +482,8 @@ def update_shader_texture_reflection_settings(self, context): name="Add Ambient", description="SCS shader 'Add Ambient' value", default=0.0, - min=0.0, + min=-10.0, + max=10.0, step=1, precision=2, options={'HIDDEN'}, diff --git a/addon/io_scs_tools/properties/object.py b/addon/io_scs_tools/properties/object.py index 8a9caf4..3ce506a 100644 --- a/addon/io_scs_tools/properties/object.py +++ b/addon/io_scs_tools/properties/object.py @@ -849,6 +849,7 @@ def locator_prefab_type_update(self, context): (_PL_consts.PSP.UNLOAD_HARD_POS, (str(_PL_consts.PSP.UNLOAD_HARD_POS), "Unload (Hard)", "")), (_PL_consts.PSP.UNLOAD_RIGID_POS, (str(_PL_consts.PSP.UNLOAD_RIGID_POS), "Unload (Rigid)", "")), (_PL_consts.PSP.WEIGHT_POS, (str(_PL_consts.PSP.WEIGHT_POS), "Weight Station", "")), + (_PL_consts.PSP.WEIGHT_CAT_POS, (str(_PL_consts.PSP.WEIGHT_CAT_POS), "Weight Station CAT", "")), ]) # LOCATORS - PREFAB - SPAWN POINTS locator_prefab_spawn_type = EnumProperty( @@ -881,10 +882,12 @@ def locator_prefab_type_update(self, context): # (_PL_consts.TST.TRAFFIC_LIGHT, (str(_PL_consts.TST.TRAFFIC_LIGHT), "Traffic Light", "")), (_PL_consts.TST.TRAFFIC_LIGHT_MINOR, (str(_PL_consts.TST.TRAFFIC_LIGHT_MINOR), "Traffic Light (minor road)", "")), (_PL_consts.TST.TRAFFIC_LIGHT_MAJOR, (str(_PL_consts.TST.TRAFFIC_LIGHT_MAJOR), "Traffic Light (major road)", "")), + (_PL_consts.TST.TRAFFIC_LIGHT_VIRTUAL, (str(_PL_consts.TST.TRAFFIC_LIGHT_VIRTUAL), "Traffic Light (virtual)", "")), (_PL_consts.TST.TRAFFIC_LIGHT_BLOCKABLE, (str(_PL_consts.TST.TRAFFIC_LIGHT_BLOCKABLE), "Blockable Traffic Light", "")), - (_PL_consts.TST.BARRIER_MANUAL_TIMED, (str(_PL_consts.TST.BARRIER_MANUAL_TIMED), "Barrier - Manual Timed", "")), + (_PL_consts.TST.BARRIER_MANUAL_TIMED, (str(_PL_consts.TST.BARRIER_MANUAL_TIMED), "Barrier - Manual", "")), (_PL_consts.TST.BARRIER_GAS, (str(_PL_consts.TST.BARRIER_GAS), "Barrier - Gas", "")), (_PL_consts.TST.BARRIER_DISTANCE, (str(_PL_consts.TST.BARRIER_DISTANCE), "Barrier - Distance Activated", "")), + (_PL_consts.TST.BARRIER_AUTOMATIC, (str(_PL_consts.TST.BARRIER_AUTOMATIC), "Barrier - Automatic", "")), ]) locator_prefab_tsem_type = EnumProperty( name="Type", @@ -1058,20 +1061,50 @@ def locator_prefab_type_update(self, context): default=False, ) enum_mp_road_size_items = OrderedDict([ - (_PL_consts.MPVF.ROAD_SIZE_AUTO, (str(_PL_consts.MPVF.ROAD_SIZE_AUTO), - "Auto", "The road size is automatically determmined based on connected roads")), - (_PL_consts.MPVF.ROAD_SIZE_ONE_WAY, (str(_PL_consts.MPVF.ROAD_SIZE_ONE_WAY), - "One Way", "Narrow road (one direction with one lane)")), - (_PL_consts.MPVF.ROAD_SIZE_1_LANE, (str(_PL_consts.MPVF.ROAD_SIZE_1_LANE), - "1 - Lane", "One lane in both directions")), - (_PL_consts.MPVF.ROAD_SIZE_2_LANE, (str(_PL_consts.MPVF.ROAD_SIZE_2_LANE), - "2 - Lane", "Two lanes in both directions")), - (_PL_consts.MPVF.ROAD_SIZE_3_LANE, (str(_PL_consts.MPVF.ROAD_SIZE_3_LANE), - "3 - Lane", "Three lanes in both directions")), - (_PL_consts.MPVF.ROAD_SIZE_4_LANE, (str(_PL_consts.MPVF.ROAD_SIZE_4_LANE), - "4 - Lane", "Four lanes in both directions")), - (_PL_consts.MPVF.ROAD_SIZE_MANUAL, (str(_PL_consts.MPVF.ROAD_SIZE_MANUAL), - "Polygon", "The map point is used to draw a polygon instead of a road")), + (_PL_consts.MPVF.ROAD_SIZE_AUTO, ( + str(_PL_consts.MPVF.ROAD_SIZE_AUTO), + "Auto", "The road size is automatically determmined based on connected roads." + )), + (_PL_consts.MPVF.ROAD_SIZE_ONE_WAY, ( + str(_PL_consts.MPVF.ROAD_SIZE_ONE_WAY), + "One Way", "Narrow road (one direction with one lane)." + )), + (_PL_consts.MPVF.ROAD_SIZE_1_LANE, ( + str(_PL_consts.MPVF.ROAD_SIZE_1_LANE), + "1 + 1 (2 + 0) Lane", "One lane in both directions. Or one way road with 2 lanes." + )), + (_PL_consts.MPVF.ROAD_SIZE_3_LANE_ONE_WAY, ( + str(_PL_consts.MPVF.ROAD_SIZE_3_LANE_ONE_WAY), + "2 + 1 (3 + 0) Lane", "Two lanes in one direction and one lane in second direction. Or one way with 3 lanes." + )), + (_PL_consts.MPVF.ROAD_SIZE_2_LANE, ( + str(_PL_consts.MPVF.ROAD_SIZE_2_LANE), + "2 + 2 (4 + 0) Lane", "Two lanes in both directions. Or one way road with 4 lanes." + )), + (_PL_consts.MPVF.ROAD_SIZE_2_LANE_SPLIT, ( + str(_PL_consts.MPVF.ROAD_SIZE_2_LANE_SPLIT), + "2 + 1 + 2 Lane", "Two lanes in both directions + one turning lane." + )), + (_PL_consts.MPVF.ROAD_SIZE_3_LANE, ( + str(_PL_consts.MPVF.ROAD_SIZE_3_LANE), + "3 + 3 Lane", "Three lanes in both directions." + )), + (_PL_consts.MPVF.ROAD_SIZE_3_LANE_SPLIT, ( + str(_PL_consts.MPVF.ROAD_SIZE_3_LANE_SPLIT), + "3 + 1 + 3 Lane", "Three lanes in both directions + one turning lane." + )), + (_PL_consts.MPVF.ROAD_SIZE_4_LANE, ( + str(_PL_consts.MPVF.ROAD_SIZE_4_LANE), + "4 + 4 Lane", "Four lanes in both directions." + )), + # (_PL_consts.MPVF.ROAD_SIZE_4_LANE_SPLIT, ( + # str(_PL_consts.MPVF.ROAD_SIZE_4_LANE_SPLIT), + # "4 + 1 + 4 Lane", "" + # )), + (_PL_consts.MPVF.ROAD_SIZE_MANUAL, ( + str(_PL_consts.MPVF.ROAD_SIZE_MANUAL), + "Polygon", "The map point is used to draw a polygon instead of a road." + )), ]) locator_prefab_mp_road_size = EnumProperty( name="Road Size", @@ -1088,6 +1121,7 @@ def locator_prefab_type_update(self, context): (_PL_consts.MPVF.ROAD_OFFSET_15, (str(_PL_consts.MPVF.ROAD_OFFSET_15), "15 m", "")), (_PL_consts.MPVF.ROAD_OFFSET_20, (str(_PL_consts.MPVF.ROAD_OFFSET_20), "20 m", "")), (_PL_consts.MPVF.ROAD_OFFSET_25, (str(_PL_consts.MPVF.ROAD_OFFSET_25), "25 m", "")), + # (_PL_consts.MPVF.ROAD_OFFSET_LANE, (str(_PL_consts.MPVF.ROAD_OFFSET_LANE), "", "")), ]) locator_prefab_mp_road_offset = EnumProperty( name="Road Offset", diff --git a/addon/io_scs_tools/properties/world.py b/addon/io_scs_tools/properties/world.py index 427566d..dc500b7 100644 --- a/addon/io_scs_tools/properties/world.py +++ b/addon/io_scs_tools/properties/world.py @@ -357,23 +357,35 @@ def update_shader_preset_search_value(self, context): def scs_project_path_update(self, context): # Update all related paths so their libraries gets reloaded from new "SCS Project Path" location. if not _get_scs_globals().config_update_lock: - _config_container.update_sign_library_rel_path(_get_scs_globals().scs_sign_model_inventory, - _get_scs_globals().sign_library_rel_path) - _config_container.update_tsem_library_rel_path(_get_scs_globals().scs_tsem_profile_inventory, - _get_scs_globals().tsem_library_rel_path) + _config_container.update_item_in_file('Paths.ProjectPath', self.scs_project_path) - _config_container.update_traffic_rules_library_rel_path(_get_scs_globals().scs_traffic_rules_inventory, - _get_scs_globals().traffic_rules_library_rel_path) + scs_globals = _get_scs_globals() - _config_container.update_hookup_library_rel_path(_get_scs_globals().scs_hookup_inventory, - _get_scs_globals().hookup_library_rel_path) + # enable update lock because we only want to reload libraries, as their paths are not changed + _config_container.engage_config_lock() - _config_container.update_matsubs_inventory(_get_scs_globals().scs_matsubs_inventory, - _get_scs_globals().matsubs_library_rel_path) + # trigger update functions via asynchronous operator for library paths initialization + bpy.ops.world.scs_paths_initialization('INVOKE_DEFAULT', paths_list=[ + {"name": "trigger actions library", "attr": "trigger_actions_rel_path", "path": scs_globals.trigger_actions_rel_path}, + {"name": "sign library", "attr": "sign_library_rel_path", "path": scs_globals.sign_library_rel_path}, + {"name": "traffic semaphore library", "attr": "tsem_library_rel_path", "path": scs_globals.tsem_library_rel_path}, + {"name": "traffic rules library", "attr": "traffic_rules_library_rel_path", "path": scs_globals.traffic_rules_library_rel_path}, + {"name": "hookups library", "attr": "hookup_library_rel_path", "path": scs_globals.hookup_library_rel_path}, + {"name": "material substance library", "attr": "matsubs_library_rel_path", "path": scs_globals.matsubs_library_rel_path}, + {"name": "sun profiles library", "attr": "sun_profiles_lib_path", "path": scs_globals.sun_profiles_lib_path}, + ]) - _config_container.update_item_in_file('Paths.ProjectPath', self.scs_project_path) + # release lock as properties are applied + _config_container.release_config_lock(use_paths_init_callback=True) + + return None + + def use_alternative_bases_update(self, context): + + _config_container.update_item_in_file('Paths.UseAlternativeBases', int(self.use_alternative_bases)) + self.scs_project_path_update(context) return None def shader_presets_filepath_update(self, context): @@ -459,6 +471,25 @@ def matsubs_library_rel_path_update(self, context): subtype='NONE', update=scs_project_path_update ) + use_alternative_bases = BoolProperty( + name="Use SCS Resources and Libraries From Alternative Bases", + description="When used, all resources with relative paths ('//') will also be searched for inside alternative 'base' directories.\n\n" + "For example let's say we have:\n" + "- SCS Project Base Path: 'D:/projects/ets_mod_1/my_mod_base'\n" + "- Sign Library: '//def/world/sign.sii'\n\n" + "then you can use texture resources from:\n\n" + "1. 'D:/projects/ets_mod_1/my_mod_base'\n" + "2. 'D:/projects/ets_mod_1/base'\n" + "3. 'D:/projects/base'\n\n" + "and sign library will be loaded from this files:\n\n" + "1. 'D:/projects/ets_mod_1/my_mod_base/def/world/sign.sii'\n" + "2. 'D:/projects/ets_mod_1/base/def/world/sign.sii'\n" + "3. 'D:/projects/base/def/world/sign.sii'\n\n" + "In short, alternative 'base' paths are intended for any existing resources from 'base.scs'\n" + "that you don't want to pack with your mod, but still use in SCS Blender Tools.\n", + default=True, + update=use_alternative_bases_update, + ) shader_presets_filepath = StringProperty( name="Shader Presets Library", description="Shader Presets library file path (absolute file path; *.txt)", @@ -1305,3 +1336,122 @@ def path_update(self, context): ), default=_CONV_HLPR_consts.DeflatedZip ) + + # SUN PROFILE SETTINGS + class SunProfileInventoryItem(bpy.types.PropertyGroup): + """ + Sun profile properties used to load climate profiles and create lighting scene from them. + """ + + name = StringProperty(name="Name", default="") + + low_elevation = IntProperty( + name="Low Elevation", + options={'HIDDEN'}, + ) + high_elevation = IntProperty( + name="High Elevation", + options={'HIDDEN'}, + ) + + sun_direction = IntProperty( + name="Sun Elevation Direction", + options={'HIDDEN'}, + ) + + ambient = FloatVectorProperty( + name="Ambient", + options={'HIDDEN'}, + subtype='COLOR', + size=3, + min=-5, max=5, + soft_min=0, soft_max=1, + step=3, precision=2, + ) + ambient_hdr_coef = FloatProperty( + name="Ambient HDR Coeficient", + options={'HIDDEN'}, + ) + + diffuse = FloatVectorProperty( + name="Diffuse", + options={'HIDDEN'}, + subtype='COLOR', + size=3, + min=-5, max=5, + soft_min=0, soft_max=1, + step=3, precision=2, + ) + diffuse_hdr_coef = FloatProperty( + name="Diffuse HDR Coeficient", + options={'HIDDEN'}, + ) + + specular = FloatVectorProperty( + name="Specular", + options={'HIDDEN'}, + subtype='COLOR', + size=3, + min=-5, max=5, + soft_min=0, soft_max=1, + step=3, precision=2, + ) + specular_hdr_coef = FloatProperty( + name="Specular HDR Coeficient", + options={'HIDDEN'}, + ) + + sun_color = FloatVectorProperty( + name="Sun Color", + options={'HIDDEN'}, + subtype='COLOR', + size=3, + min=-5, max=5, + soft_min=0, soft_max=1, + step=3, precision=2, + ) + sun_color_hdr_coef = FloatProperty( + name="Sun Color HDR Coeficient", + options={'HIDDEN'}, + ) + + env = FloatProperty( + name="Env Factor", + options={'HIDDEN'}, + ) + env_static_mod = FloatProperty( + name="Env Static Modulator", + options={'HIDDEN'}, + ) + + sun_profiles_inventory = CollectionProperty( + type=SunProfileInventoryItem, + options={'SKIP_SAVE'}, + ) + + sun_profiles_inventory_active = IntProperty( + name="Currently Active Sun Profile", + ) + + def sun_profiles_path_update(self, context): + _config_container.update_sun_profiles_library_path(_get_scs_globals().sun_profiles_inventory, + self.sun_profiles_lib_path) + return + + sun_profiles_lib_path = StringProperty( + name="Sun Profiles Library", + description="Relative or absolute path to sun profiles definition file.", + default="//def/climate/default/nice.sii", + update=sun_profiles_path_update + ) + + def lighting_scene_east_direction_update(self, context): + bpy.ops.world.scs_use_sun_profile(skip_background_set=True) + return + + lighting_scene_east_direction = IntProperty( + name="SCS Lighting East", + description="Defines east position in lighting scene (changing it will change direction of diffuse and specular light).", + default=0, min=0, max=360, step=10, subtype='ANGLE', + update=lighting_scene_east_direction_update + ) diff --git a/addon/io_scs_tools/shader_presets.txt b/addon/io_scs_tools/shader_presets.txt index 03a2139..be1bbda 100644 --- a/addon/io_scs_tools/shader_presets.txt +++ b/addon/io_scs_tools/shader_presets.txt @@ -1182,7 +1182,7 @@ Shader { Shader { PresetName: "lamp" Effect: "eut2.lamp" - Flavors: ( "NMAP|NMAPUV|NMAP_16|NMAPUV_16" "ENVMAP" "SHADOW" ) + Flavors: ( "NMAP|NMAPUV|NMAP_16|NMAPUV_16" "SHADOW" "ENVMAP" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1482,42 +1482,42 @@ Shader { Attribute { Format: FLOAT Tag: "shininess" - Value: ( 90.0 ) + Value: ( 10.0 ) } Attribute { Format: FLOAT3 Tag: "env_factor" - Value: ( 0.5 0.5 0.5 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT3 Tag: "aux[0]" FriendlyTag: "Distances (Near, Far, Scramble)" - Value: ( 700.0 1650.0 10.0 ) + Value: ( 100.0 200.0 2.0 ) } Attribute { Format: FLOAT3 Tag: "aux[1]" FriendlyTag: "Near Color" - Value: ( 0.012, 0.022, 0.045 ) + Value: ( 0.188, 0.302, 0.400 ) } Attribute { Format: FLOAT3 Tag: "aux[2]" FriendlyTag: "Horizon Color" - Value: ( 0.100, 0.19, 0.42 ) + Value: ( 0.188, 0.302, 0.400 ) } Attribute { Format: FLOAT4 Tag: "aux[3]" FriendlyTag: "Layer0 (Yaw, Speed, ScaleX, ScaleY)" - Value: ( -8.0, 0.8, 70, 40 ) + Value: ( 0.0, 0.1, 1.0, 1.0 ) } Attribute { Format: FLOAT4 Tag: "aux[4]" FriendlyTag: "Layer1 (Yaw, Speed, ScaleX, ScaleY)" - Value: ( 5.0, 0.5, 50, 17 ) + Value: ( 20.0, 0.05, 2.0, 2.0 ) } Texture { Tag: "texture[X]:texture_layer0" diff --git a/addon/io_scs_tools/supported_effects.bin b/addon/io_scs_tools/supported_effects.bin new file mode 100644 index 0000000..6f64105 Binary files /dev/null and b/addon/io_scs_tools/supported_effects.bin differ diff --git a/addon/io_scs_tools/ui/__init__.py b/addon/io_scs_tools/ui/__init__.py index 77446ec..6c256cd 100644 --- a/addon/io_scs_tools/ui/__init__.py +++ b/addon/io_scs_tools/ui/__init__.py @@ -23,4 +23,5 @@ from io_scs_tools.ui import scene from io_scs_tools.ui import object from io_scs_tools.ui import material -from io_scs_tools.ui import mesh \ No newline at end of file +from io_scs_tools.ui import mesh +from io_scs_tools.ui import world diff --git a/addon/io_scs_tools/ui/icons/.icon_scs_bt_logo_orange.png b/addon/io_scs_tools/ui/icons/.icon_scs_bt_logo_orange.png new file mode 100644 index 0000000..09aa440 Binary files /dev/null and b/addon/io_scs_tools/ui/icons/.icon_scs_bt_logo_orange.png differ diff --git a/addon/io_scs_tools/ui/icons/.scs_bt_logo.png b/addon/io_scs_tools/ui/icons/.scs_bt_logo.png new file mode 100644 index 0000000..5fe547d Binary files /dev/null and b/addon/io_scs_tools/ui/icons/.scs_bt_logo.png differ diff --git a/addon/io_scs_tools/ui/material.py b/addon/io_scs_tools/ui/material.py index 2d23647..dbacf06 100644 --- a/addon/io_scs_tools/ui/material.py +++ b/addon/io_scs_tools/ui/material.py @@ -21,6 +21,7 @@ import bpy import os from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shader_presets import cache as _shader_presets_cache from io_scs_tools.utils import object as _object_utils from io_scs_tools.utils import path as _path_utils from io_scs_tools.utils import get_scs_globals as _get_scs_globals @@ -85,27 +86,69 @@ def _draw_shader_flavors(layout, mat): for preset in _get_shader_presets_inventory(): if preset.name == mat.scs_props.active_shader_preset_name: + # if there is no flavors in found preset, + # then we don't have to draw anything so exit drawing shader flavors right away + if len(preset.flavors) <= 0: + return + # strip of base effect name, to avoid any flavor matching in base effect name effect_flavor_part = mat.scs_props.mat_effect_name[len(preset.effect):] - if len(preset.flavors) > 0: - - column = layout.box().column(align=True) - column.alignment = "LEFT" + column = layout.column(align=True) + column.alignment = "LEFT" - for flavor in preset.flavors: + # draw switching operator for each flavor (if there is more variants draw operator for each variant) + enabled_flavors = {} # store enabled flavors, for later analyzing which rows should be disabled + flavor_rows = [] # store row UI layout of flavor operators, for later analyzing which rows should be disabled + for i, flavor in enumerate(preset.flavors): row = column.row(align=True) + flavor_rows.append(row) for flavor_variant in flavor.variants: is_in_middle = "." + flavor_variant.name + "." in effect_flavor_part is_on_end = effect_flavor_part.endswith("." + flavor_variant.name) + flavor_enabled = is_in_middle or is_on_end - icon = "FILE_TICK" if is_in_middle or is_on_end else "X" + icon = "FILE_TICK" if flavor_enabled else "X" props = row.operator("material.scs_switch_flavor", text=flavor_variant.name, icon=icon) props.flavor_name = flavor_variant.name - props.flavor_enabled = is_in_middle or is_on_end + props.flavor_enabled = flavor_enabled + + if flavor_enabled: + enabled_flavors[i] = flavor_variant + + # now as we drawn the flavors and we know which ones are enabled, + # search the ones that are not compatible with currently enabled flavors and disable them in UI! + for i, flavor in enumerate(preset.flavors): + + # enabled flavors have to stay enabled so skip them + if i in enabled_flavors: + continue + + for flavor_variant in flavor.variants: + + # 1. construct proposed new flavor string: + # combine strings of enabled flavors and current flavor variant + new_flavor_str = "" + curr_flavor_added = False + for enabled_i in enabled_flavors.keys(): + + if i < enabled_i and not curr_flavor_added: + new_flavor_str += "." + flavor_variant.name + curr_flavor_added = True + + new_flavor_str += "." + enabled_flavors[enabled_i].name + + if not curr_flavor_added: + new_flavor_str += "." + flavor_variant.name + + # 2. check if proposed new flavor combination exists in cache: + # if not then row on current flavor index has to be disabled + if not _shader_presets_cache.has_section(preset, new_flavor_str): + flavor_rows[i].enabled = False + break # once preset was found just use return to skip other presets return @@ -322,29 +365,28 @@ def _draw_shader_texture(layout, mat, split_perc, texture, read_only): tobj_filepath = _path_utils.get_tobj_path_from_shader_texture(shader_texture) + tobj_settings_row = layout_box_col.row(align=True) + tobj_settings_row = tobj_settings_row.split(percentage=1 / (1 - split_perc + 0.000001) * 0.1, align=True) + if not tobj_filepath: - layout_box_inner_col = layout_box_col.row(align=True) - item_space = layout_box_inner_col.column(align=True) - props = item_space.operator("material.scs_create_tobj", icon="NEW", text="") + props = tobj_settings_row.operator("material.scs_create_tobj", icon="NEW", text="") props.texture_type = texture_type # creating extra column->row so it can be properly disabled as tobj doesn't exists - item_space = layout_box_inner_col.column(align=True).row(align=True) - else: - item_space = layout_box_col.row(align=True) + tobj_settings_row = tobj_settings_row.column(align=True).row(align=True) # enable settings only if tobj exists and map type of the tobj is 2d - item_space.enabled = tobj_filepath is not None and getattr(mat.scs_props, shader_texture_id + "_map_type", "") == "2d" + tobj_settings_row.enabled = tobj_filepath is not None and getattr(mat.scs_props, shader_texture_id + "_map_type", "") == "2d" if tobj_filepath: mtime = str(os.path.getmtime(tobj_filepath)) - item_space.alert = (mtime != getattr(mat.scs_props, shader_texture_id + "_tobj_load_time", "NOT FOUND")) + tobj_settings_row.alert = (mtime != getattr(mat.scs_props, shader_texture_id + "_tobj_load_time", "NOT FOUND")) else: - item_space.alert = True + tobj_settings_row.alert = True - props = item_space.operator("material.scs_reload_tobj", icon="LOAD_FACTORY", text="") + props = tobj_settings_row.operator("material.scs_reload_tobj", icon="LOAD_FACTORY", text="") props.texture_type = texture_type - item_space.prop_menu_enum( + tobj_settings_row.prop_menu_enum( mat.scs_props, str('shader_' + tag_id_string + '_settings'), icon='SETTINGS', @@ -374,12 +416,6 @@ def _draw_shader_texture(layout, mat, split_perc, texture, read_only): "If the uv map is not provided first entry from Mappings list above will be used!") _shared.draw_warning_operator(item_space_row, "Mapping Info", preview_nmap_msg, icon="INFO") - # add ensuring operator for norma map uv mapping - if tag_id_string == "texture_nmap": - props = item_space_row.operator("mesh.scs_ensure_active_uv", text="", icon="FILE_REFRESH") - props.mat_name = mat.name - props.uv_layer = uv_mappings[0].value - if mapping.value and mapping.value != "" and mapping.value in bpy.context.active_object.data.uv_layers: icon = "GROUP_UVS" else: @@ -439,7 +475,7 @@ def _draw_shader_parameters(layout, mat, scs_props, scs_globals, read_only=False col.operator("mesh.scs_add_vcolors_to_active") col.operator("mesh.scs_add_vcolors_to_all") - global_mat_attr = layout.column(align=True) + global_mat_attr = layout.column(align=False) # UI SPLIT PERCENTAGE PROPERTY global_mat_attr.row().prop(scs_props, "shader_item_split_percentage", slider=True) @@ -652,6 +688,9 @@ def draw(self, context): mat = context.material scs_globals = _get_scs_globals() + # disable pane if config is being updated + layout.enabled = not scs_globals.config_update_lock + if mat: # PROVISIONAL SHADER PRESET PANEL _draw_preset_shader_panel(layout.box(), mat, scene.scs_props, scs_globals) diff --git a/addon/io_scs_tools/ui/object.py b/addon/io_scs_tools/ui/object.py index 1f819e6..dbd8e63 100644 --- a/addon/io_scs_tools/ui/object.py +++ b/addon/io_scs_tools/ui/object.py @@ -85,6 +85,8 @@ def _draw_locator_panel(layout, context, scene, obj, enabled=True): :type enabled: bool """ + loc_set_split_percentage = 0.33 # the best value to match the rest of the layout in locators panel + layout_box = layout.box() if scene.scs_props.locator_settings_expand: loc_header = layout_box.row() @@ -96,10 +98,20 @@ def _draw_locator_panel(layout, context, scene, obj, enabled=True): # MODEL LOCATORS if obj.scs_props.locator_type == 'Model': - col = layout_box.column() - col.prop(obj, 'name') - # col.prop(obj.scs_props, 'locator_model_hookup', icon='NONE') - col.prop_search(obj.scs_props, 'locator_model_hookup', _get_scs_globals(), 'scs_hookup_inventory', icon='NONE') + row = layout_box.row().split(percentage=loc_set_split_percentage) + col1 = row.column() + col2 = row.column() + + # locator name + col1.label("Name:") + col2.prop(obj, 'name', text="") + + # locator hookup + col1.label("Hookup:") + row = col2.row(align=True) + props = row.operator('scene.scs_reload_library', icon='FILE_REFRESH', text="") + props.library_path_attr = "hookup_library_rel_path" + row.prop_search(obj.scs_props, 'locator_model_hookup', _get_scs_globals(), 'scs_hookup_inventory', text="") # (MODEL) LOCATOR PREVIEW PANEL _draw_locator_preview_panel(layout_box, obj) @@ -193,20 +205,37 @@ def _draw_locator_panel(layout, context, scene, obj, enabled=True): loc_set.operator('object.abort_preview_terrain_points', text="Abort") if obj.scs_props.locator_prefab_type == 'Sign': - loc_set = box_row_box.column() - loc_set.prop_search(obj.scs_props, 'locator_prefab_sign_model', _get_scs_globals(), 'scs_sign_model_inventory', - icon='NONE') + loc_set = box_row_box.row().split(percentage=loc_set_split_percentage) + loc_set.label("Sign Model:") + row = loc_set.row(align=True) + props = row.operator('scene.scs_reload_library', icon='FILE_REFRESH', text="") + props.library_path_attr = "sign_library_rel_path" + row.prop_search(obj.scs_props, 'locator_prefab_sign_model', _get_scs_globals(), 'scs_sign_model_inventory', icon='NONE', text="") if obj.scs_props.locator_prefab_type == 'Spawn Point': loc_set = box_row_box.row() loc_set.prop(obj.scs_props, 'locator_prefab_spawn_type', icon='NONE') if obj.scs_props.locator_prefab_type == 'Traffic Semaphore': loc_set = box_row_box.column() - loc_set.prop(obj.scs_props, 'locator_prefab_tsem_id', icon='NONE') - # loc_set.prop(obj.scs_props, 'locator_prefab_tsem_model', icon='NONE') - # loc_set.prop(obj.scs_props, 'locator_prefab_tsem_profile', icon='NONE') - loc_set.prop_search(obj.scs_props, 'locator_prefab_tsem_profile', _get_scs_globals(), 'scs_tsem_profile_inventory', - icon='NONE') - loc_set.prop(obj.scs_props, 'locator_prefab_tsem_type', icon='NONE') + row = loc_set.row(align=True).split(percentage=loc_set_split_percentage) + col1 = row.column() + col2 = row.column() + + # id + col1.label("ID:") + col2.prop(obj.scs_props, 'locator_prefab_tsem_id', icon='NONE', text="") + + # profile + col1.label("Profile:") + row = col2.row(align=True) + props = row.operator('scene.scs_reload_library', icon='FILE_REFRESH', text="") + props.library_path_attr = "tsem_library_rel_path" + row.prop_search(obj.scs_props, 'locator_prefab_tsem_profile', _get_scs_globals(), 'scs_tsem_profile_inventory', icon='NONE', text="") + + # type + col1.label("Type:") + col2.prop(obj.scs_props, 'locator_prefab_tsem_type', icon='NONE', text="") + + # interval distances and cycle delay loc_set_col = loc_set.column() if obj.scs_props.locator_prefab_tsem_type in ('0', '1'): loc_set_col.enabled = False @@ -237,13 +266,35 @@ def _draw_locator_panel(layout, context, scene, obj, enabled=True): loc_set.prop(obj.scs_props, 'locator_prefab_np_allowed_veh', icon='NONE') loc_set = box_row_box.row() loc_set.prop(obj.scs_props, 'locator_prefab_np_blinker', icon='NONE', expand=True) - loc_set = box_row_box.column() - loc_set.prop(obj.scs_props, 'locator_prefab_np_priority_modifier', icon='NONE') - loc_set.prop(obj.scs_props, 'locator_prefab_np_traffic_semaphore', icon='NONE') - loc_set.prop_search(obj.scs_props, 'locator_prefab_np_traffic_rule', _get_scs_globals(), - 'scs_traffic_rules_inventory', icon='NONE') - loc_set.prop(obj.scs_props, 'locator_prefab_np_boundary', icon='NONE') - loc_set.prop(obj.scs_props, 'locator_prefab_np_boundary_node', icon='NONE') + + loc_set = box_row_box.row().split(percentage=loc_set_split_percentage) + col1 = loc_set.column() + col2 = loc_set.column() + + # priority modifier + col1.label("Priority Modifier:") + col2.prop(obj.scs_props, 'locator_prefab_np_priority_modifier', icon='NONE', text="") + + # traffic semaphore + col1.label("Traffic Semaphore:") + col2.prop(obj.scs_props, 'locator_prefab_np_traffic_semaphore', icon='NONE', text="") + + # traffic rule + col1.label("Traffic Rule:") + row = col2.row(align=True) + props = row.operator('scene.scs_reload_library', icon='FILE_REFRESH', text="") + props.library_path_attr = "traffic_rules_library_rel_path" + row.prop_search(obj.scs_props, 'locator_prefab_np_traffic_rule', + _get_scs_globals(), 'scs_traffic_rules_inventory', + icon='NONE', text="") + + # boundary + col1.label("Boundary:") + col2.prop(obj.scs_props, 'locator_prefab_np_boundary', icon='NONE', text="") + + # boundary node + col1.label("Boundary Node:") + col2.prop(obj.scs_props, 'locator_prefab_np_boundary_node', icon='NONE', text="") loc_set = box_row_box.row() if len(context.selected_objects) == 2: @@ -322,10 +373,12 @@ def _draw_locator_panel(layout, context, scene, obj, enabled=True): loc_set.operator('object.connect_prefab_locators', text="Connect / Disconnect Map Points", icon='LINKED') if obj.scs_props.locator_prefab_type == 'Trigger Point': - loc_set = box_row_box.row() - loc_set.prop_search(obj.scs_props, 'locator_prefab_tp_action', - _get_scs_globals(), 'scs_trigger_actions_inventory', - icon='NONE') + loc_set = box_row_box.row().split(percentage=loc_set_split_percentage) + loc_set.label("Action:") + row = loc_set.row(align=True) + props = row.operator('scene.scs_reload_library', icon='FILE_REFRESH', text="") + props.library_path_attr = "trigger_actions_rel_path" + row.prop_search(obj.scs_props, 'locator_prefab_tp_action', _get_scs_globals(), 'scs_trigger_actions_inventory', icon='NONE', text="") loc_set = box_row_box.column(align=True) loc_set.prop(obj.scs_props, 'locator_prefab_tp_range', icon='NONE') loc_set.prop(obj.scs_props, 'locator_prefab_tp_reset_delay', icon='NONE') diff --git a/addon/io_scs_tools/ui/scene.py b/addon/io_scs_tools/ui/scene.py index afa0e97..856008a 100644 --- a/addon/io_scs_tools/ui/scene.py +++ b/addon/io_scs_tools/ui/scene.py @@ -60,10 +60,12 @@ def _draw_path_settings_panel(scene, layout, scs_globals): layout_box_col = layout_box.column(align=True) # SCS Project Path (DIR_PATH - absolute) + icon = 'SNAP_ON' if _get_scs_globals().use_alternative_bases else 'SNAP_OFF' layout_box_col.label('SCS Project Base Path:', icon='FILE_FOLDER') layout_box_row = layout_box_col.row(align=True) layout_box_row.alert = not os.path.isdir(scs_globals.scs_project_path) layout_box_row.prop(scs_globals, 'scs_project_path', text='', icon='PACKAGE') + layout_box_row.prop(scs_globals, 'use_alternative_bases', icon=icon, icon_only=True) layout_box_row.operator('scene.select_scs_project_path', text='', icon='FILESEL') # Divide labels and sub paths to columns @@ -453,6 +455,9 @@ def draw(self, context): scene = context.scene scs_globals = _get_scs_globals() + # scs tools main panel if config is being updated + layout.enabled = not scs_globals.config_update_lock + if scene: # GLOBAL SETTINGS PANEL diff --git a/addon/io_scs_tools/ui/world.py b/addon/io_scs_tools/ui/world.py new file mode 100644 index 0000000..16d4f8f --- /dev/null +++ b/addon/io_scs_tools/ui/world.py @@ -0,0 +1,148 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2013-2014: SCS Software + +from bpy.types import Panel, UIList +from io_scs_tools.consts import SCSLigthing as _LIGHTING_consts +from io_scs_tools.utils import path as _path_utils +from io_scs_tools.utils import get_scs_globals as _get_scs_globals +from io_scs_tools.ui import shared as _shared + + +class _WorldPanelBlDefs(_shared.HeaderIconPanel): + """ + Defines class for showing in Blender Scene Properties window + """ + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "world" + + +class SCSSunProfileSlots(UIList): + """ + Draw sun profile entry within ui list + """ + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + if item: + layout.prop(item, "name", text="", emboss=False) + props = layout.operator("world.scs_use_sun_profile", icon="SAVE_PREFS", text="", emboss=False) + props.sun_profile_index = index + else: + layout.label(text="", icon_value=icon) + + +class SCSLighting(_WorldPanelBlDefs, Panel): + """Creates a Panel in the Scene properties window""" + bl_label = "SCS Lighting" + bl_idname = "WORLD_PT_SCS_Lighting" + + def draw(self, context): + """UI draw function.""" + layout = self.layout + scs_globals = _get_scs_globals() + + is_active_sun_profile_valid = (0 <= scs_globals.sun_profiles_inventory_active < len(scs_globals.sun_profiles_inventory)) + is_ligting_scene_used = (context.scene and context.scene.background_set and + context.scene.background_set.name == _LIGHTING_consts.scene_name) + + # draw operator for disabling lighting scene + if is_ligting_scene_used: + layout.operator("world.scs_disable_lighting_in_scene", icon="QUIT") + + # draw warning if lighting scene is not used as background set in current scene + if is_active_sun_profile_valid and not is_ligting_scene_used: + _shared.draw_warning_operator(layout, "SCS Lighting Not Used", + "Current scene is not using SCS Ligthing.\n" + "If you want to enable it, you either press icon beside sun profile name in the list or\n" + "use 'Apply Values to SCS Lighting' button located on the bottom of selected sun profile details.", + text="SCS Lighting Not Used: Click For Info", + icon="INFO") + + # prepare main box containing header and body + col = layout.column(align=True) + header = col.box() + body = col.box() + + # 1. header + # library path + row = header.row(align=True) + split = row.split(percentage=0.35) + split.label("Sun Profiles Library:", icon="FILE_TEXT") + row = split.row(align=True) + row.alert = not _path_utils.is_valid_sun_profiles_library_path() + row.prop(scs_globals, "sun_profiles_lib_path", text="") + row.operator("scene.select_sun_profiles_lib_path", text="", icon='FILESEL') + + # 2. body + # lighting scene east direction + row = body.row() + row.label("", icon="LAMP_SPOT") + row.prop(scs_globals, "lighting_scene_east_direction", slider=True) + + # disable any UI from now on if active sun profile is not valid + body.enabled = is_active_sun_profile_valid + + # loaded sun profiles list + body.template_list( + 'SCSSunProfileSlots', + list_id="", + dataptr=scs_globals, + propname="sun_profiles_inventory", + active_dataptr=scs_globals, + active_propname="sun_profiles_inventory_active", + rows=3, + maxrows=5, + type='DEFAULT', + columns=9 + ) + + # active/selected sun profile props + if is_active_sun_profile_valid: + + layout = body.box().column() + + layout.label("Selected Sun Profile Details:", icon='LAMP') + layout.separator() + + active_sun_profile = scs_globals.sun_profiles_inventory[scs_globals.sun_profiles_inventory_active] + + layout.row(align=True).prop(active_sun_profile, "low_elevation") + layout.row(align=True).prop(active_sun_profile, "high_elevation") + + layout.row(align=True).prop(active_sun_profile, "ambient") + layout.row(align=True).prop(active_sun_profile, "ambient_hdr_coef") + + layout.row(align=True).prop(active_sun_profile, "diffuse") + layout.row(align=True).prop(active_sun_profile, "diffuse_hdr_coef") + + layout.row(align=True).prop(active_sun_profile, "specular") + layout.row(align=True).prop(active_sun_profile, "specular_hdr_coef") + + # layout.row(align=True).prop(active_sun_profile, "sun_color") + # layout.row(align=True).prop(active_sun_profile, "sun_color_hdr_coef") + + layout.row(align=True).prop(active_sun_profile, "env") + layout.row(align=True).prop(active_sun_profile, "env_static_mod") + + layout.separator() + row = layout.row() + row.scale_y = 1.5 + props = row.operator("world.scs_use_sun_profile", icon="SAVE_PREFS", text="Apply Values to SCS Lighting") + props.sun_profile_index = scs_globals.sun_profiles_inventory_active diff --git a/addon/io_scs_tools/utils/convert.py b/addon/io_scs_tools/utils/convert.py index 6e2758e..425d705 100644 --- a/addon/io_scs_tools/utils/convert.py +++ b/addon/io_scs_tools/utils/convert.py @@ -27,6 +27,35 @@ from io_scs_tools.utils import get_scs_globals as _get_scs_globals +def linear_to_srgb(value): + """Converts linear color to srgb colorspace. Function can convert single float or list of floats. + NOTE: taken from game code + + :param value: list of floats or float + :type value: float | list[float] + :return: converted color + :rtype: float | list[float] + """ + + is_float = isinstance(value, float) + if is_float: + vals = [value] + else: + vals = list(value) + + for i, v in enumerate(vals): + if v <= 0.0031308: + vals[i] = 12.92 * v + else: + a = 0.055 + vals[i] = (v ** (1.0 / 2.4) * (1.0 + a)) - a + + if is_float: + return vals[0] + else: + return vals + + def pre_gamma_corrected_col(color): """Pre applys gamma decoding to color. Usefull for preparation of color for Blender color pickers/nodes, @@ -116,6 +145,26 @@ def float_to_hex_string(value): return "Value Error" +def string_to_number(string): + """Converts string to number. It accepts hex interpretation or decimal. + NOTE: no safety checks if string is really a number string + + :param string: hex or decimal string carrying number + :type string: str + :return: float or int depending on passed string. It will result in int in case if input string will be decimal value without dot + :rtype: float | int + """ + + if string[0] == '&': + val = hex_string_to_float(string) + elif "." in string: + val = float(string) + else: + val = int(string) + + return val + + def hex_string_to_float(string): """Takes a string of a hexadecimal number and returns it as float number. diff --git a/addon/io_scs_tools/utils/info.py b/addon/io_scs_tools/utils/info.py index 42e8b0e..b052f01 100644 --- a/addon/io_scs_tools/utils/info.py +++ b/addon/io_scs_tools/utils/info.py @@ -19,6 +19,39 @@ # Copyright (C) 2013-2014: SCS Software import bpy +from io_scs_tools import bl_info + + +def __get_bl_info_version__(key): + """Gets version string from bl_info dictonary for given key. + + :param key: key in bl_info contaning version tuple (X, X, X, ..) where X is int number + :type key: str + :return: string representation of bl_info dictionary value for given key + :rtype: str + """ + ver = "" + for ver_num in bl_info[key]: + ver += str(ver_num) + "." + return ver[:-1] + + +def get_tools_version(): + """Returns Blender Tools version as string from bl_info["version"] dictonary value. + + :return: string representation of bl_info["version"] tuple + :rtype: str + """ + return __get_bl_info_version__("version") + + +def get_required_blender_version(): + """Returns required Blender version as string from bl_info["blender"] dictonary value. + + :return: string representation of bl_info["blender"] tuple + :rtype: str + """ + return __get_bl_info_version__("blender") def get_blender_version(): @@ -36,15 +69,28 @@ def get_blender_version(): return b_ver_str, build_str -def get_combined_ver_str(): +def get_combined_ver_str(only_version_numbers=False): """Returns combined version string from Blender version and Blender Tools version. + + :param only_version_numbers: True to return only versions without "Blender" and "SCS Blender Tools" strings + :type only_version_numbers: bool :return: combined version string :rtype: str """ - from io_scs_tools import get_tools_version - (version, build) = get_blender_version() - return "Blender " + version + build + ", SCS Blender Tools: " + get_tools_version() + if only_version_numbers: + return version + build + ", " + get_tools_version() + else: + return "Blender " + version + build + ", SCS Blender Tools: " + get_tools_version() + + +def is_blender_able_to_run_tools(): + """Tells if Blender version is good enough to run Blender Tools. + + :return: True if current blender version meets required version for Blender Tools; False otherwise + :rtype: bool + """ + return cmp_ver_str(bpy.app.version_string, get_required_blender_version()) > -1 def cmp_ver_str(version_str, version_str2): @@ -52,6 +98,8 @@ def cmp_ver_str(version_str, version_str2): :param version_str: version string to check (should be in format: "X.Y" where X and Y are version numbers) :type version_str: str + :param version_str2: version string to check (should be in format: "X.Y" where X and Y are version numbers) + :type version_str2: str :return: -1 if first is greater; 0 if equal; 1 if second is greater; :rtype: int """ diff --git a/addon/io_scs_tools/utils/material.py b/addon/io_scs_tools/utils/material.py index f3030f2..17acfa7 100644 --- a/addon/io_scs_tools/utils/material.py +++ b/addon/io_scs_tools/utils/material.py @@ -135,10 +135,12 @@ def get_texture(texture_path, texture_type, report_invalid=False): # set proper color space depending on texture type if texture_type == "nmap": # For TGA normal maps texture use Non-Color color space as it should be, - # but for 16-bits PNG normal maps texture sRGB has to be used + # but for 16-bits PNG normal maps texture Linear has to be used # otherwise Blender completely messes up normals calculation if texture.image.filepath.endswith(".tga"): texture.image.colorspace_settings.name = "Non-Color" + elif texture.image.filepath.endswith(".png") and texture.image.is_float: + texture.image.colorspace_settings.name = "Linear" else: texture.image.colorspace_settings.name = "sRGB" else: @@ -469,6 +471,14 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac # apply uv mappings either from imported data or from old mappings of previous shader if "scs_tex_aliases" in material: # scs_tex_aliases are present only on import + + # if mesh is corrupted then tex aliases won't be filled in properly in material from PIM importer, + # so report error and skip creation of texture mapping for current tex_coord. + if str(tex_coord) not in material["scs_tex_aliases"]: + lprint("E Material %r is missing texture coordinate aliases, some UV mappings in Material Textures will remain empty!", + (material.name,)) + continue + mapping['value'] = material["scs_tex_aliases"][str(tex_coord)] created_tex_mappings.append((tex_type, mapping.value, tex_coord)) diff --git a/addon/io_scs_tools/utils/mesh.py b/addon/io_scs_tools/utils/mesh.py index a2b39e9..a22def2 100644 --- a/addon/io_scs_tools/utils/mesh.py +++ b/addon/io_scs_tools/utils/mesh.py @@ -482,4 +482,4 @@ def cleanup_mesh(mesh): mesh.free_normals_split() if mesh.users == 0: - bpy.data.meshes.remove(mesh) + bpy.data.meshes.remove(mesh, do_unlink=True) diff --git a/addon/io_scs_tools/utils/name.py b/addon/io_scs_tools/utils/name.py index 94953bf..3a3a73f 100644 --- a/addon/io_scs_tools/utils/name.py +++ b/addon/io_scs_tools/utils/name.py @@ -84,3 +84,19 @@ def tokenize_name(name, default_name="default"): new_name = default_name return new_name + + +def is_valid_scs_root_object_name(name): + """Checks name of the SCS Root Objects for invalid charachters and + returns True if name is valid. + + :param name: SCS Root Object name + :type name: str + :return: True if valid, False otherwise + :rtype: bool + """ + for letter in name: + if not re.match("[A-Za-z0-9\.\-_]", letter): + return False + + return True diff --git a/addon/io_scs_tools/utils/object.py b/addon/io_scs_tools/utils/object.py index bcdc3b7..0ef2a46 100644 --- a/addon/io_scs_tools/utils/object.py +++ b/addon/io_scs_tools/utils/object.py @@ -73,6 +73,10 @@ def make_scs_root_object(context, dialog=False): if not obj.parent.select: scs_game_object_content.append(obj) + # UN-PARENT OBJECTS - un-parent and keep transformations, so objects stays on same place when re-parenting to new root + if context.active_object and len(context.selected_objects) > 0: + bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') + # ADD SCS ROOT OBJECT (EMPTY OBJECT) bpy.ops.object.empty_add( view_align=False, @@ -147,13 +151,12 @@ def sort_out_game_objects_for_export(objects): game_objects_dict = {} # PRE-SORTING - scs_root_objects = [] data_objects = [] rejected_objects = [] for obj in objects: if obj.type == 'EMPTY': if obj.scs_props.empty_object_type == 'SCS_Root': - scs_root_objects.append(obj) + game_objects_dict[obj] = [] elif obj.scs_props.empty_object_type == 'Locator': data_objects.append(obj) elif obj.type == 'MESH': @@ -364,7 +367,9 @@ def make_objects_selected(objects): bpy.ops.object.select_all(action='DESELECT') if len(objects) > 0: for obj in objects: - obj.select = True + # select only real objects + if obj: + obj.select = True def pick_objects_from_selection(operator_object, needs_active_obj=True, obj_type='MESH'): @@ -477,7 +482,11 @@ def create_convex_data(objects, convex_props={}, create_hull=False): except RuntimeError: import traceback - traceback.print_exc() + trace_str = traceback.format_exc().replace("\n", "\n\t ") + lprint("E Problem creating convex hull:\n\t %s", + (trace_str,), + report_errors=1, + report_warnings=1) # RETRIEVE CONVEX DATA # print(' > hull: %s' % str(hull)) @@ -830,7 +839,7 @@ def get_vertex_group(layer, vert_index): return vg_weight -def create_locator_empty(name, loc, rot=(0, 0, 0), scale=(1, 1, 1), size=1.0, data_type='Prefab', hookup=None): +def create_locator_empty(name, loc, rot=(0, 0, 0), scale=(1, 1, 1), size=1.0, data_type='Prefab', hookup=None, blend_coords=False): """ Creates an empty object for a Locator. :param name: @@ -840,6 +849,7 @@ def create_locator_empty(name, loc, rot=(0, 0, 0), scale=(1, 1, 1), size=1.0, da :param size: :param data_type: :param hookup: + :param blend_coords: :return: """ rot_quaternion = None @@ -847,10 +857,15 @@ def create_locator_empty(name, loc, rot=(0, 0, 0), scale=(1, 1, 1), size=1.0, da rot_quaternion = rot rot = (0, 0, 0) + if blend_coords: + location = loc + else: + location = _convert.change_to_scs_xyz_coordinates(loc, _get_scs_globals().import_scale) + bpy.ops.object.empty_add( type='PLAIN_AXES', view_align=False, - location=_convert.change_to_scs_xyz_coordinates(loc, _get_scs_globals().import_scale), + location=location, rotation=rot, ) @@ -860,7 +875,10 @@ def create_locator_empty(name, loc, rot=(0, 0, 0), scale=(1, 1, 1), size=1.0, da if rot_quaternion: locator.rotation_mode = 'QUATERNION' - locator.rotation_quaternion = _convert.change_to_blender_quaternion_coordinates(rot_quaternion) + if blend_coords: + locator.rotation_quaternion = rot_quaternion + else: + locator.rotation_quaternion = _convert.change_to_blender_quaternion_coordinates(rot_quaternion) locator.scale = scale locator.scs_props.empty_object_type = 'Locator' diff --git a/addon/io_scs_tools/utils/path.py b/addon/io_scs_tools/utils/path.py index b1beb1e..70e26be 100644 --- a/addon/io_scs_tools/utils/path.py +++ b/addon/io_scs_tools/utils/path.py @@ -110,6 +110,10 @@ def get_abs_path(path_in, subdir_path='', is_dir=False, skip_mod_check=False): :return: Absolute path or None :rtype: str """ + + # correct skip mod check switch if usage of alternative bases is switched off by user + skip_mod_check |= not _get_scs_globals().use_alternative_bases + root_path = _get_scs_globals().scs_project_path if subdir_path != '': root_path = os.path.join(root_path, subdir_path) @@ -137,6 +141,59 @@ def get_abs_path(path_in, subdir_path='', is_dir=False, skip_mod_check=False): return result +def get_abs_paths(filepath, is_dir=False, include_nonexist_alternative_bases=False, use_infixed_search=False): + """Gets existing absolute paths to the "SCS Project Base Path" including searching for "base" folder + one and two levels higher in filesystem hierachy. + + :param filepath: relative or absolute filepath + :type filepath: str + :param is_dir: flag specifying if given path should be directory + :type is_dir: bool + :param include_nonexist_alternative_bases: flag specifying if none existing absolute filepaths from alternative bases should be included in result + :type include_nonexist_alternative_bases: bool + :param use_infixed_search: search also for infixed filepaths? Meant for infixed library SII file searching eg. sign.dlc_north.sii + :type use_infixed_search: bool + :return: list of absolute paths or empty list if path not found + :rtype: list[str] + """ + + abs_paths = {} + """ + Store paths in dictionary to easily filter out duplicated paths. + So make sure to use normalized paths as keys and actual paths as values which should be returned as result. + """ + + existance_check = os.path.isdir if is_dir else os.path.isfile + + for i, sub_dir in enumerate(("", "../base", "../../base")): + + # only search for additional absolute paths if usage of alternative bases isn't switched off by user + if i > 0 and not _get_scs_globals().use_alternative_bases: + continue + + # additionally search for infixed files (eg. sign.dlc_north.sii) + if use_infixed_search: + infixed_files = get_all_infixed_file_paths(get_abs_path(filepath, subdir_path=sub_dir, is_dir=is_dir, skip_mod_check=True)) + else: + infixed_files = [get_abs_path(filepath, subdir_path=sub_dir, is_dir=is_dir, skip_mod_check=True)] + + for resulted_path in infixed_files: + + # ignore not found paths + if resulted_path is None: + continue + + # create normalized path to properly gather only unique paths + normalized_resulted_path = full_norm(resulted_path) + if (include_nonexist_alternative_bases or existance_check(resulted_path)) and normalized_resulted_path not in abs_paths: + abs_paths[normalized_resulted_path] = resulted_path + + # we are returning de-normalized paths, as they might be used in printout and precious information + # about origin of the path can be lost. (Eg. library was found in parent "base" directory, + # but if we return normalized path this information will be lost) + return abs_paths.values() + + def is_valid_shader_texture_path(shader_texture): """It returns True if there is valid Shader Texture file, otherwise False. @@ -258,6 +315,20 @@ def is_valid_matsubs_library_rel_path(): return False +def is_valid_sun_profiles_library_path(): + """It returns True if there is valid "*.sii" file in + the Sun Profiles Library directory, otherwise False.""" + sun_profiles_lib_path = get_abs_path(_get_scs_globals().sun_profiles_lib_path) + + if sun_profiles_lib_path: + if os.path.isfile(sun_profiles_lib_path): + return True + else: + return False + else: + return False + + def get_addon_installation_paths(): """Returns a list of paths to the directories where the addon can be installed.""" script_paths = bpy.utils.script_paths() @@ -553,6 +624,10 @@ def get_all_infixed_file_paths(filepath, include_given_path=True): :rtype: list[str] """ + # in-fixed file paths can not be searched upon none, so return empty list + if filepath is None: + return [] + infixed_filepaths = [filepath] if include_given_path else [] orig_dir, orig_file = os.path.split(filepath) diff --git a/addon/io_scs_tools/utils/printout.py b/addon/io_scs_tools/utils/printout.py index 6e6c3ab..2a54a72 100644 --- a/addon/io_scs_tools/utils/printout.py +++ b/addon/io_scs_tools/utils/printout.py @@ -171,7 +171,7 @@ def lprint(string, values=(), report_errors=0, report_warnings=0): text += "================\n" # create dialog title and message - title = "ERRORS" + title = "Errors" if dump_level == 5: dev_error_messages.extend(error_messages) @@ -199,8 +199,8 @@ def lprint(string, values=(), report_errors=0, report_warnings=0): # create dialog title and message if title != "": - title += " AND " - title += "WARNINGS DURING PROCESS" + title += " and " + title += "Warnings encountered during export!" if dump_level == 5: dev_warning_messages.extend(warning_messages) @@ -214,6 +214,11 @@ def lprint(string, values=(), report_errors=0, report_warnings=0): file_logger.write(text + "\n") file_logger.flush() bpy.ops.wm.show_3dview_report('INVOKE_DEFAULT', title=title, message=text) + + # make sure to tag any 3D view for redraw so errors OpenGL drawing is triggered + from io_scs_tools.utils.view3d import tag_redraw_all_view3d + tag_redraw_all_view3d() + return True else: return False diff --git a/addon/io_scs_tools/utils/view3d.py b/addon/io_scs_tools/utils/view3d.py index c1c6742..e526a23 100644 --- a/addon/io_scs_tools/utils/view3d.py +++ b/addon/io_scs_tools/utils/view3d.py @@ -78,6 +78,25 @@ def get_all_spaces(): return spaces +def has_view3d_space(screen): + """Tells if given blender screen has 3D view or not. + + :param screen: blender screen + :type screen: bpy.types.Screen + :return: True if at leas one 3d view is present; False otherwise + :rtype: bool + """ + + if screen is None: + return False + + for area in screen.areas: + if area.type == 'VIEW_3D': + return True + + return False + + def switch_layers_visibility(storage_list, show): """Switches visibility of layers in Blender. If show is True all layers on current scene and local layers of current 3D spaces are shown. If storage_list is provided it tries @@ -132,3 +151,11 @@ def tag_redraw_all_view3d_and_props(): for region in area.regions: if region.type == 'WINDOW': region.tag_redraw() + + +def tag_redraw_all_regions(): + # NOTE: Py can't access notifiers! + for window in bpy.context.window_manager.windows: + for area in window.screen.areas: + for region in area.regions: + region.tag_redraw() diff --git a/data/sample_base/def/climate/default/bad.sii b/data/sample_base/def/climate/default/bad.sii new file mode 100644 index 0000000..2b409ee --- /dev/null +++ b/data/sample_base/def/climate/default/bad.sii @@ -0,0 +1,1797 @@ +SiiNunit +{ +sun_profile : default.bad.01 { + low_elevation: &c11ffffe + high_elevation: &c11ffffe + sun_direction: 0 + temperature: 2 + temperature[0]: 15 + temperature[1]: 15 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_23-24_g.tobj" + skybox_texture[1]: "/model/skybox/bad0/bad0_23-24_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_23-24_c.tobj" + skycloud_texture[1]: "/model/skybox/bad0/bad0_23-24_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3efb6715, &3f3ed2d1, &3f6571ea) + ambient[1]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3c54fdf4 + ambient_hdr_coef[1]: &3c54fdf4 + diffuse: 2 + diffuse[0]: (&3ec0c0c2, &3f26a6a7, &3f72f2f4) + diffuse[1]: (&3ec0c0c2, &3f26a6a7, &3f72f2f4) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3c75c28f + diffuse_hdr_coef[1]: &3c75c28f + specular: 2 + specular[0]: (&3ec5da14, &3f21794e, &3f5099ca) + specular[1]: (&3ec5da14, &3f21794e, &3f5099ca) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3d0f5c29 + specular_hdr_coef[1]: &3d0f5c29 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color[1]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3b70f18c, &3bb7ad6c, &3be539be) + fog_color[1]: (&3b5b518a, &3b9c87fb, &3bd5635e) + fog_color2: 2 + fog_color2[0]: (&3b70f18c, &3bb7ad6c, &3be539be) + fog_color2[1]: (&3b5b518a, &3b9c87fb, &3bd5635e) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 7 + rain_additional_ambient[1]: 10 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.02 { + low_elevation: -90 + high_elevation: -15 + sun_direction: 0 + temperature: 2 + temperature[0]: 15 + temperature[1]: 15 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_1-4_g.tobj" + skybox_texture[1]: "/model/skybox/bad0/bad0_1-4_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_1-4_c.tobj" + skycloud_texture[1]: "/model/skybox/bad0/bad0_1-4_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3efb6715, &3f3ed2d1, &3f6571ea) + ambient[1]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3be56042 + ambient_hdr_coef[1]: &3be56042 + diffuse: 2 + diffuse[0]: (&3ec0c0c2, &3f26a6a7, &3f72f2f4) + diffuse[1]: (&3ec0c0c2, &3f26a6a7, &3f72f2f4) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3be56042 + diffuse_hdr_coef[1]: &3be56042 + specular: 2 + specular[0]: (&3ec5da14, &3f21794e, &3f5099ca) + specular[1]: (&3ec5da14, &3f21794e, &3f5099ca) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3c23d70a + specular_hdr_coef[1]: &3c23d70a + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color[1]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3b46eb61, &3b9c87fb, &3bc63548) + fog_color[1]: (&3b33070b, &3b83e1c4, &3b9c87fb) + fog_color2: 2 + fog_color2[0]: (&3b46eb61, &3b9c87fb, &3bc63548) + fog_color2[1]: (&3b33070b, &3b83e1c4, &3b9c87fb) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 15 + rain_additional_ambient[1]: 15 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.03 { + low_elevation: &c09ffffe + high_elevation: &c09ffffe + sun_direction: 1 + temperature: 2 + temperature[0]: 17 + temperature[1]: 17 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_5-5_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_5-5_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_5-5_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_5-5_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3cf5c28f + ambient_hdr_coef[1]: &3cf5c28f + diffuse: 2 + diffuse[0]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3cf5c28f + diffuse_hdr_coef[1]: &3cf5c28f + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3e4ccccd + specular_hdr_coef[1]: &3e4ccccd + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3daccd6e, &3d97fd48, &3d21fb3b) + sun_color[1]: (&3daccd6e, &3d97fd48, &3d21fb3c) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3d2796b1, &3d5f26b5, &3d7add24) + fog_color[1]: (&3d11d2af, &3d454fd1, &3d5f26b5) + fog_color2: 2 + fog_color2[0]: (&3d2796b1, &3d5f26b5, &3d7add24) + fog_color2[1]: (&3d11d2af, &3d454fd1, &3d5f26b5) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: &3f4ccccd + rain_max_wetness[1]: &3f4ccccd + rain_additional_ambient: 2 + rain_additional_ambient[0]: 7 + rain_additional_ambient[1]: 10 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec5da14, &3e4749e4, &3dea5d16) + sunshaft_color[1]: (&3ec5da14, &3e4749e4, &3dea5d16) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 0 + sunshaft_size[1]: 0 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.04 { + low_elevation: 0 + high_elevation: &411ffffe + sun_direction: 1 + temperature: 2 + temperature[0]: 18 + temperature[1]: 18 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_6-7_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_6-7_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_6-7_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_6-7_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3d4ccccd + ambient_hdr_coef[1]: &3d4ccccd + diffuse: 2 + diffuse[0]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3d4ccccd + diffuse_hdr_coef[1]: &3d4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3e4ccccd + specular_hdr_coef[1]: &3e4ccccd + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3ec87c30, &3ea57307, &3e822039) + sun_color[1]: (&3eb8fd33, &3eaf0f68, &3ea7d288) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3d97fd48, &3dbe95b1, &3dd6412a) + fog_color[1]: (&3d6cc562, &3d9c0714, &3db12727) + fog_color2: 2 + fog_color2[0]: (&3da020b9, &3dcc97b3, &3de53cd4) + fog_color2[1]: (&3d6cc562, &3d9c0714, &3db12727) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f000000 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: &3f4ccccd + rain_max_wetness[1]: &3f4ccccd + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 7 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f40c0d3, &3ef86592, &3e8645b8) + sunshaft_color[1]: (&3f2c253d, &3ef272b8, &3ea0c321) + sunshaft_strength: 2 + sunshaft_strength[0]: &3dcccccd + sunshaft_strength[1]: &3dcccccd + sunshaft_size: 2 + sunshaft_size[0]: &3fc00000 + sunshaft_size[1]: &3fc00000 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.05a { + low_elevation: 15 + high_elevation: &41c7fffe + sun_direction: 1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_8-10_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_8-10_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_8-10_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_8-10_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f2df680, &3f2df680, &3f2df67f) + ambient[1]: (&3f2df680, &3f2df680, &3f2df67f) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3ecccccd + ambient_hdr_coef[1]: &3e99999a + diffuse: 2 + diffuse[0]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f000000 + diffuse_hdr_coef[1]: &3e99999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3f000000 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f3ed2d1, &3f3ed2d1, &3f3ed2d0) + sun_color[1]: (&3ef272b8, &3ef272b8, &3ef272b9) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e198f0f, &3e4749e6, &3e741e7e) + fog_color[1]: (&3de53cd3, &3e080ea3, &3e22c6a1) + fog_color2: 2 + fog_color2[0]: (&3e25eb08, &3e55a4e7, &3e84308b) + fog_color2[1]: (&3de53cd3, &3e080ea3, &3e22c6a1) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 3 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f40c0d3, &3ef86592, &3e8645b8) + sunshaft_color[1]: (&3e6c4720, &3e702e08, &3e0ae378) + sunshaft_strength: 2 + sunshaft_strength[0]: &3e99999a + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.05b { + low_elevation: 30 + high_elevation: &420bffff + sun_direction: 1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_8-10_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_8-10_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_8-10_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_8-10_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f2df680, &3f2df680, &3f2df67f) + ambient[1]: (&3f2df680, &3f2df680, &3f2df67f) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3ecccccd + ambient_hdr_coef[1]: &3e99999a + diffuse: 2 + diffuse[0]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f000000 + diffuse_hdr_coef[1]: &3e99999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3f000000 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f0d50a0, &3f0d50a0, &3f0d509f) + sun_color[1]: (&3ef272b8, &3ef272b8, &3ef272b9) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e25eb08, &3e55a4e7, &3e84308b) + fog_color[1]: (&3de53cd3, &3e080ea3, &3e22c6a1) + fog_color2: 2 + fog_color2[0]: (&3e25eb08, &3e55a4e7, &3e84308b) + fog_color2[1]: (&3de53cd3, &3e080ea3, &3e22c6a1) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 3 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f40c0d3, &3ef86592, &3e8645b8) + sunshaft_color[1]: (&3e6c4720, &3e702e08, &3e0ae378) + sunshaft_strength: 2 + sunshaft_strength[0]: &3e99999a + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.06a { + low_elevation: &421ffffe + high_elevation: 45 + sun_direction: 1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient[1]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3f000000 + ambient_hdr_coef[1]: &3ecccccd + diffuse: 2 + diffuse[0]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse[1]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f19999a + diffuse_hdr_coef[1]: &3e4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f337b6b, &3f337b6b, &3f337b6b) + sun_color[1]: (&3eb3fc14, &3eb3fc14, &3eb3fc15) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_color2: 2 + fog_color2[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color2[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 2 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&bca3d70a, 0, &bca3d70a) + color_balance[1]: (&bd23d70a, &3ca3d70a, &3d23d70a) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: &3f4ccccd + sunshaft_color: 2 + sunshaft_color[0]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_color[1]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.06b { + low_elevation: &4247fffe + high_elevation: 90 + sun_direction: 0 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient[1]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3f000000 + ambient_hdr_coef[1]: &3ecccccd + diffuse: 2 + diffuse[0]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse[1]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f19999a + diffuse_hdr_coef[1]: &3e4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f337b6b, &3f337b6b, &3f337b6b) + sun_color[1]: (&3eb3fc14, &3eb3fc14, &3eb3fc15) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_color2: 2 + fog_color2[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color2[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 2 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&bca3d70a, 0, &bca3d70a) + color_balance[1]: (&bd23d70a, &3ca3d70a, &3d23d70a) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: &3f4ccccd + sunshaft_color: 2 + sunshaft_color[0]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_color[1]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.06c { + low_elevation: &421ffffe + high_elevation: 45 + sun_direction: -1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient[1]: (&3f12353e, &3f337b6b, &3f5099ca) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3f000000 + ambient_hdr_coef[1]: &3ecccccd + diffuse: 2 + diffuse[0]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse[1]: (&3f76f5af, &3f24fca0, &3e9e72b6) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f19999a + diffuse_hdr_coef[1]: &3e4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f337b6b, &3f337b6b, &3f337b6b) + sun_color[1]: (&3eb3fc14, &3eb3fc14, &3eb3fc15) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_color2: 2 + fog_color2[0]: (&3e8a7eb0, &3e9e72b6, &3ebe12de) + fog_color2[1]: (&3e10a753, &3e168e51, &3e1c98ac) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 2 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&bca3d70a, 0, &bca3d70a) + color_balance[1]: (&bd23d70a, &3ca3d70a, &3d23d70a) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: &3f4ccccd + sunshaft_color: 2 + sunshaft_color[0]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_color[1]: (&3f15862b, &3f00bd2a, &3ec0a56e) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.07a { + low_elevation: 30 + high_elevation: &420bffff + sun_direction: -1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_16-18_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_16-18_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_16-18_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_16-18_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e4ccccd + ambient_hdr_coef[1]: &3e3851ec + diffuse: 2 + diffuse[0]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse[1]: (&3f4e946e, &3f1e0141, &3eec955c) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3e99999a + diffuse_hdr_coef[1]: &3dcccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3e8645b8, &3e6495df, &3e3ce703) + sun_color[1]: (&3e741e7e, &3e741e7e, &3e3ce703) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e080ea3, &3e168e51, &3e198f0f) + fog_color[1]: (&3ddb2eec, &3de53cd3, &3def8e51) + fog_color2: 2 + fog_color2[0]: (&3e080ea3, &3e168e51, &3e198f0f) + fog_color2[1]: (&3ddb2eec, &3de53cd3, &3def8e51) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 3 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3cf5c28f, 0, &bca3d70a) + color_balance[1]: (&3c23d70a, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ed338c9, &3e9c274c, &3da883d6) + sunshaft_color[1]: (&3f00bd2a, &3eaf0f68, &3e60cb7a) + sunshaft_strength: 2 + sunshaft_strength[0]: &3e99999a + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 4 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.07b { + low_elevation: 15 + high_elevation: &41c7fffe + sun_direction: -1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_16-18_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_16-18_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_16-18_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_16-18_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e4ccccd + ambient_hdr_coef[1]: &3e3851ec + diffuse: 2 + diffuse[0]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse[1]: (&3f4e946e, &3f1e0141, &3eec955c) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3e99999a + diffuse_hdr_coef[1]: &3dcccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3e8645b8, &3e6495df, &3e3ce703) + sun_color[1]: (&3e741e7e, &3e741e7e, &3e3ce703) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f4ccccd + sun_shadow_strength[1]: &3f000000 + fog_color: 2 + fog_color[0]: (&3e080ea3, &3e168e51, &3e198f0f) + fog_color[1]: (&3ddb2eec, &3de53cd3, &3def8e51) + fog_color2: 2 + fog_color2[0]: (&3e080ea3, &3e168e51, &3e198f0f) + fog_color2[1]: (&3ddb2eec, &3de53cd3, &3def8e51) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 1 + rain_additional_ambient[1]: 3 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3cf5c28f, 0, &bca3d70a) + color_balance[1]: (&3c23d70a, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ed338c9, &3e9c274c, &3da883d6) + sunshaft_color[1]: (&3f00bd2a, &3eaf0f68, &3e60cb7a) + sunshaft_strength: 2 + sunshaft_strength[0]: &3e99999a + sunshaft_strength[1]: &3e99999a + sunshaft_size: 2 + sunshaft_size[0]: 4 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.08 { + low_elevation: &409ffffe + high_elevation: &411ffffe + sun_direction: -1 + temperature: 2 + temperature[0]: 20 + temperature[1]: 20 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_19-20_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_19-20_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_19-20_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_19-20_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31a196, &3f31a196, &3f31a195) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3dcccccd + ambient_hdr_coef[1]: &3da3d70a + diffuse: 2 + diffuse[0]: (&3f76f5af, &3f24fca0, &3eb8fd34) + diffuse[1]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3dcccccd + diffuse_hdr_coef[1]: &3d23d70a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3f000000 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e4ccccd + env_static_mod[1]: &3e4ccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f5099ca, &3f5099ca, &3f5099ca) + sun_color[1]: (&3ec87c30, &3ec87c30, &3ec87c30) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3d9018f3, &3daccd6e, &3daccd6f) + fog_color[1]: (&3d7add23, &3d8c3e48, &3d8c3e48) + fog_color2: 2 + fog_color2[0]: (&3d9018f3, &3daccd6e, &3daccd6f) + fog_color2[1]: (&3d7add23, &3d8c3e48, &3d8c3e48) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (20, 20) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 2 + rain_additional_ambient[1]: 4 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3cf5c28f, &bca3d70a, &bd8f5c29) + color_balance[1]: (&3ccccccd, 0, &bcf5c28f) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f40c0d3, &3ef86592, &3e8645b8) + sunshaft_color[1]: (&3f40c0d3, &3ef86592, &3e8645b8) + sunshaft_strength: 2 + sunshaft_strength[0]: &3dcccccd + sunshaft_strength[1]: &3dcccccd + sunshaft_size: 2 + sunshaft_size[0]: &3fc00000 + sunshaft_size[1]: &3fc00000 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.bad.09 { + low_elevation: &c09ffffe + high_elevation: 0 + sun_direction: -1 + temperature: 2 + temperature[0]: 17 + temperature[1]: 17 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/bad0/bad0_21-22_g.tobj" + skybox_texture[1]: "/model/skybox/bad1/bad1_21-22_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/bad0/bad0_21-22_c.tobj" + skycloud_texture[1]: "/model/skybox/bad1/bad1_21-22_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3d4ccccd + ambient_hdr_coef[1]: &3cf5c28f + diffuse: 2 + diffuse[0]: (&3f7933b7, &3f13dc51, &3eb3fc15) + diffuse[1]: (&3f7933b7, &3f13dc51, &3eb3fc15) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3d4ccccd + diffuse_hdr_coef[1]: &3cf5c28f + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3d8f5c29 + specular_hdr_coef[1]: &3d8f5c29 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e4ccccd + env_static_mod[1]: &3e4ccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3e3ce704, &3e4e64c3, &3e520029) + sun_color[1]: (&3dcc97b3, &3dbe95b1, &3da883d6) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3d2796b1, &3d454fd1, &3d454fd1) + fog_color[1]: (&3cc5e638, &3cf2212c, &3d0798dc) + fog_color2: 2 + fog_color2[0]: (&3d2796b1, &3d454fd1, &3d454fd1) + fog_color2[1]: (&3cc5e638, &3cf2212c, &3d0798dc) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 50 + fog_offset[1]: 0 + fog_density: 2 + fog_density[0]: &3b449ba6 + fog_density[1]: &3ba3d70a + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (500, 500) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: &3e99999a + rain_intensity[1]: 1 + rain_max_wetness: 2 + rain_max_wetness[0]: &3f4ccccd + rain_max_wetness[1]: &3f4ccccd + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 8 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (&bcf5c28f, 0, &3c23d70a) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +} diff --git a/data/sample_base/def/climate/default/nice.sii b/data/sample_base/def/climate/default/nice.sii new file mode 100644 index 0000000..8578dcc --- /dev/null +++ b/data/sample_base/def/climate/default/nice.sii @@ -0,0 +1,2073 @@ +SiiNunit +{ +sun_profile : default.nice.01 { + low_elevation: &c11ffffe + high_elevation: &c11ffffe + sun_direction: 0 + temperature: 2 + temperature[0]: 15 + temperature[1]: 15 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_23-24_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_23-24_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_23-24_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_23-24_c.tobj" + skycloud_part: 2 + skycloud_part[0]: night + skycloud_part[1]: night + ambient: 2 + ambient[0]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + ambient[1]: (&3f06f105, &3f469c4a, &3f5cf7e0) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3ba3d70a + ambient_hdr_coef[1]: &3ba3d70a + diffuse: 2 + diffuse[0]: (&3ee9aeb4, &3f26c286, &3f54ad57) + diffuse[1]: (&3ee9aeb4, &3f26c286, &3f54ad57) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3d4ccccd + diffuse_hdr_coef[1]: &3d4ccccd + specular: 2 + specular[0]: (&3ec5da14, &3f21794e, &3f5099ca) + specular[1]: (&3ec5da14, &3f21794e, &3f5099ca) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3d0f5c29 + specular_hdr_coef[1]: &3d0f5c29 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color[1]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3b70f18c, &3c3391f6, &3ca63432) + fog_color[1]: (&3b1f22b4, &3bd5635e, &3c293e69) + fog_color2: 2 + fog_color2[0]: (&3b70f18c, &3c3391f6, &3ca63432) + fog_color2[1]: (&3b1f22b4, &3bd5635e, &3c293e69) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3ad1b717 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.02 { + low_elevation: -90 + high_elevation: -15 + sun_direction: 0 + temperature: 2 + temperature[0]: 15 + temperature[1]: 15 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_1-4_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_1-4_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_1-4_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_1-4_c.tobj" + skycloud_part: 2 + skycloud_part[0]: night + skycloud_part[1]: night + ambient: 2 + ambient[0]: (&3f06f105, &3f469c4a, &3f5cf7e0) + ambient[1]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3b03126f + ambient_hdr_coef[1]: &3b03126f + diffuse: 2 + diffuse[0]: (&3ee9aeb4, &3f26c286, &3f54ad57) + diffuse[1]: (&3ec0c0c2, &3f26a6a7, &3f72f2f4) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3ca3d70a + diffuse_hdr_coef[1]: &3ca3d70a + specular: 2 + specular[0]: (&3ec5da14, &3f21794e, &3f5099ca) + specular[1]: (&3ec5da14, &3f21794e, &3f5099ca) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3c23d70a + specular_hdr_coef[1]: &3c23d70a + env: 2 + env[0]: 1 + env[1]: 0 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color[1]: (&3f70f0f2, &3f27a7a8, &3f048485) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 0 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3a6eb40f, &3b83e1c4, &3be539be) + fog_color[1]: (&3a6eb40f, &3b70f18c, &3b9c87fb) + fog_color2: 2 + fog_color2[0]: (&3a6eb40f, &3b83e1c4, &3be539be) + fog_color2[1]: (&3a6eb40f, &3b70f18c, &3b9c87fb) + fog_vgradient: 2 + fog_vgradient[0]: &3c03126f + fog_vgradient[1]: &3c03126f + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3ad1b717 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (500, 500) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.03 { + low_elevation: &c09ffffe + high_elevation: &c09ffffe + sun_direction: 1 + temperature: 2 + temperature[0]: 17 + temperature[1]: 17 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_5-5_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_5-5_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_5-5_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_5-5_c.tobj" + skycloud_part: 2 + skycloud_part[0]: night + skycloud_part[1]: night + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3c23d70a + ambient_hdr_coef[1]: &3c23d70a + diffuse: 2 + diffuse[0]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3dcccccd + diffuse_hdr_coef[1]: &3dcccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3e4ccccd + specular_hdr_coef[1]: &3e4ccccd + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3daccd6e, &3d97fd48, &3d21fb3c) + sun_color[1]: (&3daccd6e, &3d97fd48, &3d21fb3c) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3bb7ad6c, &3c792d20, &3cf2212b) + fog_color[1]: (&3b33070b, &3c9ec7c0, &3d0ca7e5) + fog_color2: 2 + fog_color2[0]: (&3bb7ad6c, &3c792d20, &3cf2212b) + fog_color2[1]: (&3b33070b, &3c9ec7c0, &3d0ca7e5) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3ad1b717 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 1 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (50, 50) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec5da14, &3e4749e4, &3dea5d16) + sunshaft_color[1]: (&3ec5da14, &3e4749e4, &3dea5d16) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 0 + sunshaft_size[1]: 0 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 2 + weight[1]: 1 +} + +sun_profile : default.nice.04 { + low_elevation: 0 + high_elevation: &411ffffe + sun_direction: 1 + temperature: 2 + temperature[0]: 18 + temperature[1]: 18 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_6-7_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_6-7_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_6-7_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_6-7_c.tobj" + skycloud_part: 2 + skycloud_part[0]: night + skycloud_part[1]: night + ambient: 2 + ambient[0]: (&3ee11abe, &3f2fca9d, &3f7db8df) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3c9374bc + ambient_hdr_coef[1]: &3c9374bc + diffuse: 2 + diffuse[0]: (&3f704db6, &3f337b6b, &3ed5f509) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3e4ccccd + diffuse_hdr_coef[1]: &3e4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3e4ccccd + specular_hdr_coef[1]: &3e4ccccd + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3dcccccd + env_static_mod[1]: &3dcccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color[1]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3c6ca5dd, &3d21fb3b, &3d7add24) + fog_color[1]: (&3c54b6c7, &3d1c7c2f, &3d84b793) + fog_color2: 2 + fog_color2[0]: (&3c6ca5dd, &3d21fb3b, &3d7add24) + fog_color2[1]: (&3c54b6c7, &3d1c7c2f, &3d84b793) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3ad1b717 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3e99999a + cloud_shadow_weight[1]: &3e99999a + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f7fffff, &3f0eef67, &3e90f8df) + sunshaft_color[1]: (&3f7fffff, &3f0eef67, &3e90f8df) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3f000000 + sunshaft_size: 2 + sunshaft_size[0]: &3fc00000 + sunshaft_size[1]: &3fc00000 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 2 + weight[1]: 1 +} + +sun_profile : default.nice.05a { + low_elevation: 15 + high_elevation: &41c7fffe + sun_direction: 1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_8-10_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_8-10_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_8-10_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_8-10_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3dcccccd + ambient_hdr_coef[1]: &3dcccccd + diffuse: 2 + diffuse[0]: (&3f6797e1, &3f2fca9d, &3ede4963) + diffuse[1]: (&3f5099ca, &3f2c253d, &3f00bd2b) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f4ccccd + diffuse_hdr_coef[1]: &3f4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color[1]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: &3fcccccd + sun_color_hdr_coef[1]: &3fa66666 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3dc7dbdf, &3e43ca60, &3e78188b) + fog_color[1]: (&3d11d2af, &3d9018f3, &3def8e51) + fog_color2: 2 + fog_color2[0]: (&3e168e51, &3e90f8df, &3eaa3717) + fog_color2[1]: (&3d5f26b5, &3dba0b37, &3e027f06) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f634eef, &3f23398e, &3e6c471f) + sunshaft_color[1]: (&3f634eef, &3f23398e, &3e6c471f) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f333333 + sunshaft_strength[1]: &3f000000 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.15 + hdr_parameters[1]: hdr.15 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.05b { + low_elevation: 30 + high_elevation: &420bffff + sun_direction: 1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_8-10_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_8-10_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_8-10_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_8-10_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e19999a + ambient_hdr_coef[1]: &3e19999a + diffuse: 2 + diffuse[0]: (&3f6797e1, &3f2fca9d, &3ede4963) + diffuse[1]: (&3f5099ca, &3f2c253d, &3f00bd2b) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f8ccccd + diffuse_hdr_coef[1]: &3f8ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color[1]: (&3f69c0d7, &3f56bb89, &3f0eef66) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3dc7dbdf, &3e43ca60, &3e78188b) + fog_color[1]: (&3cf2212c, &3d8c3e48, &3e05427f) + fog_color2: 2 + fog_color2[0]: (&3e168e51, &3e90f8df, &3eaa3717) + fog_color2[1]: (&3d5f26b5, &3dc7dbdf, &3e080ea2) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f634eef, &3f23398e, &3e6c471f) + sunshaft_color[1]: (&3f634eef, &3f23398e, &3e6c471f) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f333333 + sunshaft_strength[1]: &3f000000 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.15 + hdr_parameters[1]: hdr.15 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.06a { + low_elevation: &421ffffe + high_elevation: 45 + sun_direction: 1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e19999a + ambient_hdr_coef[1]: &3e19999a + diffuse: 2 + diffuse[0]: (&3f7282b0, &3f24fca0, &3ed338ca) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f933333 + diffuse_hdr_coef[1]: 1 + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f2fca9d, &3e822039) + sun_color[1]: (&3f7db8df, &3f5ae0cd, &3f3aff7a) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3ddb2eec, &3e702e07, &3e979f70) + fog_color[1]: (&3db5910e, &3e198f0f, &3e4749e6) + fog_color2: 2 + fog_color2[0]: (&3e3ce704, &3ec5da14, &3ede4963) + fog_color2[1]: (&3de53cd3, &3e4e64c3, &3e7c1c34) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec87c30, &3e932b72, &3e22c6a1) + sunshaft_color[1]: (&3eaca0b5, &3e741e7e, &3da020ba) + sunshaft_strength: 2 + sunshaft_strength[0]: 1 + sunshaft_strength[1]: 1 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.06b { + low_elevation: &4247fffe + high_elevation: &425bfffe + sun_direction: 1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e4ccccd + ambient_hdr_coef[1]: &3e4ccccd + diffuse: 2 + diffuse[0]: (&3f7282b0, &3f24fca0, &3ed338ca) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3fa66666 + diffuse_hdr_coef[1]: &3f99999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f2fca9d, &3e822039) + sun_color[1]: (&3f7db8df, &3f5ae0cd, &3f3aff7a) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3ddb2eec, &3e702e07, &3e979f70) + fog_color[1]: (&3db5910e, &3e198f0f, &3e4749e6) + fog_color2: 2 + fog_color2[0]: (&3e3ce704, &3ec5da14, &3ede4963) + fog_color2[1]: (&3de53cd3, &3e4e64c3, &3e7c1c34) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec87c30, &3e932b72, &3e22c6a1) + sunshaft_color[1]: (&3eaca0b5, &3e741e7e, &3da020ba) + sunshaft_strength: 2 + sunshaft_strength[0]: 1 + sunshaft_strength[1]: 1 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.06c { + low_elevation: 60 + high_elevation: 90 + sun_direction: 0 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_high1_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_high1_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_high1_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_high1_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e4ccccd + ambient_hdr_coef[1]: &3e4ccccd + diffuse: 2 + diffuse[0]: (&3f7282b0, &3f24fca0, &3ed338ca) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3fa66666 + diffuse_hdr_coef[1]: &3f99999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f2fca9d, &3e822039) + sun_color[1]: (&3f7db8df, &3f5ae0cd, &3f3aff7a) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3ddb2eec, &3e702e07, &3e979f70) + fog_color[1]: (&3db5910e, &3e198f0f, &3e4749e6) + fog_color2: 2 + fog_color2[0]: (&3e3ce704, &3ec5da14, &3ede4963) + fog_color2[1]: (&3de53cd3, &3e4e64c3, &3e7c1c34) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec87c30, &3e932b72, &3e22c6a1) + sunshaft_color[1]: (&3eaca0b5, &3e741e7e, &3da020ba) + sunshaft_strength: 2 + sunshaft_strength[0]: 1 + sunshaft_strength[1]: 1 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.06d { + low_elevation: &4247fffe + high_elevation: &425bfffe + sun_direction: -1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e4ccccd + ambient_hdr_coef[1]: &3e4ccccd + diffuse: 2 + diffuse[0]: (&3f7282b0, &3f24fca0, &3ed338ca) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3fa66666 + diffuse_hdr_coef[1]: &3f99999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f2fca9d, &3e822039) + sun_color[1]: (&3f7db8df, &3f5ae0cd, &3f3aff7a) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3ddb2eec, &3e702e07, &3e979f70) + fog_color[1]: (&3db5910e, &3e198f0f, &3e4749e6) + fog_color2: 2 + fog_color2[0]: (&3e3ce704, &3ec5da14, &3ede4963) + fog_color2[1]: (&3de53cd3, &3e4e64c3, &3e7c1c34) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec87c30, &3e932b72, &3e22c6a1) + sunshaft_color[1]: (&3eaca0b5, &3e741e7e, &3da020ba) + sunshaft_strength: 2 + sunshaft_strength[0]: 1 + sunshaft_strength[1]: 1 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.06e { + low_elevation: &421ffffe + high_elevation: 45 + sun_direction: -1 + temperature: 2 + temperature[0]: 25 + temperature[1]: 25 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_11-15_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_11-15_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_11-15_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_11-15_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient[1]: (&3f068687, &3f45c5c7, &3f5ddddf) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3e19999a + ambient_hdr_coef[1]: &3e19999a + diffuse: 2 + diffuse[0]: (&3f7282b0, &3f24fca0, &3ed338ca) + diffuse[1]: (&3f5adadc, &3f30b0b1, &3ef0f0f2) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f933333 + diffuse_hdr_coef[1]: 1 + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3f000000 + env_static_mod[1]: &3f000000 + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f2fca9d, &3e822039) + sun_color[1]: (&3f7db8df, &3f5ae0cd, &3f3aff7a) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3ddb2eec, &3e702e07, &3e979f70) + fog_color[1]: (&3db5910e, &3e198f0f, &3e4749e6) + fog_color2: 2 + fog_color2[0]: (&3e3ce704, &3ec5da14, &3ede4963) + fog_color2[1]: (&3de53cd3, &3e4e64c3, &3e7c1c34) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3ec87c30, &3e932b72, &3e22c6a1) + sunshaft_color[1]: (&3eaca0b5, &3e741e7e, &3da020ba) + sunshaft_strength: 2 + sunshaft_strength[0]: 1 + sunshaft_strength[1]: 1 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 3 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.07a { + low_elevation: 30 + high_elevation: &420bffff + sun_direction: -1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_16-18_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_16-18_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_16-18_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_16-18_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3df5c28f + ambient_hdr_coef[1]: &3df5c28f + diffuse: 2 + diffuse[0]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse[1]: (&3f76f5af, &3f24fca0, &3eb8fd34) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f666666 + diffuse_hdr_coef[1]: &3f666666 + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f40c0d3, &3f24fc9f) + sun_color[1]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3d3f23e3, &3dd1641c, &3e168e51) + fog_color[1]: (&3d810b65, &3ddb2eec, &3e0ae377) + fog_color2: 2 + fog_color2[0]: (&3da44a49, &3e29186a, &3e702e07) + fog_color2[1]: (&3da883d5, &3e0dc105, &3e362861) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3d4ccccd, 0, &bd4ccccd) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f3aff7b, &3f00bd2a, &3e168e51) + sunshaft_color[1]: (&3f5ae0cd, &3f3ed2d1, &3f0884ce) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3ecccccd + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.18 + hdr_parameters[1]: hdr.18 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.07b { + low_elevation: 15 + high_elevation: &41c7fffe + sun_direction: -1 + temperature: 2 + temperature[0]: 22 + temperature[1]: 22 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_16-18_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_16-18_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_16-18_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_16-18_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3da3d70a + ambient_hdr_coef[1]: &3da3d70a + diffuse: 2 + diffuse[0]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse[1]: (&3f76f5af, &3f24fca0, &3eb8fd34) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f333333 + diffuse_hdr_coef[1]: &3f333333 + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: 1 + specular_hdr_coef[1]: 1 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e99999a + env_static_mod[1]: &3e99999a + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f7282b0, &3f40c0d3, &3f24fc9f) + sun_color[1]: (&3f2fca9d, &3f2fca9d, &3f2fca9c) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: &3f666666 + sun_shadow_strength[1]: &3f666666 + fog_color: 2 + fog_color[0]: (&3d3f23e3, &3dd1641c, &3e168e51) + fog_color[1]: (&3d810b65, &3ddb2eec, &3e0ae377) + fog_color2: 2 + fog_color2[0]: (&3da44a49, &3e29186a, &3e702e07) + fog_color2[1]: (&3da883d5, &3e0dc105, &3e362861) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 250 + fog_offset[1]: 150 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3aaa64c3 + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 3 + transition_fog_speed[1]: 3 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f333333 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + leaves_back_color[1]: (&3f7db8e0, &3f6e1bc2, &3eaf0f69) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3d4ccccd, 0, &bd4ccccd) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f3aff7b, &3f00bd2a, &3e168e51) + sunshaft_color[1]: (&3f5ae0cd, &3f3ed2d1, &3f0884ce) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3ecccccd + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.15 + hdr_parameters[1]: hdr.15 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.08 { + low_elevation: &409ffffe + high_elevation: &411ffffe + sun_direction: -1 + temperature: 2 + temperature[0]: 20 + temperature[1]: 20 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_19-20_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_19-20_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_19-20_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_19-20_c.tobj" + skycloud_part: 2 + skycloud_part[0]: day + skycloud_part[1]: day + ambient: 2 + ambient[0]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient[1]: (&3f31b1b2, &3f31b1b2, &3f31b1b2) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3d4ccccd + ambient_hdr_coef[1]: &3d4ccccd + diffuse: 2 + diffuse[0]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse[1]: (&3f75f5f7, &3f25a5a6, &3eb8b8b9) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3f19999a + diffuse_hdr_coef[1]: &3f19999a + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3f000000 + specular_hdr_coef[1]: &3f000000 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e4ccccd + env_static_mod[1]: &3e4ccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f74baad, &3f391a25, &3f18e238) + sun_color[1]: (&3f7933b7, &3f3ce7b4, &3f1c4971) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: &3fc00000 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3d8c3e48, &3d454fd1, &3d65e6fc) + fog_color[1]: (&3d97fd48, &3dd64129, &3dd6412a) + fog_color2: 2 + fog_color2[0]: (&3e027f06, &3da020b9, &3daccd6f) + fog_color2[1]: (&3dd1641c, &3e25eb08, &3e25eb07) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3aaa64c3 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: 1 + fog_speed[1]: 2 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: &3f000000 + cloud_shadow_weight[1]: &3f333333 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_texture[1]: "/model/skybox/clouds_shadows1.tobj" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (1000, 1000) + cloud_shadow_area_size[1]: (1000, 1000) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (5, 5) + cloud_shadow_speed[1]: (5, 5) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (&3d4ccccd, 0, &bd75c28f) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3f5099ca, &3e8a7eb0, &3dc7dbe0) + sunshaft_color[1]: (&3f4a9282, &3f03d1a5, &3e9e72b6) + sunshaft_strength: 2 + sunshaft_strength[0]: &3f000000 + sunshaft_strength[1]: &3f000000 + sunshaft_size: 2 + sunshaft_size[0]: 3 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 1 + weight[1]: 1 +} + +sun_profile : default.nice.09 { + low_elevation: &c09ffffe + high_elevation: 0 + sun_direction: -1 + temperature: 2 + temperature[0]: 17 + temperature[1]: 17 + skybox_texture: 2 + skybox_texture[0]: "/model/skybox/nice0/nice0_21-22_g.tobj" + skybox_texture[1]: "/model/skybox/nice1/nice1_21-22_g.tobj" + skycloud_texture: 2 + skycloud_texture[0]: "/model/skybox/nice0/nice0_21-22_c.tobj" + skycloud_texture[1]: "/model/skybox/nice1/nice1_21-22_c.tobj" + skycloud_part: 2 + skycloud_part[0]: night + skycloud_part[1]: night + ambient: 2 + ambient[0]: (&3f06f105, &3f469c4a, &3f5cf7e0) + ambient[1]: (&3ee6cd64, &3f42b1bd, &3f800001) + ambient_hdr_coef: 2 + ambient_hdr_coef[0]: &3c23d70a + ambient_hdr_coef[1]: &3c23d70a + diffuse: 2 + diffuse[0]: (&3f6571ea, &3f0884cf, &3ecb2381) + diffuse[1]: (&3f40c0d3, &3f13dc51, &3efe6e01) + diffuse_hdr_coef: 2 + diffuse_hdr_coef[0]: &3e4ccccd + diffuse_hdr_coef[1]: &3e4ccccd + specular: 2 + specular[0]: (1, 1, 1) + specular[1]: (1, 1, 1) + specular_hdr_coef: 2 + specular_hdr_coef[0]: &3d8f5c29 + specular_hdr_coef[1]: &3d8f5c29 + env: 2 + env[0]: 1 + env[1]: 1 + env_static_mod: 2 + env_static_mod[0]: &3e4ccccd + env_static_mod[1]: &3e4ccccd + sky_color: 2 + sky_color[0]: (1, 1, 1) + sky_color[1]: (1, 1, 1) + sky_color_hdr_coef: 2 + sky_color_hdr_coef[0]: 1 + sky_color_hdr_coef[1]: 1 + sun_color: 2 + sun_color[0]: (&3f3aff7b, &3ec87c30, &3e9c274c) + sun_color[1]: (&3c830aa7, &3d11d2af, &3d8c3e48) + sun_color_hdr_coef: 2 + sun_color_hdr_coef[0]: 1 + sun_color_hdr_coef[1]: 1 + sun_shadow_strength: 2 + sun_shadow_strength[0]: 1 + sun_shadow_strength[1]: 1 + fog_color: 2 + fog_color[0]: (&3cc5e638, &3ca63432, &3d02a569) + fog_color[1]: (&3b8fe614, &3c6ca5dd, &3d0ca7e5) + fog_color2: 2 + fog_color2[0]: (&3d84b793, &3d39152b, &3d588467) + fog_color2[1]: (&3bb7ad6c, &3c9ec7c0, &3d454fd1) + fog_vgradient: 2 + fog_vgradient[0]: &3ba3d70a + fog_vgradient[1]: &3ba3d70a + fog_offset: 2 + fog_offset[0]: 180 + fog_offset[1]: 120 + fog_density: 2 + fog_density[0]: &3ad1b717 + fog_density[1]: &3b03126f + fog_speed: 2 + fog_speed[0]: &3f000000 + fog_speed[1]: &3f000000 + transition_fog_speed: 2 + transition_fog_speed[0]: 5 + transition_fog_speed[1]: 5 + speed_coef: 2 + speed_coef[0]: &3e75c2a2 + speed_coef[1]: &3e75c2a2 + cloud_shadow_weight: 2 + cloud_shadow_weight[0]: 0 + cloud_shadow_weight[1]: 0 + cloud_shadow_texture: 2 + cloud_shadow_texture[0]: "null" + cloud_shadow_texture[1]: "null" + cloud_shadow_area_size: 2 + cloud_shadow_area_size[0]: (0, 0) + cloud_shadow_area_size[1]: (0, 0) + cloud_shadow_speed: 2 + cloud_shadow_speed[0]: (0, 0) + cloud_shadow_speed[1]: (0, 0) + rain_intensity: 2 + rain_intensity[0]: 0 + rain_intensity[1]: 0 + rain_max_wetness: 2 + rain_max_wetness[0]: 1 + rain_max_wetness[1]: 1 + rain_additional_ambient: 2 + rain_additional_ambient[0]: 5 + rain_additional_ambient[1]: 5 + leaves_back_color: 2 + leaves_back_color[0]: (1, 1, 1) + leaves_back_color[1]: (1, 1, 1) + wind_type: 2 + wind_type[0]: 1 + wind_type[1]: 1 + dof_start: 2 + dof_start[0]: 120 + dof_start[1]: 120 + dof_transition: 2 + dof_transition[0]: 700 + dof_transition[1]: 700 + dof_filter_size: 2 + dof_filter_size[0]: 1 + dof_filter_size[1]: 1 + color_balance: 2 + color_balance[0]: (0, 0, 0) + color_balance[1]: (0, 0, 0) + color_saturation: 2 + color_saturation[0]: 1 + color_saturation[1]: 1 + sunshaft_color: 2 + sunshaft_color[0]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_color[1]: (&3e949495, &3e78f8fa, &3e5cdcde) + sunshaft_strength: 2 + sunshaft_strength[0]: 0 + sunshaft_strength[1]: 0 + sunshaft_size: 2 + sunshaft_size[0]: 2 + sunshaft_size[1]: 2 + hdr_parameters: 2 + hdr_parameters[0]: hdr.12 + hdr_parameters[1]: hdr.12 + weight: 2 + weight[0]: 2 + weight[1]: 1 +} + +}