def get_shader_preset(shader_presets_filepath, template_name): """Returns requested Shader Preset data from preset file. :param shader_presets_filepath: A file path to SCS shader preset file, can be absolute or relative :type shader_presets_filepath: str :param template_name: Preset name :type template_name: str :return: Preset data section :rtype: SectionData """ # print('shader_presets_filepath: %r' % shader_presets_filepath) if shader_presets_filepath.startswith(str(os.sep + os.sep)): # IF RELATIVE PATH, MAKE IT ABSOLUTE shader_presets_filepath = _path.get_abs_path(shader_presets_filepath) preset_section = None if os.path.isfile(shader_presets_filepath): presets_container = _pix_container.get_data_from_file(shader_presets_filepath, ' ') if presets_container: for section in presets_container: if section.type == "Shader": for prop in section.props: if prop[0] == "PresetName": if prop[1] == template_name: # print(' + template name: "%s"' % template_name) preset_section = section break else: lprint('\nW The file path "%s" is not valid!', (shader_presets_filepath,)) return preset_section
def update_traffic_rules_library_rel_path(scs_traffic_rules_inventory, traffic_rules_library_rel_path, readonly=False): """The function deletes and populates again a list of Traffic Rules names in inventory. It also updates corresponding record in config file. :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.get_abs_path(traffic_rules_library_rel_path) if traffic_rules_library_filepath: trul_container = _sii.get_data_from_file(traffic_rules_library_filepath) if trul_container: # CLEAR INVENTORY scs_traffic_rules_inventory.clear() # 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 'num_params' in item.props: traffic_rule_item.num_params = str(item.props['num_params']) else: print(' traffic_rules_library_rel_path: "%s"' % str(traffic_rules_library_rel_path)) if not readonly: update_item_in_file('Paths.TrafficRulesRelFilePath', traffic_rules_library_rel_path)
def update_matsubs_inventory(scs_matsubs_inventory, matsubs_library_rel_path, readonly=False): """The function deletes and populates again a list of Material Substance names in inventory. It also updates corresponding record in config file. :param matsubs_library_rel_path: Relative path to the directory with Material Substance files :type matsubs_library_rel_path: str """ matsubs_library_filepath = _path_utils.get_abs_path(matsubs_library_rel_path) if matsubs_library_filepath: matsubs_container = _sii.get_data_from_file(matsubs_library_filepath) if matsubs_container: # CLEAR INVENTORY scs_matsubs_inventory.clear() # ADD "NONE" ITEM IN INVENTORY matsubs_item = scs_matsubs_inventory.add() matsubs_item.name = "None" matsubs_item.item_id = 'none' matsubs_item.item_description = "No Material Substance" # ADD ALL THE OTHER ITEMS FROM CONTAINER INTO INVENTORY for item in matsubs_container: if item.type == 'game_substance': if item.id.startswith('.'): if 'name' in item.props: matsubs_name = item.props['name'] else: continue matsubs_item = scs_matsubs_inventory.add() matsubs_item.name = matsubs_name matsubs_item.item_id = item.id[1:] # matsubs_item.item_description = "" if not readonly: update_item_in_file('Paths.MatSubsRelFilePath', matsubs_library_rel_path)
def update_hookup_library_rel_path(scs_hookup_inventory, hookup_library_rel_path, readonly=False): """The function deletes and populates again a list of Hookup names in inventory. It also updates corresponding record in config file. :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] else: hookup_file.model = item.props['model'] if 'brand_idx' in item.props: try: hookup_file.brand_idx = int(item.props['brand_idx']) except: pass if 'dir_type' in item.props: hookup_file.dir_type = item.props['dir_type'] 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)
def invoke(self, context, event): """Invoke a file path selector.""" filepath = _get_scs_globals().shader_presets_filepath if filepath.startswith("//"): self.filepath = _path_utils.get_abs_path(filepath, skip_mod_check=True) else: self.filepath = filepath context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def update_sign_library_rel_path(scs_sign_model_inventory, sign_library_rel_path, readonly=False): """The function deletes and populates again a list of Sign names in inventory. It also updates corresponding record in config file. :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] # CLEAR INVENTORY scs_sign_model_inventory.clear() for sign_library_filepath in sign_library_filepaths: 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:] 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 '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 not readonly: update_item_in_file('Paths.SignRelFilePath', sign_library_rel_path)
def load(locator): """Makes a preview model for a locator and link it to it NOTE: locator preview model path must be set :param locator: locator object to which preview model should be set :type locator: bpy.types.Object :return: True if preview model was set; False otherwise :rtype: bool """ load_model = True abs_filepath = "" if not locator.scs_props.locator_show_preview_model: load_model = False else: filepath = locator.scs_props.locator_preview_model_path if filepath: if filepath.lower().endswith(".pim"): abs_filepath = _path_utils.get_abs_path(filepath, skip_mod_check=True) if not os.path.isfile(abs_filepath): lprint("W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, abs_filepath.replace("\\", "/"))) load_model = False else: lprint("W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, filepath.replace("\\", "/"))) load_model = False else: load_model = False if load_model: unload(locator) prem_name = str("prem_" + locator.name) obj = _get_model_mesh(locator, prem_name) if not obj: from io_scs_tools.imp import pim as _pim_import obj = _pim_import.load_pim_file(bpy.context, abs_filepath, preview_model=True) obj.name = prem_name obj.data.name = prem_name obj.data.scs_props.locator_preview_model_path = locator.scs_props.locator_preview_model_path obj.select = False link(locator, obj) return True else: return False
def invoke(self, context, event): """Invoke a file path selector.""" curr_texture_path = getattr(bpy.context.active_object.active_material.scs_props, self.shader_texture) extensions, curr_texture_path = _path_utils.get_texture_extens_and_strip_path(curr_texture_path) for ext in extensions: filepath = _path_utils.get_abs_path(curr_texture_path + ext) if os.path.isfile(filepath): self.filepath = filepath break else: self.filepath = _get_scs_globals().scs_project_path context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def reload_tobj_settings(material, tex_type): """Relaods TOBJ settings on given texture type of material. If tobj doesn't exists it does nothing. :param material: material :type material: bpy.types.Material :param tex_type: texture type :type tex_type: str """ shader_texture_str = "shader_texture_" + tex_type shader_texture_filepath = getattr(material.scs_props, shader_texture_str) if is_valid_shader_texture_path(shader_texture_filepath, tobj_check=True): tobj_file = _path.get_abs_path(shader_texture_filepath[:-4] + ".tobj") # intentionally set ID property directly to avoid update function invoke material.scs_props[shader_texture_str + "_settings"] = int(_tobj_imp.get_settings(tobj_file), 2) setattr(material.scs_props, shader_texture_str + "_tobj_load_time", str(os.path.getmtime(tobj_file)))
def update_tsem_library_rel_path(scs_tsem_profile_inventory, tsem_library_rel_path, readonly=False): """The function deletes and populates again a list of Traffic Semaphore Profile names in inventory. It also updates corresponding record in config file. :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] # CLEAR INVENTORY scs_tsem_profile_inventory.clear() for tsem_library_filepath in tsem_library_filepaths: 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:] 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)
def execute(self, context): material = context.active_object.active_material if material: shader_texture_filepath = getattr(material.scs_props, "shader_texture_" + self.texture_type) if _material_utils.is_valid_shader_texture_path(shader_texture_filepath): tex_filepath = _path_utils.get_abs_path(shader_texture_filepath) if tex_filepath and (tex_filepath.endswith(".tga") or tex_filepath.endswith(".png")): if _tobj_exp.export(tex_filepath[:-4] + ".tobj", os.path.basename(tex_filepath), set()): _material_utils.reload_tobj_settings(material, self.texture_type) else: self.report({'ERROR'}, "Please load texture properly first!") return {'FINISHED'}
def get_shader_presets_container(shader_presets_filepath): """Returns shader presets data continaer from given path. :param shader_presets_filepath: relative or absolute shader presets filepath :type shader_presets_filepath: str :return: data container if file is found; None otherwise :rtype: io_scs_tools.internals.structure.SectionData """ presets_container = None if shader_presets_filepath.startswith("//"): # IF RELATIVE PATH, MAKE IT ABSOLUTE shader_presets_filepath = _path.get_abs_path(shader_presets_filepath) if os.path.isfile(shader_presets_filepath): presets_container = _pix_container.get_data_from_file(shader_presets_filepath, ' ') else: lprint('\nW The file path "%s" is not valid!', (shader_presets_filepath,)) return presets_container
def __update_shader_texture_tobj_file__(self, context, tex_type): """Hookup function for updating TOBJ file on any texture type. :param context: Blender context :type context: bpy.types.Context :param tex_type: string representig texture type :type tex_type: str """ # dummy context arg usage so IDE doesn't report it as unused if context == context: pass shader_texture_str = "shader_texture_" + tex_type if _material_utils.is_valid_shader_texture_path(getattr(self, shader_texture_str)): tex_filepath = _path_utils.get_abs_path(getattr(self, shader_texture_str)) if tex_filepath and (tex_filepath.endswith(".tga") or tex_filepath.endswith(".png")): tobj_file = tex_filepath[:-4] + ".tobj" if _tobj_exp.export(tobj_file, os.path.basename(tex_filepath), getattr(self, shader_texture_str + "_settings")): self[shader_texture_str + "_tobj_load_time"] = str(os.path.getmtime(tobj_file))
def is_valid_shader_texture_path(shader_texture, tobj_check=False): """It returns True if there is valid Shader Texture file, otherwise False. :param shader_texture: SCS texture path, can be absolute or relative :type shader_texture: str :return: True if there is valid Shader Texture file, otherwise False :rtype: bool """ if shader_texture != "": if tobj_check and (shader_texture.endswith(".tga") or shader_texture.endswith(".png")): shader_texture = shader_texture[:-4] + ".tobj" if shader_texture.startswith("//"): # RELATIVE PATH shader_texture_abs_path = _path.get_abs_path(shader_texture) if shader_texture_abs_path: if os.path.isfile(shader_texture_abs_path): return True else: # ABSOLUTE PATH if os.path.isfile(shader_texture): return True return False
def update_trigger_actions_rel_path(scs_trigger_actions_inventory, trigger_actions_rel_path, readonly=False): """The function deletes and populates again a list of Trigger Actions in inventory. It also updates corresponding record in config file. :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] # CLEAR INVENTORY scs_trigger_actions_inventory.clear() for trig_actions_path in trig_actions_paths: trig_actions_container = _sii.get_data_from_file(trig_actions_path) if trig_actions_container: # 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)
def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_filepath): """The function deletes and populates again a list of Shader Preset items in inventory. It also updates corresponding record in config 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.get_abs_path(shader_presets_filepath) else: shader_presets_abs_path = shader_presets_filepath # CLEAR INVENTORY scs_shader_presets_inventory.clear() if os.path.isfile(shader_presets_abs_path): # ADD DEFAULT PRESET ITEM "<none>" INTO INVENTORY new_shader_preset = scs_shader_presets_inventory.add() new_shader_preset.name = "<none>" presets_container = _pix.get_data_from_file(shader_presets_abs_path, ' ') # ADD ALL PRESET ITEMS FROM FILE INTO INVENTORY if presets_container: for section in presets_container: if section.type == "Shader": for prop in section.props: if prop[0] == "PresetName": preset_name = prop[1] # print(' + preset name: "%s"' % preset_name) new_shader_preset = scs_shader_presets_inventory.add() new_shader_preset.name = preset_name else: lprint('\nW The file path "%s" is not valid!', (shader_presets_abs_path,)) update_item_in_file('Paths.ShaderPresetsFilePath', shader_presets_filepath)
def get_texture_image(texture_path, texture_type, report_invalid=False): """Creates and returns image for given texture path and type. :param texture_path: Texture path :type texture_path: str :param texture_type: Texture type keyword :type texture_type: str :param report_invalid: flag indicating if invalid texture should be reported in 3d view :type report_invalid: bool :return: loaded image datablock to be used in SCS material :rtype: bpy.types.Image """ # get reflection image texture if texture_path.endswith(".tobj") and texture_type == "reflection": return get_reflection_image(texture_path, report_invalid=report_invalid) # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = _path.get_filename(texture_path, with_ext=False) # CREATE ABSOLUTE FILEPATH abs_texture_filepath = _path.get_abs_path(texture_path) # return None on non-existing texture file path if not abs_texture_filepath or not os.path.isfile(abs_texture_filepath): return None if abs_texture_filepath.endswith(".tobj"): abs_texture_filepath = _path.get_texture_path_from_tobj(abs_texture_filepath) # if not existing or none supported file if abs_texture_filepath is None or abs_texture_filepath[-4:] not in (".tga", ".png", ".dds"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) # take care of none existing paths referenced in tobj texture names if abs_texture_filepath: lprint("W Texture can't be displayed as TOBJ file: %r is referencing non texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) else: lprint("W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file.", (texture_path,)) if report_invalid: lprint("", report_warnings=1, report_errors=1) return None image = None if abs_texture_filepath and os.path.isfile(abs_texture_filepath): # reuse existing image texture if possible postfix = 0 postfixed_tex = teximag_id_name while postfixed_tex in bpy.data.images: 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_texture_filepath): image = bpy.data.images[postfixed_tex] break postfix += 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) # if image wasn't found create new one if not image: image = None # reuse existing image if possible postfix = 0 postfixed_img = teximag_id_name while postfixed_img in bpy.data.images: if _path.repair_path(bpy.data.images[postfixed_img].filepath) == _path.repair_path(abs_texture_filepath): image = bpy.data.images[postfixed_img] break postfix += 1 postfixed_img = teximag_id_name + "." + str(postfix).zfill(3) # if image wasn't found load it if not image: image = bpy.data.images.load(abs_texture_filepath) image.name = teximag_id_name image.alpha_mode = 'CHANNEL_PACKED' # try to get relative path to the Blender file and set it to the image if bpy.data.filepath != '': # empty file path means blender file is not saved try: rel_path = _path.relative_path(os.path.dirname(bpy.data.filepath), abs_texture_filepath) except ValueError: # catch different mount paths: "path is on mount 'C:', start on mount 'E:'" rel_path = None if rel_path: image.filepath = rel_path if image is None and texture_path.endswith(".tobj"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) lprint("W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) if report_invalid: lprint("", report_warnings=1, report_errors=1) return image
def set_shader_data_to_material(material, section, preset_effect, is_import=False, override_back_data=True): """Used to set up material properties from given shader data even via UI or on import. :param material: :type material: bpy.types.Material :param section: :param preset_effect: :return: """ attributes = {} textures = {} attribute_i = 0 texture_i = 0 used_attribute_types = {} # attribute types used by current shader used_texture_types = {} # texture types used by current shader and should be overlooked during clearing of texture slots for item in section.sections: if item.type == "Attribute": attribute_data = {} for prop in item.props: key, value = prop # # GETTING RID OF "[" AND "]" CHARS... if type(value) is str: value = value.replace("[", "").replace("]", "") attribute_data[key] = value attribute_type = attribute_data['Tag'] attributes[str(attribute_i)] = attribute_data attribute_i += 1 used_attribute_types[attribute_type] = attribute_data elif item.type == "Texture": texture_data = {} for prop in item.props: # print(' prop: "%s"' % str(prop)) texture_data[prop[0]] = prop[1] # APPLY SECTION TEXTURE VALUES texture_type = texture_data['Tag'].split(':')[1] tex_type = texture_type[8:] used_texture_types[tex_type] = texture_data textures[str(texture_i)] = texture_data texture_i += 1 # clear attribute values for current shader to be stored in blend data block # NOTE: looks also takes into account that all the unused properties are omitted from scs_props dict scs_props_keys = material.scs_props.keys() for key in scs_props_keys: is_key_used = False if key.startswith("shader_attribute"): for used_attribute in used_attribute_types: if used_attribute in key[16:]: is_key_used = True if key.startswith("shader_texture"): for used_tex_type in used_texture_types: if used_tex_type in key[14:]: is_key_used = True # delete only unused shader keys everything else should stay in the place # as those keys might be used in some other way if not is_key_used and key.startswith("shader_"): lprint("D Unsetting property from material in set_shader_data %s:", (key,)) material.scs_props.property_unset(key) # apply used attributes created_attributes = {} for attribute_type in used_attribute_types.keys(): attribute_data = used_attribute_types[attribute_type] if attribute_type in ("diffuse", "specular", "env_factor", "fresnel", "tint"): material.scs_props["shader_attribute_" + attribute_type] = attribute_data['Value'] created_attributes[attribute_type] = material.scs_props["shader_attribute_" + attribute_type] elif attribute_type in ("shininess", "add_ambient", "reflection", "reflection2", "shadow_bias", "tint_opacity"): material.scs_props["shader_attribute_" + attribute_type] = attribute_data['Value'][0] created_attributes[attribute_type] = material.scs_props["shader_attribute_" + attribute_type] elif attribute_type.startswith("aux") and hasattr(material.scs_props, "shader_attribute_" + attribute_type): auxiliary_prop = getattr(material.scs_props, "shader_attribute_" + attribute_type, None) # clean old values possible left from previous shader while len(auxiliary_prop) > 0: auxiliary_prop.remove(0) for val in attribute_data['Value']: item = auxiliary_prop.add() item['value'] = val item['aux_type'] = attribute_type created_attributes[attribute_type] = material.scs_props["shader_attribute_" + attribute_type] elif attribute_type == "substance": material.scs_props.substance = attribute_data['Value'][0] # if shader attribute properties are still unset reset it to default if material.scs_props.substance == _MAT_consts.unset_substance and "substance" not in material.scs_props.keys(): material.scs_props.substance = "None" # apply used textures created_textures = {} created_tex_uvs = {} for tex_type in used_texture_types: texture_data = used_texture_types[tex_type] if tex_type in material.scs_props.get_texture_types().keys(): if "Lock" in texture_data: setattr(material.scs_props, "shader_texture_" + tex_type + "_locked", bool(texture_data["Lock"])) texture_mappings = getattr(material.scs_props, "shader_texture_" + tex_type + "_uv") # clear all texture mapping for current texture from previous shader if override_back_data: while len(texture_mappings) > 0: texture_mappings.remove(0) # if shader is imported try to create custom tex coord mappings on material if material.scs_props.active_shader_preset_name == "<imported>" and "scs_tex_aliases" in material: custom_maps = material.scs_props.custom_tex_coord_maps for tex_coord_key in sorted(material["scs_tex_aliases"].keys()): if _invetory.get_index(custom_maps, "tex_coord_" + tex_coord_key) == -1: new_map = custom_maps.add() new_map.name = "tex_coord_" + tex_coord_key new_map.value = material["scs_tex_aliases"][tex_coord_key] # add one mapping field for using it as a preview uv layer in case of imported shader mapping = texture_mappings.add() mapping.texture_type = tex_type mapping.tex_coord = -1 # if there is an info about mapping in shader use it (in case of imported material this condition will fall!) elif "TexCoord" in texture_data: for tex_coord in texture_data['TexCoord']: tex_coord = int(tex_coord) if tex_coord != -1: mapping = texture_mappings.add() mapping.texture_type = tex_type mapping.tex_coord = tex_coord if "scs_tex_aliases" in material: mapping.value = material["scs_tex_aliases"][str(tex_coord)] # for now make sure to use only first coord mapping info for shader if len(texture_mappings) == 1: created_tex_uvs[tex_type] = mapping.value # set bitmap file to current texture bitmap_filepath = _path.get_bitmap_filepath(texture_data['Value']) # apply texture path if not empty, except if import is going on # NOTE: during import bitmap has to be applied even if empty # because otherwise texture from previous look might be applied if (bitmap_filepath and bitmap_filepath != "") or is_import: material.scs_props["shader_texture_" + tex_type] = bitmap_filepath created_textures[tex_type] = get_texture(bitmap_filepath, tex_type) if is_import: # only if shader is imported then make sure that by default imported values will be used if material.scs_props.active_shader_preset_name == "<imported>": setattr(material.scs_props, "shader_texture_" + tex_type + "_use_imported", True) setattr(material.scs_props, "shader_texture_" + tex_type + "_imported_tobj", texture_data['Value']) # if property is still unset reset it to empty if getattr(material.scs_props, "shader_texture_" + tex_type, "") == _MAT_consts.unset_bitmap_filepath: material.scs_props["shader_texture_" + tex_type] = "" else: bitmap_filepath = _path.get_abs_path(getattr(material.scs_props, "shader_texture_" + tex_type, "")) created_textures[tex_type] = get_texture(bitmap_filepath, tex_type) # override shader data for identifying used attributes and textures in UI if override_back_data: shader_data = {'effect': preset_effect, 'attributes': attributes, 'textures': textures} material["scs_shader_attributes"] = shader_data # setup nodes for 3D view visualization _shader.setup_nodes(material, preset_effect, created_attributes, created_textures, created_tex_uvs, override_back_data)
def execute(self, context): material = context.material tex_raw_path = getattr(material.scs_props, "shader_texture_base", "") tex_raw_path = tex_raw_path.replace("\\", "/") is_aliased_directory = ("/material/road" in tex_raw_path or "/material/terrain" in tex_raw_path or "/material/custom" in tex_raw_path) # abort if empty texture or not aliased directory if not (tex_raw_path != "" and is_aliased_directory): self.report({ 'ERROR' }, "Base texture is not from aliased directories, aliasing aborted!" ) return {'CANCELLED'} tex_abs_path = _path_utils.get_abs_path(tex_raw_path) mat_abs_path = tex_abs_path[:tex_abs_path.rfind(".")] + ".mat" # abort if aliasing material doesn't exists if not os.path.isfile(mat_abs_path): self.report({'ERROR'}, "Aliasing material not found, aliasing aborted!") return {'CANCELLED'} # finally try to do aliasing from io_scs_tools.internals.containers import mat as mat_container mat_cont = mat_container.get_data_from_file(mat_abs_path) # set attributes for attr_tuple in mat_cont.get_attributes().items(): attr_key = attr_tuple[0] attr_val = attr_tuple[1] if attr_key == "substance": if "substance" not in material.scs_props.keys(): continue setattr(material.scs_props, "substance", attr_val) elif attr_key.startswith("aux"): attr_key = attr_key.replace("[", "").replace("]", "") auxiliary_prop = getattr(material.scs_props, "shader_attribute_" + attr_key, None) if "shader_attribute_" + attr_key not in material.scs_props.keys( ): continue for i, val in enumerate(attr_val): auxiliary_prop[i].value = val else: if "shader_attribute_" + attr_key not in material.scs_props.keys( ): continue setattr(material.scs_props, "shader_attribute_" + attr_key, attr_val) # set textures for tex_tuple in mat_cont.get_textures().items(): if "shader_texture_" + tex_tuple[ 0] not in material.scs_props.keys(): continue setattr(material.scs_props, "shader_texture_" + tex_tuple[0], tex_tuple[1]) # report success of aliasing if mat_cont.get_effect() == material.scs_props.mat_effect_name: self.report({'INFO'}, "Material fully aliased!") else: msg = ( "Aliased shader type doesn't match with current one, aliasing was not complete!\n" + "Current shader type: %r\n" % material.scs_props.mat_effect_name + "Aliased shader type: %r\n\n" % mat_cont.get_effect() + "If you want to alias values from aliased material completely,\n" "select correct shader preset and flavor combination and execute aliasing again!" ) bpy.ops.wm.show_warning_message( "INVOKE_DEFAULT", icon="INFO", title="Aliasing partially successful!", message=msg) self.report({'WARNING'}, "Aliased partially succeded!") return {'FINISHED'}
def get_texture(texture_path, texture_type, report_invalid=False): """Creates and setup Texture and Image data on active Material. :param texture_path: Texture path :type texture_path: str :param texture_type: Texture type keyword :type texture_type: str :param report_invalid: flag indicating if invalid texture should be reported in 3d view :type report_invalid: bool """ # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = _path.get_filename(texture_path, with_ext=False) # CREATE ABSOLUTE FILEPATH abs_texture_filepath = _path.get_abs_path(texture_path) # return None on non-existing texture file path if not abs_texture_filepath or not os.path.isfile(abs_texture_filepath): return None if abs_texture_filepath.endswith(".tobj"): abs_texture_filepath = _path.get_texture_path_from_tobj( abs_texture_filepath) # if not existing or none supported file if abs_texture_filepath is None or abs_texture_filepath[-4:] not in ( ".tga", ".png", ".dds"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) # take care of none existing paths referenced in tobj texture names if abs_texture_filepath: lprint( "W Texture can't be displayed as TOBJ file: %r is referencing non texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) else: lprint( "W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file.", (texture_path, )) if report_invalid: lprint("", report_warnings=1, report_errors=1) return None texture = None if abs_texture_filepath and os.path.isfile(abs_texture_filepath): # find existing texture with this image if teximag_id_name in bpy.data.textures: # reuse existing image texture if possible postfix = 0 postfixed_tex = teximag_id_name while postfixed_tex in bpy.data.textures: img_exists = bpy.data.textures[postfixed_tex].image is not None if img_exists and _path.repair_path( bpy.data.textures[postfixed_tex].image.filepath ) == _path.repair_path(abs_texture_filepath): texture = bpy.data.textures[postfixed_tex] break postfix += 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) # if texture wasn't found create new one if not texture: texture = bpy.data.textures.new(teximag_id_name, 'IMAGE') image = None # reuse existing image if possible postfix = 0 postfixed_img = teximag_id_name while postfixed_img in bpy.data.images: if _path.repair_path( bpy.data.images[postfixed_img].filepath ) == _path.repair_path(abs_texture_filepath): image = bpy.data.images[postfixed_img] break postfix += 1 postfixed_img = teximag_id_name + "." + str(postfix).zfill(3) # if image wasn't found load it if not image: image = bpy.data.images.load(abs_texture_filepath) image.name = teximag_id_name # try to get relative path to the Blender file and set it to the image if bpy.data.filepath != '': # empty file path means blender file is not saved try: rel_path = _path.relative_path( os.path.dirname(bpy.data.filepath), abs_texture_filepath) except ValueError: # catch different mount paths: "path is on mount 'C:', start on mount 'E:'" rel_path = None if rel_path: image.filepath = rel_path # finally link image to texture texture.image = image image.use_alpha = True # 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 # otherwise Blender completely messes up normals calculation if texture.image.filepath.endswith(".tga"): texture.image.colorspace_settings.name = "Non-Color" else: texture.image.colorspace_settings.name = "sRGB" else: texture.image.colorspace_settings.name = "sRGB" # set usage of normal map if texture type is correct texture.use_normal_map = (texture_type == "nmap") if texture is None and texture_path.endswith(".tobj"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) lprint( "W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) if report_invalid: lprint("", report_warnings=1, report_errors=1) return texture
def export(root_object, used_parts, used_materials, scene, filepath): scs_globals = _get_scs_globals() output_type = scs_globals.output_type file_name = root_object.name print("\n************************************") print("** SCS PIT Exporter **") print("** (c)2014 SCS Software **") print("************************************\n") # DATA GATHERING look_list = [] variant_list = [] saved_active_look = root_object.scs_props.active_scs_look looks_inventory = root_object.scs_object_look_inventory looks_count = len(looks_inventory) if looks_count <= 0: looks_count = 1 for i in range(0, looks_count): # apply each look from inventory first if len(looks_inventory) > 0: root_object.scs_props.active_scs_look = i # actually write values to material because Blender might not refresh data yet _looks.apply_active_look(root_object) curr_look_name = looks_inventory[i].name else: # if no looks create default curr_look_name = "default" material_dict = {} material_list = [] # get materials data for material in used_materials: if material is not None: # if material in ("_empty_slot_", "_empty_material_"): # NOTE: only workaround until module doesn't gets rewritten if material in bpy.data.materials: material = bpy.data.materials[material] if isinstance(material, str): material_name = str(material + "-_default_settings_") # DEFAULT MATERIAL material_export_data = _default_material(material_name) material_list.append(material_name) else: # print('material name: %r' % material.name) material_name = material.name material_list.append(material) # SUBSTANCE if material.scs_props.substance != 'None': lprint('D material.name: %r\tmat.scs_props.substance: "%s"', (material.name, str(material.scs_props.substance))) # TODO: Substance Export... # MATERIAL EFFECT # shader_data = material.get("scs_shader_attributes", {}) # effect_name = shader_data.get('effect', "NO EFFECT") effect_name = material.scs_props.mat_effect_name # CgFX SHADERS # print("\n=== GET SHADER EXPORT DATA =======================") ## NOTE: The following code is OBSOLETE!!! # cgfx_export_data = None # print(" cgfx_export_data:\n%s" % str(cgfx_export_data)) # if cgfx_export_data: # print("\nAttributes:") # for attribute in cgfx_export_data['attributes']: # if cgfx_export_data['attributes'][attribute]: # print(" %s:" % str(attribute)) # for rec in cgfx_export_data['attributes'][attribute]: # print(" %s: %s" % (str(rec), str(cgfx_export_data['attributes'][attribute][rec]))) # else: # print("%s:\n %s" % (str(attribute), cgfx_export_data['attributes'][attribute])) # print("\nTextures:") # for attribute in cgfx_export_data['textures']: # if cgfx_export_data['textures'][attribute]: # print(" %s:" % str(attribute)) # for rec in cgfx_export_data['textures'][attribute]: # print(" %s: %s" % (str(rec), str(cgfx_export_data['textures'][attribute][rec]))) # else: # print("%s:\n %s" % (str(attribute), cgfx_export_data['textures'][attribute])) # else: # Print(1, 'E No CgFX data for material %r!' % material.name) # print("==================================================") # PRESET SHADERS preset_found = False alias = "NO SHADER" def_cnt = attribute_cnt = texture_cnt = 0 def_sections = [] attribute_sections = [] texture_sections = [] active_shader_preset_name = material.scs_props.active_shader_preset_name # print(' active_shader_preset_name: %r' % active_shader_preset_name) for preset_i, preset in enumerate(scene.scs_shader_presets_inventory): # print(' preset[%i]: %r' % (preset_i, preset.name)) if preset.name == active_shader_preset_name: # print(' - material %r - %r' % (material.name, preset.name)) # LOAD PRESET shader_presets_abs_path = _path_utils.get_abs_path(scs_globals.shader_presets_filepath) # shader_presets_filepath = _get_scs_globals().shader_presets_filepath # print('shader_presets_filepath: %r' % shader_presets_filepath) # if shader_presets_filepath.startswith(str(os.sep + os.sep)): ## RELATIVE PATH # shader_presets_abs_path = get_abs_path(shader_presets_filepath) # else: # shader_presets_abs_path = shader_presets_filepath if os.path.isfile(shader_presets_abs_path): presets_container = _pix_container.get_data_from_file(shader_presets_abs_path, ' ') # FIND THE PRESET IN FILE if presets_container: for section in presets_container: if section.type == "Shader": section_properties = _get_properties(section) if 'PresetName' in section_properties: preset_name = section_properties['PresetName'] if preset_name == active_shader_preset_name: alias = material.name # print(' + preset name: %r' % preset_name) # COLLECT ATTRIBUTES AND TEXTURES for item in section.sections: # DATA EXCHANGE FORMAT ATRIBUTE if item.type == "DataExchangeFormat": def_data = _SectionData("DataExchangeFormat") for rec in item.props: def_data.props.append((rec[0], rec[1])) def_sections.append(def_data) def_cnt += 1 # ATTRIBUTES if item.type == "Attribute": # print(' Attribute:') attribute_data = _SectionData("Attribute") for rec in item.props: # print(' rec: %r' % str(rec)) if rec[0] == "Format": attribute_data.props.append((rec[0], rec[1])) elif rec[0] == "Tag": # tag_prop = rec[1].replace("[", "").replace("]", "") # attribute_data.props.append((rec[0], tag_prop)) attribute_data.props.append((rec[0], rec[1])) elif rec[0] == "Value": format_prop = item.get_prop("Format")[1] tag_prop = item.get_prop("Tag")[1] tag_prop = tag_prop.replace("[", "").replace("]", "") # print(' format_prop: %r' % str(format_prop)) # print(' tag_prop: %r' % str(tag_prop)) if "aux" in tag_prop: aux_props = getattr(material.scs_props, "shader_attribute_" + tag_prop) value = [] for aux_prop in aux_props: value.append(aux_prop.value) else: value = getattr(material.scs_props, "shader_attribute_" + tag_prop, "NO TAG") # print(' value: %s' % str(value)) if format_prop == 'FLOAT': attribute_data.props.append((rec[0], ["&&", (value,)])) else: attribute_data.props.append((rec[0], ["i", tuple(value)])) attribute_sections.append(attribute_data) attribute_cnt += 1 # TEXTURES elif item.type == "Texture": # print(' Texture:') texture_data = _SectionData("Texture") for rec in item.props: # print(' rec: %r' % str(rec)) if rec[0] == "Tag": tag_prop = rec[1].split(":")[1] tag = str("texture[" + str(texture_cnt) + "]:" + tag_prop) texture_data.props.append((rec[0], tag)) elif rec[0] == "Value": tag_prop = item.get_prop("Tag")[1].split(":")[1] # print(' tag_prop: %r' % str(tag_prop)) # create and get path to tobj tobj_rel_path = _get_texture_path_from_material(material, tag_prop, os.path.dirname(filepath)) texture_data.props.append((rec[0], tobj_rel_path)) texture_sections.append(texture_data) texture_cnt += 1 preset_found = True break else: lprint('\nW The file path "%s" is not valid!', (shader_presets_abs_path,)) if preset_found: break if preset_found: material_export_data = _SectionData("Material") material_export_data.props.append(("Alias", alias)) material_export_data.props.append(("Effect", effect_name)) material_export_data.props.append(("Flags", 0)) if output_type.startswith('def'): material_export_data.props.append(("DataExchangeFormatCount", def_cnt)) material_export_data.props.append(("AttributeCount", attribute_cnt)) material_export_data.props.append(("TextureCount", texture_cnt)) if output_type.startswith('def'): for def_section in def_sections: material_export_data.sections.append(def_section) for attribute in attribute_sections: material_export_data.sections.append(attribute) for texture in texture_sections: material_export_data.sections.append(texture) elif active_shader_preset_name == "<imported>": material_attributes = material['scs_shader_attributes']['attributes'].to_dict().values() material_textures = material['scs_shader_attributes']['textures'].to_dict().values() material_export_data = _SectionData("Material") material_export_data.props.append(("Alias", material.name)) material_export_data.props.append(("Effect", effect_name)) material_export_data.props.append(("Flags", 0)) material_export_data.props.append(("AttributeCount", len(material_attributes))) material_export_data.props.append(("TextureCount", len(material_textures))) for attribute_dict in material_attributes: attribute_section = _SectionData("Attribute") format_value = "" for attr_prop in sorted(attribute_dict.keys()): # get the format of current attribute (we assume that "Format" attribute is before "Value" attribute in this for loop) if attr_prop == "Format": format_value = attribute_dict[attr_prop] if attr_prop == "Value" and ("FLOAT" in format_value or "STRING" in format_value): attribute_section.props.append((attr_prop, ["i", tuple(attribute_dict[attr_prop])])) elif attr_prop == "Tag" and "aux" in attribute_dict[attr_prop]: attribute_section.props.append((attr_prop, "aux[" + attribute_dict[attr_prop][3:] + "]")) else: attribute_section.props.append((attr_prop, attribute_dict[attr_prop])) material_export_data.sections.append(attribute_section) for texture_dict in material_textures: texture_section = _SectionData("Texture") tag_id_string = "" for tex_prop in sorted(texture_dict.keys()): if tex_prop == "Tag": tag_id_string = texture_dict[tex_prop].split(':')[1] if tex_prop == "Value" and tag_id_string != "": tobj_rel_path = _get_texture_path_from_material(material, tag_id_string, os.path.dirname(filepath)) texture_section.props.append((tex_prop, tobj_rel_path)) else: texture_section.props.append((tex_prop, texture_dict[tex_prop])) material_export_data.sections.append(texture_section) else: # DEFAULT MATERIAL material_name = str("_" + material_name + "_-_default_settings_") material_export_data = _default_material(material_name) material_dict[material_name] = material_export_data # create materials sections for looks material_sections = _fill_material_sections(material_list, material_dict) look_data = { "name": curr_look_name, "material_sections": material_sections } look_list.append(look_data) # restore look applied before export root_object.scs_props.active_scs_look = saved_active_look # PARTS AND VARIANTS... part_list_cnt = len(used_parts.keys()) if len(root_object.scs_object_variant_inventory) == 0: # If there is no Variant, add the Default one... part_list = _fill_part_list(root_object.scs_object_part_inventory, used_parts, all_parts=True) variant_list.append((_VARIANT_consts.default_name, part_list), ) else: for variant in root_object.scs_object_variant_inventory: part_list = _fill_part_list(variant.parts, used_parts) variant_list.append((variant.name, part_list), ) # DATA CREATION header_section = _fill_header_section(file_name, scs_globals.sign_export) look_section = _fill_look_sections(look_list) # part_sections = fill_part_section(part_list) variant_section = _fill_variant_sections(variant_list) comment_header_section = _fill_comment_header_section(look_list, variant_list) global_section = _fill_global_section(len(look_list), len(variant_list), part_list_cnt, len(used_materials)) # DATA ASSEMBLING pit_container = [comment_header_section, header_section, global_section] for section in look_section: pit_container.append(section) for section in variant_section: pit_container.append(section) # FILE EXPORT ind = " " pit_filepath = str(filepath + ".pit") result = _pix_container.write_data_to_file(pit_container, pit_filepath, ind) # print("************************************") return result
def get_reflection_image(texture_path, report_invalid=False): """Gets reflection image for given texture path. 1. gets all textures names and check existance 2. create image objects for all planes 3. setup scene, create planes, create camera projector and assign images 4. render & save image 5. cleanup & scene restoring 6. load temp image and pack it 7. set filepath to TOBJ :param texture_path: Texture path :type texture_path: str :param report_invalid: flag indicating if invalid texture should be reported in 3d view :type report_invalid: bool :return: loaded image datablock to be used in SCS material :rtype: bpy.types.Image """ # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = _path.get_filename(texture_path, with_ext=False) + "_cubemap" # CREATE ABSOLUTE FILEPATH abs_tobj_filepath = _path.get_abs_path(texture_path) # return None on non-existing TOBJ 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: if _path.get_abs_path(bpy.data.images[teximag_id_name].filepath) == abs_tobj_filepath: return bpy.data.images[teximag_id_name] bpy.data.images.remove(bpy.data.images[teximag_id_name]) # 1. get all textures file paths and check their existance abs_texture_filepaths = _path.get_texture_paths_from_tobj(abs_tobj_filepath) # should be a cubemap with six images if not abs_texture_filepaths or len(abs_texture_filepaths) != 6: return None # all six images have to exist for abs_texture_filepath in abs_texture_filepaths: if abs_texture_filepath[-4:] not in (".tga", ".png", ".dds"): # none supported file if report_invalid: lprint("", report_warnings=-1, report_errors=-1) lprint("W Texture can't be displayed as TOBJ file: %r is referencing non texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) if report_invalid: lprint("", report_warnings=1, report_errors=1) return None elif not os.path.isfile(abs_texture_filepath): # none existing file if report_invalid: lprint("", report_warnings=-1, report_errors=-1) # take care of none existing paths referenced in tobj texture names lprint("W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) if report_invalid: lprint("", report_warnings=1, report_errors=1) return None # 2. 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 old_scene = bpy.context.window.scene tmp_scene = bpy.data.scenes.new("cubemap") bpy.context.window.scene = tmp_scene meshes = [] materials = [] objects = [] for i, plane in enumerate(("x+", "x-", "y+", "y-", "z+", "z-")): # mesh creation bm = bmesh.new(use_operators=True) bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=1, calc_uvs=True) mesh = bpy.data.meshes.new(plane) bm.to_mesh(mesh) bm.free() mesh.uv_layers.new() meshes.append(mesh) # material creation material = bpy.data.materials.new(plane) material.use_nodes = True material.node_tree.nodes.clear() out_node = material.node_tree.nodes.new("ShaderNodeOutputMaterial") emission_node = material.node_tree.nodes.new("ShaderNodeEmission") tex_node = material.node_tree.nodes.new("ShaderNodeTexImage") tex_node.image = images[i] material.node_tree.links.new(emission_node.inputs['Color'], tex_node.outputs['Color']) material.node_tree.links.new(out_node.inputs['Surface'], emission_node.outputs['Emission']) mesh.materials.append(material) materials.append(material) # object creation obj = bpy.data.objects.new(mesh.name, mesh) obj.location = (0,) * 3 obj.rotation_euler = (0,) * 3 if plane == "x+": obj.rotation_euler.x = pi * 0.5 obj.location.y = 1 elif plane == "x-": obj.rotation_euler.x = pi * 0.5 obj.rotation_euler.z = pi obj.location.y = -1 elif plane == "y+": obj.rotation_euler.x = pi obj.rotation_euler.z = pi * 0.5 obj.location.z = 1 elif plane == "y-": obj.rotation_euler.z = pi * 0.5 obj.location.z = -1 elif plane == "z+": obj.rotation_euler.x = pi * 0.5 obj.rotation_euler.z = pi * 0.5 obj.location.x = -1 elif plane == "z-": obj.rotation_euler.x = pi * 0.5 obj.rotation_euler.z = -pi * 0.5 obj.location.x = 1 tmp_scene.collection.objects.link(obj) objects.append(obj) # camera creation camera = bpy.data.cameras.new("projector") camera.type = "PANO" camera.lens = 5 camera.sensor_width = 32 camera.cycles.panorama_type = "EQUIRECTANGULAR" camera.cycles.latitude_min = -pi * 0.5 camera.cycles.latitude_max = pi * 0.5 camera.cycles.longitude_min = pi camera.cycles.longitude_max = -pi cam_obj = bpy.data.objects.new(camera.name, camera) cam_obj.location = (0,) * 3 cam_obj.rotation_euler = (pi * 0.5, 0, 0) tmp_scene.collection.objects.link(cam_obj) # 4. render & save image final_image_path = os.path.join(tempfile.gettempdir(), teximag_id_name + ".tga") tmp_scene.render.engine = "CYCLES" tmp_scene.cycles.samples = 1 tmp_scene.camera = cam_obj tmp_scene.render.image_settings.file_format = "TARGA" tmp_scene.render.image_settings.color_mode = "RGBA" tmp_scene.render.resolution_percentage = 100 tmp_scene.render.resolution_x = images[0].size[0] * 4 tmp_scene.render.resolution_y = images[0].size[1] * 2 tmp_scene.render.filepath = final_image_path bpy.ops.render.render(write_still=True, scene=tmp_scene.name) # 5. cleanup & scene restoring for obj in objects: bpy.data.objects.remove(obj) for mesh in meshes: bpy.data.meshes.remove(mesh) for material in materials: bpy.data.materials.remove(material) for image in images: bpy.data.images.remove(image) bpy.data.objects.remove(cam_obj) bpy.data.cameras.remove(camera) bpy.context.window.scene = old_scene bpy.data.scenes.remove(tmp_scene) # 6. 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 final_image.filepath = abs_tobj_filepath return final_image
def execute(self, context): material = context.material tex_raw_path = getattr(material.scs_props, "shader_texture_base", "") tex_raw_path = tex_raw_path.replace("\\", "/") is_aliased_directory = ("/material/road" in tex_raw_path or "/material/terrain" in tex_raw_path or "/material/custom" in tex_raw_path) # abort if empty texture or not aliased directory if not (tex_raw_path != "" and is_aliased_directory): self.report({'ERROR'}, "Base texture is not from aliased directories, aliasing aborted!") return {'CANCELLED'} tex_abs_path = _path_utils.get_abs_path(tex_raw_path) mat_abs_path = tex_abs_path[:tex_abs_path.rfind(".")] + ".mat" # abort if aliasing material doesn't exists if not os.path.isfile(mat_abs_path): self.report({'ERROR'}, "Aliasing material not found, aliasing aborted!") return {'CANCELLED'} # finally try to do aliasing from io_scs_tools.internals.containers import mat as mat_container mat_cont = mat_container.get_data_from_file(mat_abs_path) # set attributes for attr_tuple in mat_cont.get_attributes().items(): attr_key = attr_tuple[0] attr_val = attr_tuple[1] if attr_key == "substance": if "substance" not in material.scs_props.keys(): continue setattr(material.scs_props, "substance", attr_val) elif attr_key.startswith("aux"): attr_key = attr_key.replace("[", "").replace("]", "") auxiliary_prop = getattr(material.scs_props, "shader_attribute_" + attr_key, None) if "shader_attribute_" + attr_key not in material.scs_props.keys(): continue for i, val in enumerate(attr_val): auxiliary_prop[i].value = val else: if "shader_attribute_" + attr_key not in material.scs_props.keys(): continue setattr(material.scs_props, "shader_attribute_" + attr_key, attr_val) # set textures for tex_tuple in mat_cont.get_textures().items(): if "shader_texture_" + tex_tuple[0] not in material.scs_props.keys(): continue setattr(material.scs_props, "shader_texture_" + tex_tuple[0], tex_tuple[1]) # report success of aliasing if mat_cont.get_effect() == material.scs_props.mat_effect_name: self.report({'INFO'}, "Material fully aliased!") else: msg = ("Aliased shader type doesn't match with current one, aliasing was not complete!\n" + "Current shader type: %r\n" % material.scs_props.mat_effect_name + "Aliased shader type: %r\n\n" % mat_cont.get_effect() + "If you want to alias values from aliased material completely,\n" "select correct shader preset and flavor combination and execute aliasing again!") bpy.ops.wm.show_warning_message("INVOKE_DEFAULT", icon="INFO", title="Aliasing partially successful!", message=msg) self.report({'WARNING'}, "Aliased partially succeded!") return {'FINISHED'}
def validate(self): """Validates content of TOBJ container. If any problem is found, validation is canceled and problems reported. :return: True if content is valid; False otherwise :rtype: bool """ if self.map_type not in self.MAP_TYPES: lprint("E Unknown TOBJ map type %r, should be one of: %r.", (self.map_type, self.MAP_TYPES)) return False tex_count = 6 if self.map_type == "cube" else 1 # only cube map has 6 textures if len(self.map_names) != tex_count: lprint("E Not enough textures referenced %s/%s in TOBJ.", (len(self.map_names), tex_count)) return False for map_name in self.map_names: # use None string for invalid map names if map_name is None or len(map_name) < 1: tex_path = "None" elif map_name[0] == "/": tex_path = _path_utils.get_abs_path("//" + map_name[1:]) else: tex_path = os.path.join(os.path.split(self.filepath)[0], map_name) if not os.path.isfile(tex_path): lprint("E Texture %r used in TOBJ doesn't exists.", (tex_path,)) return False addr_count = 3 if self.map_type == "cube" else int(self.map_type[0]) if len(self.addr) > 0: if len(self.addr) != addr_count: lprint("E Not enough address values %s/%s in TOBJ.", (len(self.addr), addr_count)) return False for addr_entry in self.addr: if addr_entry not in self.ADDR_TYPES: lprint("E Unknown TOBJ texture address type: %r, should be one of: %r.", (addr_entry, self.ADDR_TYPES)) return False if self.bias != -1: try: self.bias = int(self.bias) except ValueError: lprint("E Invalid TOBJ bias value: %r, should be non-negative integer.", (self.bias,)) return False if len(self.filter) > 0: if len(self.filter) != 2: lprint("E Invalid number of filter values %s/2 in TOBJ.", (len(self.filter),)) return False for i in range(2): if self.filter[i] not in self.FILTER_TYPES: lprint("E Invalid TOBJ filter value: %s, should be one of: %r.", (self.filter[i], self.FILTER_TYPES)) return False if self.target not in self.TARGET_TYPES: lprint("E Invalid TOBJ target value: %s, should be one of: %r.", (self.target, self.TARGET_TYPES)) return False if len(self.border_color) > 0: if len(self.border_color) != 4: lprint("E Not enough border color values %s/4 in TOBJ.", (len(self.border_color),)) return False for i in range(4): try: self.border_color[i] = float(self.border_color[i]) except ValueError: lprint("E Invalid TOBJ border color value: %s, should be float.", (self.border_color[i],)) return False if self.color_space not in self.COLOR_SPACE_TYPES: lprint("E Invalid TOBJ color_space value: %s, should be one of: %r.", (self.color_space, self.COLOR_SPACE_TYPES)) return False if self.usage not in self.USAGE_TYPES: lprint("E Invalid TOBJ usage value: %s, should be one of: %r.", (self.usage, self.USAGE_TYPES)) return False return True
def validate(self): """Validates content of TOBJ container. If any problem is found, validation is canceled and problems reported. :return: True if content is valid; False otherwise :rtype: bool """ if self.map_type not in self.MAP_TYPES: lprint("E Unknown TOBJ map type %r, should be one of: %r.", (self.map_type, self.MAP_TYPES)) return False tex_count = 6 if self.map_type == "cube" else 1 # only cube map has 6 textures if len(self.map_names) != tex_count: lprint("E Not enough textures referenced %s/%s in TOBJ.", (len(self.map_names), tex_count)) return False for map_name in self.map_names: # use None string for invalid map names if map_name is None or len(map_name) < 1: tex_path = "None" elif map_name[0] == "/": tex_path = _path_utils.get_abs_path("//" + map_name[1:]) else: tex_path = os.path.join( os.path.split(self.filepath)[0], map_name) if not os.path.isfile(tex_path): lprint("E Texture %r used in TOBJ doesn't exists.", (tex_path, )) return False addr_count = 3 if self.map_type == "cube" else int(self.map_type[0]) if len(self.addr) > 0: if len(self.addr) != addr_count: lprint("E Not enough address values %s/%s in TOBJ.", (len(self.addr), addr_count)) return False for addr_entry in self.addr: if addr_entry not in self.ADDR_TYPES: lprint( "E Unknown TOBJ texture address type: %r, should be one of: %r.", (addr_entry, self.ADDR_TYPES)) return False if self.bias != -1: try: self.bias = int(self.bias) except ValueError: lprint( "E Invalid TOBJ bias value: %r, should be non-negative integer.", (self.bias, )) return False if len(self.filter) > 0: if len(self.filter) != 2: lprint("E Invalid number of filter values %s/2 in TOBJ.", (len(self.filter), )) return False for i in range(2): if self.filter[i] not in self.FILTER_TYPES: lprint( "E Invalid TOBJ filter value: %s, should be one of: %r.", (self.filter[i], self.FILTER_TYPES)) return False if self.target not in self.TARGET_TYPES: lprint("E Invalid TOBJ target value: %s, should be one of: %r.", (self.target, self.TARGET_TYPES)) return False if len(self.border_color) > 0: if len(self.border_color) != 4: lprint("E Not enough border color values %s/4 in TOBJ.", (len(self.border_color), )) return False for i in range(4): try: self.border_color[i] = float(self.border_color[i]) except ValueError: lprint( "E Invalid TOBJ border color value: %s, should be float.", (self.border_color[i], )) return False if self.color_space not in self.COLOR_SPACE_TYPES: lprint( "E Invalid TOBJ color_space value: %s, should be one of: %r.", (self.color_space, self.COLOR_SPACE_TYPES)) return False if self.usage not in self.USAGE_TYPES: lprint("E Invalid TOBJ usage value: %s, should be one of: %r.", (self.usage, self.USAGE_TYPES)) return False return True
def update_shader_presets_path(scs_shader_presets_inventory, shader_presets_filepath): """The function deletes and populates again a list of Shader Preset items in inventory. It also updates corresponding record in config 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 # CLEAR INVENTORY AND CACHE scs_shader_presets_inventory.clear() _shader_presets_cache.clear() if os.path.isfile(shader_presets_abs_path): # ADD DEFAULT PRESET ITEM "<none>" INTO INVENTORY new_shader_preset = scs_shader_presets_inventory.add() new_shader_preset.name = "<none>" presets_container = _pix.get_data_from_file(shader_presets_abs_path, ' ') # ADD ALL SHADER PRESET ITEMS FROM FILE INTO INVENTORY if presets_container: # sort sections to shaders and flavors shaders = [] flavors = {} for section in presets_container: if section.type == "Shader": shaders.append(section) elif section.type == "Flavor": flavors[section.get_prop_value("Type")] = section for shader in shaders: unique_names = [] shader_flavors = shader.get_prop_value("Flavors") # create new preset item new_shader_preset = scs_shader_presets_inventory.add() new_shader_preset.name = shader.get_prop_value("PresetName") new_shader_preset.effect = shader.get_prop_value("Effect") unique_names.append("") _shader_presets_cache.add_section(new_shader_preset, "", shader) if shader_flavors: for j, flavor_types in enumerate(shader_flavors): # create new flavor item flavor_item = new_shader_preset.flavors.add() new_unique_names = [] for i, flavor_type in enumerate(flavor_types.split("|")): if flavor_type not in flavors: lprint("D Flavor used by shader preset, but not defined: %s", (flavor_type,)) continue # create new flavor variant item flavor_variant = flavor_item.variants.add() flavor_variant.name = flavors[flavor_type].get_prop_value("Name") flavor_variant.preset_name = new_shader_preset.name # modify and save section as string into cache for unique_name in unique_names: section = _shader_presets_cache.get_section(new_shader_preset, unique_name) for flavor_section in flavors[flavor_type].sections: flavor_section_tag = flavor_section.get_prop_value("Tag") # check if current flavor section already exists in section, # then override props and sections directly otherwise add flavor section for subsection in section.sections: subsection_tag = subsection.get_prop_value("Tag") if subsection_tag and subsection_tag == flavor_section_tag: subsection.props = flavor_section.props subsection.sections = flavor_section.sections break 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) unique_names.extend(new_unique_names) update_item_in_file('Paths.ShaderPresetsFilePath', shader_presets_filepath)
def get_texture(texture_path, texture_type): """Creates and setup Texture and Image data on active Material. :param texture_path: Texture path :type texture_path: str :param texture_type: Texture type keyword :type texture_type: str """ # print(' texture_path: %s' % str(texture_path)) # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = _path.get_filename(texture_path, with_ext=False) # CREATE ABSOLUTE FILEPATH abs_texture_filepath = _path.get_abs_path(texture_path) texture = None if abs_texture_filepath and os.path.isfile(abs_texture_filepath): # find existing texture with this image if teximag_id_name in bpy.data.textures: # reuse existing image texture if possible postfix = 0 postfixed_tex = teximag_id_name while postfixed_tex in bpy.data.textures: if _path.repair_path(bpy.data.textures[postfixed_tex].image.filepath) == _path.repair_path(abs_texture_filepath): texture = bpy.data.textures[postfixed_tex] break postfix += 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) # if texture wasn't found create new one if not texture: texture = bpy.data.textures.new(teximag_id_name, 'IMAGE') image = None # reuse existing image if possible postfix = 0 postfixed_img = teximag_id_name while postfixed_img in bpy.data.images: if _path.repair_path(bpy.data.images[postfixed_img].filepath) == _path.repair_path(abs_texture_filepath): image = bpy.data.images[postfixed_img] break postfix += 1 postfixed_img = teximag_id_name + "." + str(postfix).zfill(3) # if image wasn't found load it if not image: image = bpy.data.images.load(abs_texture_filepath) image.name = teximag_id_name # try to get relative path to the Blender file and set it to the image if bpy.data.filepath != '': # empty file path means blender file is not saved try: rel_path = _path.relative_path(os.path.dirname(bpy.data.filepath), abs_texture_filepath) except ValueError: # catch different mount paths: "path is on mount 'C:', start on mount 'E:'" rel_path = None if rel_path: image.filepath = rel_path # finally link image to texture texture.image = image image.use_alpha = True # normal map related settings if texture_type == "nmap": texture.image.colorspace_settings.name = "Raw" texture.use_normal_map = True else: texture.image.colorspace_settings.name = "sRGB" texture.use_normal_map = False return texture
def invoke(self, context, event): """Invoke a file path selector.""" self.filepath = _path_utils.get_abs_path( _get_scs_globals().matsubs_library_rel_path) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def _get_texture_path_from_material(material, texture_type, export_path): """Get's relative path for Texture section of tobj from given texture_type. If tobj is not yet created it also creates tobj for it. :param material: Blender material :type material: bpy.types.Material :param texture_type: type of texture which should be readed from material (example "texture_base") :type texture_type: str :return: relative path for Texture section data of PIT material :rtype: str """ # overwrite tobj value directly if specified if getattr(material.scs_props, "shader_" + texture_type + "_use_imported", False): return getattr(material.scs_props, "shader_" + texture_type + "_imported_tobj", "") # use tobj value from shader preset if texture is locked and has default value if "scs_shader_attributes" in material and "textures" in material["scs_shader_attributes"]: for tex_entry in material["scs_shader_attributes"]["textures"].values(): if "Tag" in tex_entry and texture_type in tex_entry["Tag"]: if "Lock" in tex_entry and tex_entry["Lock"] == "True": if "Value" in tex_entry and tex_entry["Value"] != "": return tex_entry["Value"] # TEXTURE PATH texture_raw_path = getattr(material.scs_props, "shader_" + texture_type, "NO PATH") texture_abs_filepath = _path_utils.get_abs_path(texture_raw_path) # TOBJ PATH scs_project_path = _get_scs_globals().scs_project_path.rstrip("\\").rstrip("/") if os.path.isfile(scs_project_path + os.sep + texture_raw_path): # relative within base tobj_rel_filepath = os.path.splitext(texture_raw_path)[0][1:] tobj_abs_filepath = str(os.path.splitext(texture_abs_filepath)[0] + ".tobj") elif os.path.isfile(texture_raw_path): # absolute # if we are exporting somewhere into SCS Project Base Path texture still can be saved if scs_project_path != "" and export_path.startswith(scs_project_path): tex_dir, tex_filename = os.path.split(texture_raw_path) tobj_filename = tex_filename.rsplit(".", 1)[0] + ".tobj" # copy texture beside exported files shutil.copy2(texture_raw_path, os.path.join(export_path, tex_filename)) # copy also TOBJ if exists texture_raw_tobj_path = tex_dir + os.sep + tobj_filename if os.path.isfile(texture_raw_tobj_path): shutil.copy2(texture_raw_tobj_path, os.path.join(export_path, tobj_filename)) tobj_rel_filepath = "" if export_path != scs_project_path: tobj_rel_filepath = os.sep + os.path.relpath(export_path, scs_project_path) tobj_rel_filepath = tobj_rel_filepath + os.sep + tobj_filename[:-5] tobj_abs_filepath = os.path.join(export_path, tobj_filename) else: lprint("E Can not properly export texture %r from material %r!\n\t " + "Make sure you are exporting somewhere into Project Base Path and texture is properly set!", (texture_raw_path, material.name)) return "" else: lprint("E Texture file %r from material %r doesn't exists inside current Project Base Path, TOBJ can not be exported!", (texture_raw_path, material.name)) return "" # CREATE TOBJ FILE if not os.path.isfile(tobj_abs_filepath): # only if it does not exists yet # export tobj only if file of texture exists if os.path.isfile(texture_abs_filepath): settings = getattr(material.scs_props, "shader_" + texture_type + "_settings", set()) texture_name = os.path.basename(_path_utils.strip_sep(texture_raw_path)) _tobj.export(tobj_abs_filepath, texture_name, settings) else: lprint("E Texture file %r from material %r doesn't exists, TOBJ can not be exported!", (texture_raw_path, material.name)) # make sure that Windows users will export proper paths tobj_rel_filepath = tobj_rel_filepath.replace("\\", "/") return tobj_rel_filepath
def _draw_shader_texture(layout, mat, split_perc, texture, read_only): """Draws texture box with it's properties. :param layout: layout to draw attribute to :type layout: bpy.types.UILayout :param mat: material from which data should be displayed :type mat: bpy.types.Material :param split_perc: split percentage for attribute name/value :type split_perc: float :param texture: texture data :type texture: dict :param read_only: if texture should be read only :type read_only: bool """ tag = texture.get('Tag', None) hide_state = texture.get('Hide', None) tag_id = tag.split(':') tag_id_string = tag_id[1] texture_type = tag_id_string[8:] shader_texture_id = str('shader_' + tag_id_string) if hide_state == 'True': return texture_box = layout.box() header_split = texture_box.row().split(percentage=0.5) header_split.label(texture_type.title(), icon="TEXTURE_SHADED") if hasattr(mat.scs_props, shader_texture_id): shader_texture = mat.scs_props.get(shader_texture_id, "") # imported tobj boolean switch (only for imported shaders) use_imported_tobj = getattr(mat.scs_props, shader_texture_id + "_use_imported", False) if read_only: row = header_split.row(align=True) row.alignment = 'RIGHT' row.prop(mat.scs_props, shader_texture_id + "_use_imported") if use_imported_tobj: texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) item_space.label("TOBJ Path:") item_space = item_space.split(percentage=(1 - split_perc) * 0.2, align=True) props = item_space.operator("material.scs_looks_wt", text="WT") props.property_str = shader_texture_id item_space.prop(mat.scs_props, shader_texture_id + "_imported_tobj", text="") # disable whole texture layout if it's locked texture_box.enabled = not mat.scs_props.get( shader_texture_id + "_locked", False) texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) # in case of custom tobj value texture is used only for preview if read_only and use_imported_tobj: item_space.label("Preview Tex:") else: item_space.label("Texture:") layout_box_col = item_space.column(align=True) layout_box_row = layout_box_col.row(align=True) if shader_texture: texture_icon = 'TEXTURE' else: texture_icon = 'MATPLANE' # MARK INVALID SLOTS if _material_utils.is_valid_shader_texture_path(shader_texture): layout_box_row.alert = False else: layout_box_row.alert = True texture_icon = 'ERROR' # MARK EMPTY SLOTS if shader_texture == "": layout_box_row.alert = True layout_box_row = layout_box_row.split(percentage=(1 - split_perc) * 0.2, align=True) props = layout_box_row.operator("material.scs_looks_wt", text="WT") props.property_str = shader_texture_id layout_box_row = layout_box_row.row(align=True) layout_box_row.prop(mat.scs_props, shader_texture_id, text='', icon=texture_icon) props = layout_box_row.operator('scene.select_shader_texture_filepath', text='', icon='FILESEL') props.shader_texture = shader_texture_id # DYNAMIC ID SAVE (FOR FILE REQUESTER) # ADDITIONAL TEXTURE SETTINGS if (not read_only or (read_only and not use_imported_tobj)) and texture_box.enabled: tobj_exists = _material_utils.is_valid_shader_texture_path( shader_texture, True) if not tobj_exists: item_split = layout_box_col.split(percentage=(1 - split_perc) * 0.2, align=True) item_space = item_split.row(align=True) props = item_space.operator("material.scs_create_tobj", icon="NEW", text="") props.texture_type = texture_type item_space = item_split.row(align=True) else: item_space = layout_box_col.row(align=True) item_space.enabled = tobj_exists if tobj_exists: mtime = str( os.path.getmtime( _path_utils.get_abs_path(shader_texture[:-4] + ".tobj"))) item_space.alert = (mtime != getattr( mat.scs_props, shader_texture_id + "_tobj_load_time", "NOT FOUND")) else: item_space.alert = True props = item_space.operator("material.scs_reload_tobj", icon="LOAD_FACTORY", text="") props.texture_type = texture_type item_space.prop_menu_enum( mat.scs_props, str('shader_' + tag_id_string + '_settings'), icon='SETTINGS', ) # UV LAYERS FOR TEXTURE uv_mappings = getattr(mat.scs_props, "shader_" + tag_id_string + "_uv", None) if len(uv_mappings) > 0: texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) if read_only: item_space.label("Preview UV Map:") else: item_space.label("Mapping:") layout_box_col = item_space.column(align=True) for mapping in uv_mappings: item_space_row = layout_box_col.row(align=True) # add info about normal map uv mapping property in case of imported shader if read_only and tag_id_string == "texture_nmap": preview_nmap_msg = str( "Maping value for normal maps is in the case of imported shader\n" "also used for defining uv map layer for tangent calculations!\n" "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: icon = "ERROR" item_space_row.prop_search( data=mapping, property="value", search_data=bpy.context.active_object.data, search_property='uv_layers', text="", icon=icon, ) else: texture_box.row().label('Unsupported Shader Texture Type!', icon="ERROR")
def _draw_shader_texture(layout, mat, split_perc, texture, read_only): """Draws texture box with it's properties. :param layout: layout to draw attribute to :type layout: bpy.types.UILayout :param mat: material from which data should be displayed :type mat: bpy.types.Material :param split_perc: split percentage for attribute name/value :type split_perc: float :param texture: texture data :type texture: dict :param read_only: if texture should be read only :type read_only: bool """ tag = texture.get('Tag', None) hide_state = texture.get('Hide', None) tag_id = tag.split(':') tag_id_string = tag_id[1] texture_type = tag_id_string[8:] shader_texture_id = str('shader_' + tag_id_string) if hide_state == 'True': return texture_box = layout.box() header_split = texture_box.row().split(percentage=0.5) header_split.label(texture_type.title(), icon="TEXTURE_SHADED") if hasattr(mat.scs_props, shader_texture_id): shader_texture = mat.scs_props.get(shader_texture_id, "") # imported tobj boolean switch (only for imported shaders) use_imported_tobj = getattr(mat.scs_props, shader_texture_id + "_use_imported", False) if read_only: row = header_split.row(align=True) row.alignment = 'RIGHT' row.prop(mat.scs_props, shader_texture_id + "_use_imported") if use_imported_tobj: texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) item_space.label("TOBJ Path:") item_space = item_space.split(percentage=(1 - split_perc) * 0.2, align=True) props = item_space.operator("material.scs_looks_wt", text="WT") props.property_str = shader_texture_id item_space.prop(mat.scs_props, shader_texture_id + "_imported_tobj", text="") # disable whole texture layout if it's locked texture_box.enabled = not mat.scs_props.get(shader_texture_id + "_locked", False) texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) # in case of custom tobj value texture is used only for preview if read_only and use_imported_tobj: item_space.label("Preview Tex:") else: item_space.label("Texture:") layout_box_col = item_space.column(align=True) layout_box_row = layout_box_col.row(align=True) if shader_texture: texture_icon = 'TEXTURE' else: texture_icon = 'MATPLANE' # MARK INVALID SLOTS if _material_utils.is_valid_shader_texture_path(shader_texture): layout_box_row.alert = False else: layout_box_row.alert = True texture_icon = 'ERROR' # MARK EMPTY SLOTS if shader_texture == "": layout_box_row.alert = True layout_box_row = layout_box_row.split(percentage=(1 - split_perc) * 0.2, align=True) props = layout_box_row.operator("material.scs_looks_wt", text="WT") props.property_str = shader_texture_id layout_box_row = layout_box_row.row(align=True) layout_box_row.prop(mat.scs_props, shader_texture_id, text='', icon=texture_icon) props = layout_box_row.operator('scene.select_shader_texture_filepath', text='', icon='FILESEL') props.shader_texture = shader_texture_id # DYNAMIC ID SAVE (FOR FILE REQUESTER) # ADDITIONAL TEXTURE SETTINGS if (not read_only or (read_only and not use_imported_tobj)) and texture_box.enabled: tobj_exists = _material_utils.is_valid_shader_texture_path(shader_texture, True) if not tobj_exists: item_split = layout_box_col.split(percentage=(1 - split_perc) * 0.2, align=True) item_space = item_split.row(align=True) props = item_space.operator("material.scs_create_tobj", icon="NEW", text="") props.texture_type = texture_type item_space = item_split.row(align=True) else: item_space = layout_box_col.row(align=True) item_space.enabled = tobj_exists if tobj_exists: mtime = str(os.path.getmtime(_path_utils.get_abs_path(shader_texture[:-4] + ".tobj"))) item_space.alert = (mtime != getattr(mat.scs_props, shader_texture_id + "_tobj_load_time", "NOT FOUND")) else: item_space.alert = True props = item_space.operator("material.scs_reload_tobj", icon="LOAD_FACTORY", text="") props.texture_type = texture_type item_space.prop_menu_enum( mat.scs_props, str('shader_' + tag_id_string + '_settings'), icon='SETTINGS', ) # UV LAYERS FOR TEXTURE uv_mappings = getattr(mat.scs_props, "shader_" + tag_id_string + "_uv", None) if len(uv_mappings) > 0: texture_row = texture_box.row() item_space = texture_row.split(percentage=split_perc, align=True) if read_only: item_space.label("Preview UV Map:") else: item_space.label("Mapping:") layout_box_col = item_space.column(align=True) for mapping in uv_mappings: item_space_row = layout_box_col.row(align=True) # add info about normal map uv mapping property in case of imported shader if read_only and tag_id_string == "texture_nmap": item_space_row.operator("material.show_normal_maps_mapping_info", text="", 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: icon = "ERROR" item_space_row.prop_search( data=mapping, property="value", search_data=bpy.context.active_object.data, search_property='uv_layers', text="", icon=icon, ) else: texture_box.row().label('Unsupported Shader Texture Type!', icon="ERROR")
def load(locator): """Makes a preview model for a locator and link it to it NOTE: locator preview model path must be set :param locator: locator object to which preview model should be set :type locator: bpy.types.Object :return: True if preview model was set; False otherwise :rtype: bool """ load_model = True abs_filepath = "" if not locator.scs_props.locator_show_preview_model: load_model = False else: filepath = locator.scs_props.locator_preview_model_path if filepath: if filepath.lower().endswith(".pim"): abs_filepath = _path_utils.get_abs_path(filepath, skip_mod_check=True) if not os.path.isfile(abs_filepath): lprint( "W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, _path_utils.readable_norm(abs_filepath))) load_model = False else: lprint( "W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, _path_utils.readable_norm(filepath))) load_model = False else: load_model = False if load_model: unload(locator) prem_name = str("prem_" + locator.name) obj = _get_model_mesh(locator, prem_name) if not obj: from io_scs_tools.imp import pim as _pim_import obj = _pim_import.load_pim_file(bpy.context, abs_filepath, preview_model=True) # in case used preview model doesn't have any mesh, abort loading, report error and reset path # Path has to be reset to prevent loading preview model over and over again # from possible callbacks trying to fix not present preview model if not obj: message = "Selected PIM model doesn't have any mesh inside, so it can not be used as a preview model." bpy.ops.wm.show_warning_message( 'INVOKE_DEFAULT', is_modal=True, title="Preview Model Load Error!", message=message, width=500, height=100) lprint("E " + message) locator.scs_props.locator_preview_model_path = "" return False obj.name = prem_name obj.data.name = prem_name obj.data.scs_props.locator_preview_model_path = locator.scs_props.locator_preview_model_path obj.select = False link(locator, obj) return True else: return False
def load(locator): """Makes a preview model for a locator and link it to it NOTE: locator preview model path must be set :param locator: locator object to which preview model should be set :type locator: bpy.types.Object :return: True if preview model was set; False otherwise :rtype: bool """ load_model = True abs_filepath = "" if not locator.scs_props.locator_show_preview_model: load_model = False else: filepath = locator.scs_props.locator_preview_model_path if filepath: if filepath.lower().endswith(".pim"): abs_filepath = _path_utils.get_abs_path(filepath) if not os.path.isfile(abs_filepath): lprint( "W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, abs_filepath.replace("\\", "/"))) load_model = False else: lprint( "W Locator %r has invalid path to Preview Model PIM file: %r", (locator.name, filepath.replace("\\", "/"))) load_model = False else: load_model = False if load_model: unload(locator) prem_name = str("prem_" + locator.name) obj = _get_model_mesh(locator, prem_name) if not obj: from io_scs_tools.imp import pim as _pim_import obj = _pim_import.load_pim_file( bpy.context, abs_filepath, preview_model=True, looks_and_materials=False, textures=False, uv=False, vc=False, vg=False, weld_smoothed=False, bones=False, shadow_casters=False, create_locators=False, parts_and_variants=False, ) obj.name = prem_name obj.data.name = prem_name obj.data.scs_props.locator_preview_model_path = locator.scs_props.locator_preview_model_path obj.select = False link(locator, obj) return True else: return False
def invoke(self, context, event): """Invoke a file path selector.""" self.filepath = _path_utils.get_abs_path(_get_scs_globals().matsubs_library_rel_path) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
def update_texture_slots(material, texture_path, texture_type): """Creates and setup Texture and Image data on active Material. :param material: Blender material :type material: Material :param texture_path: Texture path :type texture_path: str :param texture_type: Texture type keyword :type texture_type: str """ # print(' texture_path: %s' % str(texture_path)) # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = str("scs_" + texture_type + "_" + os.path.splitext(os.path.basename(texture_path))[0]) # print(' teximag_id_name: %r' % str(teximag_id_name)) # PROVIDE A TEXTURE SLOT slot_found = None texture_slots = material.texture_slots # A. Check all Texture slots for existing Image Textures (OLD WAY) # NOTE: This method often results in arbitrary order of Image Textures # within Texture Slots. In such circumstances it is very hard to ensure # uniform way of Material appearance in 3D viewport, because of # different mixing order of Textures. # for texture_slot_i, texture_slot in enumerate(texture_slots): # B. Fixed slots for Textures... fixed_slots_dict = {'base': 5, 'reflection': 6, 'over': 7, 'oclu': 8, 'mask': 9, 'mult': 10, 'iamod': 11, 'lightmap': 12, 'paintjob': 13, 'flakenoise': 14, 'nmap': 15} if texture_type in fixed_slots_dict: texture_slot_i = fixed_slots_dict[texture_type] texture_slot = texture_slots[texture_slot_i] # print(' texture_slot[%.2i]: %s' % (texture_slot_i, str(texture_slot))) if texture_slot: # print(' texture_slot.name: %r' % str(texture_slot.name)) if texture_slot.name.startswith(str("scs_" + texture_type + "_")): if texture_slot.name == teximag_id_name: slot_found = texture_slot else: texture_slots.clear(texture_slot_i) # break # Needed for "A. Method" above... # CREATE ABSOLUT FILEPATH abs_texture_filepath = _path.get_abs_path(texture_path) # print(' Set texture base filepath: %r' % abs_texture_filepath) if abs_texture_filepath and os.path.isfile(abs_texture_filepath): # IF SLOT EXISTS, INSPECT IT FOR VALIDITY # NOTE: If Blend file contains Image links from another, # currently unexisting location, it is needed to correct these links. if slot_found: # print(' "SLOT_FOUND" - texture_path: %r' % str(texture_path)) # print(' "SLOT_FOUND" - abs_texture_filepath:\n\t%r' % str(abs_texture_filepath)) # print(' "SLOT_FOUND" - teximag_id_name: %r' % str(teximag_id_name)) # print(' "SLOT_FOUND" - texture_slot:\n\t%r' % str(texture_slot)) correct_image_filepath(texture_slot, abs_texture_filepath) return # CREATE/FIND NEW TEXTURE if teximag_id_name in bpy.data.textures: if os.path.abspath(bpy.data.textures[teximag_id_name].image.filepath) == abs_texture_filepath: # REUSE EXISTING IMAGE TEXTURE new_texture = bpy.data.textures[teximag_id_name] else: # also check all the duplicates postfix = 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) while postfixed_tex in bpy.data.textures: if os.path.abspath(bpy.data.textures[postfixed_tex].image.filepath) == abs_texture_filepath: # REUSE EXISTING IMAGE TEXTURE new_texture = bpy.data.textures[postfixed_tex] break postfix += 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) else: # CREATE NEW IMAGE TEXTURE new_texture = bpy.data.textures.new(teximag_id_name, 'IMAGE') else: # CREATE NEW IMAGE TEXTURE new_texture = bpy.data.textures.new(teximag_id_name, 'IMAGE') # print(' new_texture: %s' % str(new_texture)) # CREATE/FIND NEW IMAGE if teximag_id_name in bpy.data.images.keys() and os.path.abspath(bpy.data.images[teximag_id_name].filepath) == abs_texture_filepath: # REUSE EXISTING IMAGE new_image = bpy.data.images[teximag_id_name] else: # CREATE NEW IMAGE new_image = bpy.data.images.load(abs_texture_filepath) new_image.name = teximag_id_name # print(' new_image: %s' % str(new_image)) # LINK IMAGE TO IMAGE TEXTURE new_texture.image = new_image # CREATE AND SETUP NEW TEXTURE SLOT # for texture_slot_i, texture_slot in enumerate(texture_slots): # Needed for "A. Method" above... # print(' texture_slot[%.2i]: %s' % (texture_slot_i, str(texture_slot))) # if not texture_slot: new_texture_slot = texture_slots.create(texture_slot_i) # LINK IMAGE TEXTURE TO TEXTURE SLOT new_texture_slot.texture = new_texture # MAKE VISUAL SETTINGS new_texture_slot.color = (1.0, 1.0, 1.0) new_image.use_alpha = False texture_mappings = getattr(material.scs_props, "shader_texture_" + texture_type + "_uv") if texture_mappings and len(texture_mappings) > 0: new_texture_slot.uv_layer = texture_mappings[0].value if texture_type == 'base': new_texture_slot.texture_coords = 'UV' new_texture_slot.use_map_color_diffuse = True if material.scs_props.mat_effect_name.endswith(".a"): new_texture_slot.use_map_alpha = True new_image.use_alpha = True else: new_texture_slot.use_map_color_diffuse = False if texture_type == 'reflection': new_texture_slot.texture_coords = 'REFLECTION' new_texture_slot.use_map_emit = True if texture_type == "mult": new_texture_slot.use_map_color_diffuse = True new_texture_slot.blend_type = "MULTIPLY" if texture_type == 'nmap': new_texture_slot.texture_coords = 'UV' new_texture_slot.use_map_normal = True new_texture_slot.normal_map_space = 'TANGENT' new_texture.use_normal_map = True
def get_texture(texture_path, texture_type, report_invalid=False): """Creates and setup Texture and Image data on active Material. :param texture_path: Texture path :type texture_path: str :param texture_type: Texture type keyword :type texture_type: str :param report_invalid: flag indicating if invalid texture should be reported in 3d view :type report_invalid: bool """ # CREATE TEXTURE/IMAGE ID NAME teximag_id_name = _path.get_filename(texture_path, with_ext=False) # CREATE ABSOLUTE FILEPATH abs_texture_filepath = _path.get_abs_path(texture_path) # return None on non-existing texture file path if not abs_texture_filepath or not os.path.isfile(abs_texture_filepath): return None if abs_texture_filepath.endswith(".tobj"): abs_texture_filepath = _path.get_texture_path_from_tobj(abs_texture_filepath) # if not existing or none supported file if abs_texture_filepath is None or abs_texture_filepath[-4:] not in (".tga", ".png", ".dds"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) # take care of none existing paths referenced in tobj texture names if abs_texture_filepath: lprint("W Texture can't be displayed as TOBJ file: %r is referencing non texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) else: lprint("W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file.", (texture_path,)) if report_invalid: lprint("", report_warnings=1, report_errors=1) return None texture = None if abs_texture_filepath and os.path.isfile(abs_texture_filepath): # find existing texture with this image if teximag_id_name in bpy.data.textures: # reuse existing image texture if possible postfix = 0 postfixed_tex = teximag_id_name while postfixed_tex in bpy.data.textures: img_exists = bpy.data.textures[postfixed_tex].image is not None if img_exists and _path.repair_path(bpy.data.textures[postfixed_tex].image.filepath) == _path.repair_path(abs_texture_filepath): texture = bpy.data.textures[postfixed_tex] break postfix += 1 postfixed_tex = teximag_id_name + "." + str(postfix).zfill(3) # if texture wasn't found create new one if not texture: texture = bpy.data.textures.new(teximag_id_name, 'IMAGE') image = None # reuse existing image if possible postfix = 0 postfixed_img = teximag_id_name while postfixed_img in bpy.data.images: if _path.repair_path(bpy.data.images[postfixed_img].filepath) == _path.repair_path(abs_texture_filepath): image = bpy.data.images[postfixed_img] break postfix += 1 postfixed_img = teximag_id_name + "." + str(postfix).zfill(3) # if image wasn't found load it if not image: image = bpy.data.images.load(abs_texture_filepath) image.name = teximag_id_name # try to get relative path to the Blender file and set it to the image if bpy.data.filepath != '': # empty file path means blender file is not saved try: rel_path = _path.relative_path(os.path.dirname(bpy.data.filepath), abs_texture_filepath) except ValueError: # catch different mount paths: "path is on mount 'C:', start on mount 'E:'" rel_path = None if rel_path: image.filepath = rel_path # finally link image to texture texture.image = image image.use_alpha = True # 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 # otherwise Blender completely messes up normals calculation if texture.image.filepath.endswith(".tga"): texture.image.colorspace_settings.name = "Non-Color" else: texture.image.colorspace_settings.name = "sRGB" else: texture.image.colorspace_settings.name = "sRGB" # set usage of normal map if texture type is correct texture.use_normal_map = (texture_type == "nmap") if texture is None and texture_path.endswith(".tobj"): if report_invalid: lprint("", report_warnings=-1, report_errors=-1) lprint("W Texture can't be displayed as TOBJ file: %r is referencing non existing texture file:\n\t %r", (texture_path, _path.readable_norm(abs_texture_filepath))) if report_invalid: lprint("", report_warnings=1, report_errors=1) return texture
def update_hookup_library_rel_path(scs_hookup_inventory, hookup_library_rel_path, readonly=False): """The function deletes and populates again a list of Hookup names in inventory. It also updates corresponding record in config file. :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] else: hookup_file.model = item.props['model'] if 'brand_idx' in item.props: try: hookup_file.brand_idx = int( item.props['brand_idx']) except: pass if 'dir_type' in item.props: hookup_file.dir_type = item.props[ 'dir_type'] 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)