예제 #1
0
    def build_provider_based_on_config(config):
        """ Builds up the provider using the parameters described in the given config.

        The given config should follow the following scheme:

        .. code-block:: yaml

            {
              "provider": "<name of provider class>"
              "parameters": {
                <provider parameters>
              }
            }

        :param config: A Configuration object or a dict containing the configuration data.
        :return: The constructed provider.
        """
        if isinstance(config, dict):
            config = Config(config)

        parameters = {}
        for key in config.data.keys():
            if key != 'provider':
                parameters[key] = config.data[key]

        if not config.has_param('provider'):
            raise Exception("Each provider needs a provider label, this one does not contain one: {}".format(config.data))

        return Utility.build_provider(config.get_string("provider"), parameters)
예제 #2
0
    def _get_the_set_params(self, params_conf):
        """ Extracts actual values to set from a Config object.

        :param params_conf: Object with all user-defined data. Type: Config.
        :return: Parameters to set as {name of the parameter: it's value} pairs. Type: dict.
        """
        params = {}
        for key in params_conf.data.keys():
            result = None
            if key == "color_link_to_displacement":
                result = params_conf.get_float(key)
            elif key == "change_to_vertex_color":
                result = params_conf.get_string(key)
            elif key == "textures":
                result = {}
                paths_conf = Config(params_conf.get_raw_dict(key))
                for text_key in paths_conf.data.keys():
                    text_path = paths_conf.get_string(text_key)
                    result.update({text_key: text_path})
            else:
                result = params_conf.get_raw_value(key)

            params.update({key: result})

        return params
    def run(self):
        """ Loads rocks."""

        rocks_settings = self.config.get_list("batches", [])
        for subsec_num, subsec_settings in enumerate(rocks_settings):
            subsec_config = Config(subsec_settings)

            subsec_objects = RockEssentialsRockLoader.load_rocks(
                path=subsec_config.get_string("path"),
                subsec_num=subsec_num,
                objects=subsec_config.get_list("objects", []),
                sample_objects=subsec_config.get_bool("sample_objects", False),
                amount=subsec_config.get_int("amount") if subsec_config.has_param("amount") else None
            )

            RockEssentialsRockLoader.set_rocks_properties(
                objects=subsec_objects,
                physics=subsec_config.get_bool("physics", False),
                render_levels=subsec_config.get_int("render_levels", 3),
                high_detail_mode=subsec_config.get_bool("high_detail_mode", False),
                scale=subsec_config.get_vector3d("scale", [1, 1, 1]),
                reflection_amount=subsec_config.get_float("reflection_amount") if subsec_config.has_param("reflection_amount") else None,
                reflection_roughness=subsec_config.get_float("reflection_roughness") if subsec_config.has_param("reflection_roughness") else None,
                hsv=subsec_config.get_list("HSV") if subsec_config.has_param("HSV") else None
            )
예제 #4
0
    def run(self):
        """ Adds specified basic empty objects to the scene and sets at least their names to the user-defined ones.
            1. Get configuration parameters' values.
            2. Add an object.
            3. Set attribute values.
        """
        empties_to_add = self.config.get_list("empties_to_add")
        for empty in empties_to_add:
            empty_conf = Config(empty)
            obj_name = empty_conf.get_string("name")
            obj_type = empty_conf.get_string("type", "plain_axes")

            entity = Entity.create_empty(obj_name, obj_type)
            entity.set_location(empty_conf.get_vector3d("location", [0, 0, 0]))
            entity.set_rotation_euler(
                empty_conf.get_vector3d("rotation", [0, 0, 0]))
            entity.set_scale(empty_conf.get_vector3d("scale", [1, 1, 1]))
