def set_shader_data_to_material(material, section, 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: blender material to which section data should be set :type material: bpy.types.Material :param section: new material data presented with Material section of PIX files :type section: io_scs_tools.internals.structure.SectionData :param is_import: flag indication if shader data are set from import process :type is_import: bool :param override_back_data: flag indication if back data for UI shall be overwritten :type override_back_data: bool """ preset_effect = section.get_prop_value("Effect") # check shader flags for attr in section.props: if attr[0] == "Flags": material.scs_props.enable_aliasing = not attr[1] == 1 # Flags: 1 #DISABLE ALIASING break 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 scs_props_keys = material.scs_props.keys() # if overriding back data also make sure to clear attribute values for current shader # to prevent storing of unused values from blend data block # NOTE: looks also takes into account that all the unused properties are omitted from scs_props dict if override_back_data: 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] # acquire old attribute value if exists and not importing old_value = None if "shader_attribute_" + attribute_type in scs_props_keys and not is_import: old_value = getattr(material.scs_props, "shader_attribute_" + attribute_type) if attribute_type in ("diffuse", "specular", "env_factor", "fresnel", "tint"): if not old_value: 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"): if not old_value: 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) # NOTE : invalidate old value if size of existing auxiliary property is different # then size of new one overwrite it anyway, because otherwise we will might access # values that doesn't exists but they should, for example: # switching from "eut2.dif.spec.weight.mult2" to "eut2.dif.spec.weight.mult2.weight2" if len(auxiliary_prop) != len(attribute_data['Value']): old_value = None if not old_value: # 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": if not old_value: 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" # collect old uv mappings per tex_coord values and delete them from material (they will be set back later) # NOTE: we have to create extra iteration for uv mappings collecting # as multiple texture types can use same tex coord and we have to make sure # to collect them before we apply any texture data back to material. old_texture_mappings = {} for tex_type in used_texture_types: texture_mappings = getattr(material.scs_props, "shader_texture_" + tex_type + "_uv") if override_back_data: while len(texture_mappings) > 0: # save only set uv mapping tex_cord:value pairs if texture_mappings[0].value != "": old_texture_mappings[texture_mappings[0].tex_coord] = texture_mappings[0].value texture_mappings.remove(0) # apply used textures created_textures = {} created_tex_mappings = [] for tex_type in used_texture_types: # skip unknown texture type if tex_type not in material.scs_props.get_texture_types().keys(): lprint("D Trying to apply unknown texture type to SCS material: %r", (tex_type,)) continue texture_data = used_texture_types[tex_type] # always set texture lock attribute # important to set it always as some texture might get unlocked in preset and # selecting same present again has to unlock that texture texture_locked = False if "Lock" in texture_data: texture_locked = bool(texture_data["Lock"]) setattr(material.scs_props, "shader_texture_" + tex_type + "_locked", texture_locked) texture_mappings = getattr(material.scs_props, "shader_texture_" + tex_type + "_uv") # 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_i, tex_coord in enumerate(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 # apply uv mappings either from imported data or from old mappings of previous shader if "scs_tex_aliases" in material: # scs_tex_aliases are present only on import mapping['value'] = material["scs_tex_aliases"][str(tex_coord)] created_tex_mappings.append((tex_type, mapping.value, tex_coord)) elif tex_coord in old_texture_mappings: mapping['value'] = old_texture_mappings[tex_coord] created_tex_mappings.append((tex_type, mapping.value, tex_coord)) # set texture file to current texture scs_texture_str = _path.get_scs_texture_str(texture_data['Value']) # apply texture path if not empty and not yet set, 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 (scs_texture_str != "" and getattr(material.scs_props, "shader_texture_" + tex_type, "") == "") or is_import: material.scs_props["shader_texture_" + tex_type] = scs_texture_str created_textures[tex_type] = get_texture(scs_texture_str, 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: final_tex_str = getattr(material.scs_props, "shader_texture_" + tex_type, "") created_textures[tex_type] = get_texture(final_tex_str, tex_type) if is_import and not override_back_data: if created_textures[tex_type] is None: lprint("E Can't find texture nor TOBJ inside SCS Project Base Path: %r", (final_tex_str,)) # 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, override_back_data) # setup uv mappings to nodes later trough dedicated function, so proper validation is made on tex coord bindings for mapping_data in created_tex_mappings: # data[0] = texture type; # data[1] = uv mapping value; # data[2] = tex coord value _shader.set_uv(material, mapping_data[0], mapping_data[1], mapping_data[2])
def set_shader_data_to_material(material, section, 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: blender material to which section data should be set :type material: bpy.types.Material :param section: new material data presented with Material section of PIX files :type section: io_scs_tools.internals.structure.SectionData :param is_import: flag indication if shader data are set from import process :type is_import: bool :param override_back_data: flag indication if back data for UI shall be overwritten :type override_back_data: bool """ preset_effect = section.get_prop_value("Effect") # check shader flags for attr in section.props: if attr[0] == "Flags": material.scs_props.enable_aliasing = not attr[ 1] == 1 # Flags: 1 #DISABLE ALIASING break 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 scs_props_keys = material.scs_props.keys() # if overriding back data also make sure to clear attribute values for current shader # to prevent storing of unused values from blend data block # NOTE: looks also takes into account that all the unused properties are omitted from scs_props dict if override_back_data: 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] # acquire old attribute value if exists and not importing old_value = None if "shader_attribute_" + attribute_type in scs_props_keys and not is_import: old_value = getattr(material.scs_props, "shader_attribute_" + attribute_type) if attribute_type in ("diffuse", "specular", "env_factor", "fresnel", "tint"): if not old_value: 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"): if not old_value: 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) # NOTE : invalidate old value if size of existing auxiliary property is different # then size of new one overwrite it anyway, because otherwise we will might access # values that doesn't exists but they should, for example: # switching from "eut2.dif.spec.weight.mult2" to "eut2.dif.spec.weight.mult2.weight2" if len(auxiliary_prop) != len(attribute_data['Value']): old_value = None if not old_value: # 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": if not old_value: 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" # collect old uv mappings per tex_coord values and delete them from material (they will be set back later) # NOTE: we have to create extra iteration for uv mappings collecting # as multiple texture types can use same tex coord and we have to make sure # to collect them before we apply any texture data back to material. old_texture_mappings = {} for tex_type in used_texture_types: texture_mappings = getattr(material.scs_props, "shader_texture_" + tex_type + "_uv") if override_back_data: while len(texture_mappings) > 0: # save only set uv mapping tex_cord:value pairs if texture_mappings[0].value != "": old_texture_mappings[texture_mappings[0]. tex_coord] = texture_mappings[0].value texture_mappings.remove(0) # apply used textures created_textures = {} created_tex_mappings = [] for tex_type in used_texture_types: # skip unknown texture type if tex_type not in material.scs_props.get_texture_types().keys(): lprint( "D Trying to apply unknown texture type to SCS material: %r", (tex_type, )) continue texture_data = used_texture_types[tex_type] # always set texture lock attribute # important to set it always as some texture might get unlocked in preset and # selecting same present again has to unlock that texture texture_locked = False if "Lock" in texture_data: texture_locked = bool(texture_data["Lock"]) setattr(material.scs_props, "shader_texture_" + tex_type + "_locked", texture_locked) texture_mappings = getattr(material.scs_props, "shader_texture_" + tex_type + "_uv") # 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_i, tex_coord in enumerate(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 # apply uv mappings either from imported data or from old mappings of previous shader if "scs_tex_aliases" in material: # scs_tex_aliases are present only on import mapping['value'] = material["scs_tex_aliases"][str( tex_coord)] created_tex_mappings.append( (tex_type, mapping.value, tex_coord)) elif tex_coord in old_texture_mappings: mapping['value'] = old_texture_mappings[tex_coord] created_tex_mappings.append( (tex_type, mapping.value, tex_coord)) # set texture file to current texture scs_texture_str = _path.get_scs_texture_str(texture_data['Value']) # apply texture path if not empty and not yet set, 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 (scs_texture_str != "" and getattr(material.scs_props, "shader_texture_" + tex_type, "") == "") or is_import: material.scs_props["shader_texture_" + tex_type] = scs_texture_str created_textures[tex_type] = get_texture(scs_texture_str, 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: final_tex_str = getattr(material.scs_props, "shader_texture_" + tex_type, "") created_textures[tex_type] = get_texture(final_tex_str, tex_type) if is_import and not override_back_data: if created_textures[tex_type] is None: lprint( "E Can't find texture nor TOBJ inside SCS Project Base Path: %r", (final_tex_str, )) # 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, override_back_data) # setup uv mappings to nodes later trough dedicated function, so proper validation is made on tex coord bindings for mapping_data in created_tex_mappings: # data[0] = texture type; # data[1] = uv mapping value; # data[2] = tex coord value _shader.set_uv(material, mapping_data[0], mapping_data[1], mapping_data[2])