Ejemplo n.º 1
0
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])
Ejemplo n.º 2
0
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])