From 92708cfa348be4f7eaf8dd130e1042ddc7be4710 Mon Sep 17 00:00:00 2001 From: Simon Lusenc Date: Wed, 19 Feb 2020 15:20:25 +0100 Subject: [PATCH] Release - 2.0.1 --- addon/io_scs_tools/__init__.py | 2 +- addon/io_scs_tools/exp/pim/exporter.py | 4 ++ addon/io_scs_tools/exp/pim_ef/exporter.py | 4 ++ .../internals/shaders/flavors/paint.py | 2 +- addon/io_scs_tools/operators/bases/export.py | 7 ++- addon/io_scs_tools/operators/material.py | 54 ++++++++++--------- addon/io_scs_tools/operators/scene.py | 5 +- addon/io_scs_tools/shader_presets.txt | 7 ++- addon/io_scs_tools/utils/material.py | 31 ++++++----- addon/io_scs_tools/utils/path.py | 6 ++- 10 files changed, 78 insertions(+), 44 deletions(-) diff --git a/addon/io_scs_tools/__init__.py b/addon/io_scs_tools/__init__.py index 1ba452f1..112514be 100644 --- a/addon/io_scs_tools/__init__.py +++ b/addon/io_scs_tools/__init__.py @@ -22,7 +22,7 @@ "name": "SCS Tools", "description": "Setup models, Import-Export SCS data format", "author": "Simon Lusenc (50keda), Milos Zajic (4museman)", - "version": (2, 0, "fae8eb4"), + "version": (2, 0, "dd0ff0b"), "blender": (2, 81, 0), "location": "File > Import-Export", "wiki_url": "http://modding.scssoft.com/wiki/Documentation/Tools/SCS_Blender_Tools", diff --git a/addon/io_scs_tools/exp/pim/exporter.py b/addon/io_scs_tools/exp/pim/exporter.py index 9e61d317..33303fe0 100644 --- a/addon/io_scs_tools/exp/pim/exporter.py +++ b/addon/io_scs_tools/exp/pim/exporter.py @@ -132,6 +132,10 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat # create mesh object data sections for mesh_i, mesh_obj in enumerate(mesh_objects): + if mesh_obj.mode != 'OBJECT': + lprint("W Invalid object mode detected on: %r, skipping it on export!", (mesh_obj.name,)) + continue + lprint("I Preparing mesh object: %r ...", (mesh_obj.name,)) # create part if it doesn't exists yet diff --git a/addon/io_scs_tools/exp/pim_ef/exporter.py b/addon/io_scs_tools/exp/pim_ef/exporter.py index 1d0fe072..172b1127 100644 --- a/addon/io_scs_tools/exp/pim_ef/exporter.py +++ b/addon/io_scs_tools/exp/pim_ef/exporter.py @@ -113,6 +113,10 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat # create mesh object data sections for mesh_obj in mesh_objects: + if mesh_obj.mode != 'OBJECT': + lprint("W Invalid object mode detected on: %r, skipping it on export!", (mesh_obj.name,)) + continue + vert_groups = mesh_obj.vertex_groups # calculate faces flip state from all ancestors of current object diff --git a/addon/io_scs_tools/internals/shaders/flavors/paint.py b/addon/io_scs_tools/internals/shaders/flavors/paint.py index dd979139..0c88f65e 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/paint.py +++ b/addon/io_scs_tools/internals/shaders/flavors/paint.py @@ -122,4 +122,4 @@ def set_color(node_tree, color): return color = _convert_utils.to_node_color(color) - node_tree.nodes[PAINT_MULT_NODE].inputs[1].default_value = color + node_tree.nodes[PAINT_MULT_NODE].inputs[1].default_value = color[:3] diff --git a/addon/io_scs_tools/operators/bases/export.py b/addon/io_scs_tools/operators/bases/export.py index 67e0c45c..4ac0eb19 100644 --- a/addon/io_scs_tools/operators/bases/export.py +++ b/addon/io_scs_tools/operators/bases/export.py @@ -68,7 +68,7 @@ def get_objects_for_export(self): for child_obj in children: if child_obj.select_get(): children_selected.append(child_obj) - else: + elif child_obj.users_scene: # unlinked objects shouldn't be exported children_unselected.append(child_obj) # if any children was selected make sure, to export only them @@ -79,7 +79,10 @@ def get_objects_for_export(self): elif export_scope == "scene": objs_to_export = tuple(bpy.context.scene.objects) elif export_scope == "scenes": - objs_to_export = tuple(bpy.data.objects) + scenes_objs = set() + for scene in bpy.data.scenes: + scenes_objs.update(scene.objects) + objs_to_export = tuple(scenes_objs) # cache filtered list, to be able to retrive it quickly on second call self.cached_objects = objs_to_export diff --git a/addon/io_scs_tools/operators/material.py b/addon/io_scs_tools/operators/material.py index 3ea1eceb..06b6037c 100644 --- a/addon/io_scs_tools/operators/material.py +++ b/addon/io_scs_tools/operators/material.py @@ -520,7 +520,7 @@ class SCS_TOOLS_OT_MaterialItemExtrasMenu(bpy.types.Operator): bl_label = "More Options" bl_idname = "material.scs_tools_material_item_extras" bl_description = "Show more options for this material item (WT, Copy Linear Color etc.)" - bl_options = {'REGISTER', 'UNDO'} + bl_options = set() property_str: StringProperty( description="String representing which property should be worked on.", @@ -635,12 +635,33 @@ class SCS_TOOLS_OT_WriteThroughLooks(bpy.types.Operator): is_ctrl = False # WT to same look of this material in other SCS Roots is_shift = False # is_shift + is_ctrl -> WT to all looks of this material in all SCS Roots + def init_control_states(self): + + # decide which type of WT is it, prefer manually set type over ctrl and shift modifiers + if self.wt_type == self.WT_TYPE_NORMAL: + self.is_ctrl = False + self.is_shift = False + elif self.wt_type == self.WT_TYPE_SAME_LOOK: + self.is_ctrl = True + self.is_shift = False + elif self.wt_type == self.WT_TYPE_ALL: + self.is_ctrl = True + self.is_shift = True + else: + self.is_ctrl = False + self.is_shift = False + + # always reset type for next invoke + self.wt_type = -1 + def execute(self, context): material = context.active_object.active_material if not material or not hasattr(material.scs_props, self.property_str): return {'CANCELLED'} + self.init_control_states() + scs_roots = [] active_scs_root = _object_utils.get_scs_root(context.active_object) if active_scs_root: @@ -652,11 +673,17 @@ def execute(self, context): if self.is_shift or not self.is_ctrl: # WT either on active only or all SCS roots; (Shift + Ctrl) or none altered_looks = 0 + altered_scs_roots = 0 for scs_root in scs_roots: - altered_looks += _looks.write_through(scs_root, material, self.property_str) + res = _looks.write_through(scs_root, material, self.property_str) + + # only log altered looks if write trought succeded + if res > 0: + altered_looks += res + altered_scs_roots += 1 if altered_looks > 0: - message = "Write through successfully altered %s looks on %s SCS Root Objects!" % (altered_looks, len(scs_roots)) + message = "Write through successfully altered %s looks on %s SCS Root Objects!" % (altered_looks, altered_scs_roots) else: message = "Nothing to write through." @@ -698,27 +725,6 @@ def execute(self, context): return {'FINISHED'} - def invoke(self, context, event): - - # decide which type of WT is it, prefer manually set type over ctrl and shift modifiers - if self.wt_type == self.WT_TYPE_NORMAL: - self.is_ctrl = False - self.is_shift = False - elif self.wt_type == self.WT_TYPE_SAME_LOOK: - self.is_ctrl = True - self.is_shift = False - elif self.wt_type == self.WT_TYPE_ALL: - self.is_ctrl = True - self.is_shift = True - else: - self.is_ctrl = False - self.is_shift = False - - # always reset type for next invoke - self.wt_type = -1 - - return self.execute(context) - class Tobj: """ diff --git a/addon/io_scs_tools/operators/scene.py b/addon/io_scs_tools/operators/scene.py index 73b74dbe..34deac4a 100644 --- a/addon/io_scs_tools/operators/scene.py +++ b/addon/io_scs_tools/operators/scene.py @@ -2699,6 +2699,7 @@ def export_to_sii(self, op_inst, config_path): pjs_alternate_uvset: BoolProperty(default=False) pjs_flipflake: BoolProperty(default=False) pjs_airbrush: BoolProperty(default=False) + pjs_stock: BoolProperty(default=False) @staticmethod def do_report(the_type, message, do_report=False): @@ -2888,7 +2889,7 @@ def export_texture(self, orig_img, tgas_dir_path, texture_portion): img = bpy.data.images[_SINGLE_COLOR_IMAGE_NAME] img.colorspace_settings.name = "sRGB" # make sure we use sRGB color-profile - img.use_alpha = True + img.alpha_mode = 'STRAIGHT' img.pixels[:] = comparing_pixel * 16 # we use shared prefix for 4x4 textures in case any other portion will be using same one @@ -2898,7 +2899,7 @@ def export_texture(self, orig_img, tgas_dir_path, texture_portion): int(comparing_pixel[3] * 255.0)) tga_path = os.path.join(tgas_dir_path, tga_name) - img.save_render(tga_path, bpy.context.scene) + img.save_render(tga_path, scene=bpy.context.scene) lprint("I Texture portion %r has only one color in common texture, optimizing it by exporting 4x4px TGA!", (texture_portion.id,)) diff --git a/addon/io_scs_tools/shader_presets.txt b/addon/io_scs_tools/shader_presets.txt index cd33d009..582ca277 100644 --- a/addon/io_scs_tools/shader_presets.txt +++ b/addon/io_scs_tools/shader_presets.txt @@ -1270,6 +1270,11 @@ Shader { Tag: "tint_opacity" Value: ( -0.01 ) } + Attribute { + Format: INT + Tag: "queue_bias" + Value: ( 0 ) + } Texture { Tag: "texture[X]:texture_base" Value: "/vehicle/truck/share/glass_ex" @@ -2021,4 +2026,4 @@ Flavor { FriendlyTag: "TexGen1 Scale (X,Z)" Value: ( 8.0 8.0 ) } -} \ No newline at end of file +} diff --git a/addon/io_scs_tools/utils/material.py b/addon/io_scs_tools/utils/material.py index a0b52150..ddeffc58 100644 --- a/addon/io_scs_tools/utils/material.py +++ b/addon/io_scs_tools/utils/material.py @@ -175,15 +175,22 @@ def get_reflection_image(texture_path, report_invalid=False): if not abs_tobj_filepath or not os.path.isfile(abs_tobj_filepath): return None - # check existance of this cubemap - if teximag_id_name in bpy.data.images: + # 1. reuse existing image texture if possible, otherwise construct first free slot - if _path.get_abs_path(bpy.data.images[teximag_id_name].filepath) == abs_tobj_filepath: - return bpy.data.images[teximag_id_name] + postfix = 0 + postfixed_tex = teximag_id_name + while postfixed_tex in bpy.data.images: - bpy.data.images.remove(bpy.data.images[teximag_id_name]) + img_exists = postfixed_tex in bpy.data.images + if img_exists and _path.repair_path(bpy.data.images[postfixed_tex].filepath) == _path.repair_path(abs_tobj_filepath): + return bpy.data.images[postfixed_tex] - # 1. get all textures file paths and check their existance + postfix += 1 + postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) + + teximag_id_name = postfixed_tex + + # 2. get all textures file paths and check their existance abs_texture_filepaths = _path.get_texture_paths_from_tobj(abs_tobj_filepath) @@ -221,13 +228,13 @@ def get_reflection_image(texture_path, report_invalid=False): return None - # 2. create image objects for all planes + # 3. create image objects for all planes images = [] for abs_texture_filepath in abs_texture_filepaths: images.append(bpy.data.images.load(abs_texture_filepath)) - # 3. setup scene, create planes, create camera projector and assign images + # 4. setup scene, create planes, create camera projector and assign images old_scene = bpy.context.window.scene tmp_scene = bpy.data.scenes.new("cubemap") @@ -314,7 +321,7 @@ def get_reflection_image(texture_path, report_invalid=False): tmp_scene.collection.objects.link(cam_obj) - # 4. render & save image + # 5. render & save image final_image_path = os.path.join(tempfile.gettempdir(), teximag_id_name + ".tga") @@ -329,7 +336,7 @@ def get_reflection_image(texture_path, report_invalid=False): tmp_scene.render.filepath = final_image_path bpy.ops.render.render(write_still=True, scene=tmp_scene.name) - # 5. cleanup & scene restoring + # 6. cleanup & scene restoring for obj in objects: bpy.data.objects.remove(obj) @@ -349,14 +356,14 @@ def get_reflection_image(texture_path, report_invalid=False): bpy.context.window.scene = old_scene bpy.data.scenes.remove(tmp_scene) - # 6. load temp image and pack it + # 7. load temp image and pack it final_image = bpy.data.images.load(final_image_path) final_image.name = teximag_id_name final_image.alpha_mode = 'CHANNEL_PACKED' final_image.pack() - # 7. set filepath to original image + # 8. set filepath to original image final_image.filepath = abs_tobj_filepath return final_image diff --git a/addon/io_scs_tools/utils/path.py b/addon/io_scs_tools/utils/path.py index 488662c8..3334a49b 100644 --- a/addon/io_scs_tools/utils/path.py +++ b/addon/io_scs_tools/utils/path.py @@ -391,8 +391,12 @@ def get_texture_path_from_tobj(tobj_filepath, raw_value=False): :return: absolute texture file path if found or None :rtype: str | None """ + texture_paths = get_texture_paths_from_tobj(tobj_filepath, raw_value=raw_value, first_only=True) - return get_texture_paths_from_tobj(tobj_filepath, raw_value=raw_value, first_only=True)[0] + if not texture_paths: + return None + + return texture_paths[0] def get_texture_paths_from_tobj(tobj_filepath, raw_value=False, first_only=False):