예제 #5
0
    def _get_the_set_params(self, params_conf: Config) -> dict:
        """ Extracts actual values to set from a Config object.

        :param params_conf: Object with all user-defined data.
        :return: Parameters to set as {name of the parameter: it's value} pairs.
        """
        params = {}
        for key in params_conf.data.keys():
            result = None
            if key == "cf_color_link_to_displacement":
                result = params_conf.get_float(key)
            elif key == "cf_change_to_vertex_color":
                result = params_conf.get_string(key)
            elif key == "cf_textures":
                result = {}
                paths_conf = Config(params_conf.get_raw_dict(key))
                for text_key in paths_conf.data.keys():
                    text_path = paths_conf.get_string(text_key)
                    result.update({text_key: text_path})
            elif key == "cf_switch_to_emission_shader":
                result = {}
                emission_conf = Config(params_conf.get_raw_dict(key))
                for emission_key in emission_conf.data.keys():
                    if emission_key == "color":
                        attr_val = emission_conf.get_list(
                            "color", [1, 1, 1, 1])
                    elif emission_key == "strength":
                        attr_val = emission_conf.get_float("strength", 1.0)
                    else:
                        attr_val = emission_conf.get_raw_value(emission_key)

                    result.update({emission_key: attr_val})
            elif key == "cf_infuse_texture":
                result = Config(params_conf.get_raw_dict(key))
            elif key == "cf_infuse_material":
                result = Config(params_conf.get_raw_dict(key))
            elif key == "cf_add_dust":
                result = params_conf.get_raw_dict(key)
            elif "cf_set_" in key or "cf_add_" in key:
                result = params_conf.get_raw_value(key)
            else:
                result = params_conf.get_raw_value(key)

            params.update({key: result})

        return params
예제 #6
0
    def _cam2world_matrix_from_cam_extrinsics(self,
                                              config: Config) -> np.ndarray:
        """ Determines camera extrinsics by using the given config and returns them in form of a cam to world frame transformation matrix.

        :param config: The configuration object.
        :return: The 4x4 cam to world transformation matrix.
        """
        if not config.has_param("cam2world_matrix"):
            position = MathUtility.change_coordinate_frame_of_point(
                config.get_vector3d("location", [0, 0, 0]), self.source_frame)
            # position = Vector((-0.01111459918320179, -0.051188092678785324, 0.19301876425743103))

            rotation_format = config.get_string("rotation/format", "euler")
            value = config.get_vector3d("rotation/value", [0, 0, 0])
            # Transform to blender coord frame
            value = MathUtility.change_coordinate_frame_of_point(
                value, self.source_frame)
            if rotation_format == "euler":
                # Rotation, specified as euler angles
                rotation_matrix = Euler(value, 'XYZ').to_matrix()
            elif rotation_format == "forward_vec":
                # Convert forward vector to euler angle (Assume Up = Z)
                rotation_matrix = CameraUtility.rotation_from_forward_vec(
                    value)
            elif rotation_format == "look_at":
                # Convert forward vector to euler angle (Assume Up = Z)
                rotation_matrix = CameraUtility.rotation_from_forward_vec(
                    value - position)
            else:
                raise Exception("No such rotation format:" +
                                str(rotation_format))

            if rotation_format == "look_at" or rotation_format == "forward_vec":
                inplane_rot = config.get_float("rotation/inplane_rot", 0.0)
                rotation_matrix = np.matmul(
                    rotation_matrix,
                    Euler((0.0, 0.0, inplane_rot)).to_matrix())

                extra_rot = config.get_vector("rotation/extra_rot",
                                              mathutils.Vector([0., 0., 0.]))
                #extra_rot = Vector([0.3,-0.3,-0.7841])
                rotation_matrix = rotation_matrix @ Euler(
                    extra_rot).to_matrix()

            # cam2world_matrix = Matrix.Translation(Vector(position)) @ rotation_matrix.to_4x4()

            cam2world_matrix = MathUtility.build_transformation_mat(
                position, rotation_matrix)

        else:
            cam2world_matrix = np.array(
                config.get_list("cam2world_matrix")).reshape(4, 4).astype(
                    np.float32)
            cam2world_matrix = MathUtility.change_target_coordinate_frame_of_transformation_matrix(
                cam2world_matrix, self.source_frame)
        return cam2world_matrix
예제 #7
0
    def _apply_function(self, entity, key, result):
        """ Applies a custom function to the selected entity.

        :param entity: Entity to be modified via the application of the custom function. Type: bpy.types.Object.
        :param key: Name of the custom function. Type: string.
        :param result: Configuration parameters of the custom function. Type: dict.
        """
        if key == "add_modifier":
            result = Config(result)
            name = result.get_string("name")  # the name of the modifier
            if name.upper() == "SOLIDIFY":
                thickness = result.get_float("thickness")
                bpy.context.view_layer.objects.active = entity
                bpy.ops.object.modifier_add(type=name.upper())
                bpy.context.object.modifiers["Solidify"].thickness = thickness
            else:
                raise Exception("Unknown modifier name: {}.".format(name))
        elif key == "set_shading":
            result = Config(result)
            mode = result.get_string("cf_set_shaing")
            Loader.change_shading_mode([entity], mode)
        else:
            raise Exception("Unknown custom function name: {}.".format(key))
예제 #8
0
 def run(self):
     """ Adds specified basic mesh objects to the scene and sets at least their names to the user-defined ones.
         1. Get configuration parameters' values.
         2. Add an object.
         3. Set attribute values.
         4. Initialize a material, if needed.
     """
     meshes_to_add = self.config.get_list("meshes_to_add")
     init_objs_mats = self.config.get_bool("init_materials", True)
     for mesh in meshes_to_add:
         mesh_conf = Config(mesh)
         obj_type = mesh_conf.get_string("type")
         obj_name = mesh_conf.get_string("name")
         obj_location = mesh_conf.get_vector3d("location", [0, 0, 0])
         obj_rotation = mesh_conf.get_vector3d("rotation", [0, 0, 0])
         obj_scale = mesh_conf.get_vector3d("scale", [1, 1, 1])
         new_obj = self._add_obj(obj_type)
         self._set_attrs(new_obj, obj_name, obj_location, obj_rotation,
                         obj_scale)
         if init_objs_mats:
             self._init_material(obj_name)
예제 #9
0
    def build_provider_based_on_config(config):
        """ Builds up the provider using the parameters described in the given config.

        The given config should follow the following scheme:

        {
          "name": "<name of provider class>"
          "parameters": {
            <provider parameters>
          }
        }

        :param config: A Configuration object or a dict containing the configuration data.
        :return: The constructed provider.
        """
        if isinstance(config, dict):
            config = Config(config)

        parameters = {}
        for key in config.data.keys():
            if key != 'name':
                parameters[key] = config.data[key]

        return Utility.build_provider(config.get_string("name"), parameters)
예제 #10
0
    def _infuse_material(material: Material, config: Config):
        """
        Infuse a material inside of another material. The given material, will be adapted and the used material, will
        be added, depending on the mode either as add or as mix. This change is applied to all outputs of the material,
        this include the Surface (Color) and also the displacement and volume. For displacement mix means multiply.

        :param material: Used material
        :param config: Used config
        """
        # determine the mode
        used_mode = config.get_string("mode", "mix").lower()
        if used_mode not in ["add", "mix"]:
            raise Exception(f'This mode is unknown here: {used_mode}, only ["mix", "add"]!')

        mix_strength = 0.0
        if used_mode == "mix":
            mix_strength = config.get_float("mix_strength", 0.5)
        elif used_mode == "add" and config.has_param("mix_strength"):
            raise Exception("The mix_strength only works in the mix mode not in the add mode!")

        # get the material, which will be used to infuse the given material
        used_materials = config.get_list("used_material")
        if len(used_materials) == 0:
            raise Exception(f"You have to select a material, which is {used_mode}ed over the material!")

        used_material = random.choice(used_materials)

        # move the copied material inside of a group
        group_node = material.new_node("ShaderNodeGroup")
        group = BlenderUtility.add_nodes_to_group(used_material.node_tree.nodes,
                                                  f"{used_mode.title()}_{used_material.name}")
        group_node.node_tree = group
        # get the current material output and put the used material in between the last node and the material output
        material_output = material.get_the_one_node_with_type("OutputMaterial")
        for mat_output_input in material_output.inputs:
            if len(mat_output_input.links) > 0:
                if "Float" in mat_output_input.bl_idname or "Vector" in mat_output_input.bl_idname:
                    # For displacement
                    infuse_node = material.new_node("ShaderNodeMixRGB")
                    if used_mode == "mix":
                        # as there is no mix mode, we use multiply here, which is similar
                        infuse_node.blend_type = "MULTIPLY"
                        infuse_node.inputs["Fac"].default_value = mix_strength
                        input_offset = 1
                    elif used_mode == "add":
                        infuse_node.blend_type = "ADD"
                        input_offset = 0
                    else:
                        raise Exception(f"This mode is not supported here: {used_mode}!")
                    infuse_output = infuse_node.outputs["Color"]
                else:
                    # for the normal surface output (Color)
                    if used_mode == "mix":
                        infuse_node = material.new_node('ShaderNodeMixShader')
                        infuse_node.inputs[0].default_value = mix_strength
                        input_offset = 1
                    elif used_mode == "add":
                        infuse_node = material.new_node('ShaderNodeMixShader')
                        input_offset = 0
                    else:
                        raise Exception(f"This mode is not supported here: {used_mode}!")
                    infuse_output = infuse_node.outputs["Shader"]

                # link the infuse node with the correct group node and the material output
                for link in mat_output_input.links:
                    material.link(link.from_socket, infuse_node.inputs[input_offset])
                material.link(group_node.outputs[mat_output_input.name], infuse_node.inputs[input_offset + 1])
                material.link(infuse_output, mat_output_input)
예제 #11
0
    def _infuse_texture(material: Material, config: Config):
        """
        Overlays the selected material with a texture, this can be either a color texture like for example dirt or
        it can be a texture, which is used as an input to the Principled BSDF of the given material.

        :param material: Material, which will be changed
        :param config: containing the config information
        """
        used_mode = config.get_string("mode", "overlay").lower()
        if used_mode not in ["overlay", "mix", "set"]:
            raise Exception(f'This mode is unknown here: {used_mode}, only ["overlay", "mix", "set"]!')

        used_textures = config.get_list("used_texture")
        if len(used_textures) == 0:
            raise Exception(f"You have to select a texture, which is {used_mode} over the material!")

        invert_texture = config.get_bool("invert_texture", False)

        used_texture = random.choice(used_textures)
        used_connector = config.get_string("connection", "Base Color").title()
        texture_scale = config.get_float("texture_scale", 0.05)

        if config.has_param("strength") and used_mode == "set":
            raise Exception("The strength can only be used if the mode is not \"set\"!")
        strength = config.get_float("strength", 0.5)

        principled_bsdfs = material.get_nodes_with_type("BsdfPrincipled")
        if len(principled_bsdfs) != 1:
            raise Exception("This only works with materials, which have exactly one Prinicpled BSDF, "
                            "use a different selector!")
        principled_bsdf = principled_bsdfs[0]
        if used_connector not in principled_bsdf.inputs:
            raise Exception(f"The {used_connector} not an input to Principled BSDF!")

        node_connected_to_the_connector = None
        for link in material.links:
            if link.to_socket == principled_bsdf.inputs[used_connector]:
                node_connected_to_the_connector = link.from_node
                # remove this connection
                material.links.remove(link)
        if node_connected_to_the_connector is not None or used_mode == "set":
            texture_node = material.new_node("ShaderNodeTexImage")
            texture_node.image = used_texture.image
            # add texture coords to make the scaling of the dust texture possible
            texture_coords = material.new_node("ShaderNodeTexCoord")
            mapping_node = material.new_node("ShaderNodeMapping")
            mapping_node.vector_type = "TEXTURE"
            mapping_node.inputs["Scale"].default_value = [texture_scale] * 3
            material.link(texture_coords.outputs["UV"], mapping_node.inputs["Vector"])
            material.link(mapping_node.outputs["Vector"], texture_node.inputs["Vector"])
            texture_node_output = texture_node.outputs["Color"]
            if invert_texture:
                invert_node = material.new_node("ShaderNodeInvert")
                invert_node.inputs["Fac"].default_value = 1.0
                material.link(texture_node_output, invert_node.inputs["Color"])
                texture_node_output = invert_node.outputs["Color"]
            if node_connected_to_the_connector is not None and used_mode != "set":
                mix_node = material.new_node("ShaderNodeMixRGB")
                if used_mode in "mix_node":
                    mix_node.blend_type = "OVERLAY"
                elif used_mode in "mix":
                    mix_node.blend_type = "MIX"
                mix_node.inputs["Fac"].default_value = strength
                material.link(texture_node_output, mix_node.inputs["Color2"])
                # hopefully 0 is the color node!
                material.link(node_connected_to_the_connector.outputs[0], mix_node.inputs["Color1"])
                material.link(mix_node.outputs["Color"], principled_bsdf.inputs[used_connector])
            elif used_mode == "set":
                material.link(texture_node_output, principled_bsdf.inputs[used_connector])