コード例 #1
0
    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 = self._load_rocks(subsec_num, subsec_config)
            self._set_rocks_properties(subsec_objects, subsec_config)
コード例 #2
0
    def run(self):
        # Set intrinsics
        self._set_cam_intrinsics(bpy.context.scene.camera.data, Config(self.config.get_raw_dict("intrinsics", {})))

        self.cam_pose_collection.add_items_from_dicts(self.config.get_list("cam_poses", []))
        self.cam_pose_collection.add_items_from_file(self.config.get_string("path", ""),
                                                     self.config.get_string("file_format", ""),
                                                     self.number_of_arguments_per_parameter)
コード例 #3
0
    def run(self):
        """ Constructs ground plane. """

        tiles = self.config.get_list("tiles", [])
        for tile in tiles:
            if tile:
                ground_config = Config(tile)
                self._load_shader(ground_config)
                self._construct_ground_plane(ground_config)
コード例 #4
0
    def __init__(self, config):
        Module.__init__(self, config)

        # setting up the GlobalStorage
        global_config = Config(self.config.get_raw_dict("global", {}))
        GlobalStorage.init_global(global_config)

        # call the init again to make sure all values from the global config where read correctly, too
        self._default_init()
コード例 #5
0
    def initialize_modules(module_configs, global_config):
        """ Initializes the modules described in the given configuration.

        Example for module_configs:
        [{
          "module": "base.ModuleA",
          "config": {...}
        }, ...]

        Here the name contains the path to the module class, starting from inside the src directory.

        Example for global_config:
        {"base": {
            param: 42
        }}

        In this way all modules with prefix "base" will inherit "param" into their configuration.
        Local config always overwrites global.
        Parameters specified under "all" in the global config are inherited by all modules.

        :param module_configs: A list of dicts, each one describing one module.
        :param global_config: A dict containing the global configuration.
        :return:
        """
        modules = []
        all_base_config = global_config[
            "all"] if "all" in global_config else {}

        for module_config in module_configs:
            # If only the module name is given (short notation)
            if isinstance(module_config, str):
                module_config = {"module": module_config}

            # Merge global and local config (local overwrites global)
            model_type = module_config["module"].split(".")[0]
            base_config = global_config[
                model_type] if model_type in global_config else {}

            # Initialize config with all_base_config
            config = deepcopy(all_base_config)
            # Overwrite with module type base config
            Utility.merge_dicts(base_config, config)
            # Check if there is a module specific config
            if "config" in module_config:
                # Overwrite with module specific config
                Utility.merge_dicts(module_config["config"], config)

            with Utility.BlockStopWatch("Initializing module " +
                                        module_config["module"]):
                # Import file and extract class
                module_class = getattr(
                    importlib.import_module("src." + module_config["module"]),
                    module_config["module"].split(".")[-1])
                # Create module
                modules.append(module_class(Config(config)))

        return modules
コード例 #6
0
    def run(self):
        """ Collect all mesh objects and writes their id, name and pose."""

        if ('selector' in self.config.data.keys()):
            sel_objs = {}

            sel_objs['selector'] = self.config.data['selector']
            # create Config objects
            sel_conf = Config(sel_objs)
            # invoke a Getter, get a list of entities to manipulate
            objects = sel_conf.get_list("selector")
        else:
            objects = []
            for object in get_all_blender_mesh_objects():
                objects.append(object)

        self.write_attributes_to_file(
            self.object_writer, objects, "object_states_", "object_states",
            ["name", "location", "rotation_euler", "matrix_world"])
コード例 #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))
        else:
            raise Exception("Unknown custom function name: {}.".format(key))
コード例 #8
0
    def _get_random_texture(self, textures):
        """ Chooses a random texture data from the provided list.

        :param textures: Texture data. Type: list.
        :return: Selected texture data. Type: Config.
        """
        selected_dict = choice(textures)
        selected_texture = Config(selected_dict)

        return selected_texture
コード例 #9
0
    def run(self):
        set_params = {}
        sel_objs = {}
        for key in self.config.data.keys():
            # if its not a selector -> to the set parameters dict
            if key != 'selector':
                set_params[key] = self.config.data[key]
            else:
                sel_objs[key] = self.config.data[key]
        # create Config objects
        params_conf = Config(set_params)
        sel_conf = Config(sel_objs)
        # invoke a Getter, get a list of entities to manipulate
        materials = sel_conf.get_list("selector")

        op_mode = self.config.get_string("mode", "once_for_each")

        if op_mode == "once_for_all":
            # get values to set if they are to be set/sampled once for all selected materials
            params = self._get_the_set_params(params_conf)

        for material in materials:
            if not material.use_nodes:
                raise Exception(
                    "This material does not use nodes -> not supported here.")

            if op_mode == "once_for_each":
                # get values to set if they are to be set/sampled anew for each selected entity
                params = self._get_the_set_params(params_conf)

            for key, value in params.iteritems():
                # if an attribute with such name exists for this entity
                if key == "color_link_to_displacement":
                    MaterialManipulator._link_color_to_displacement_for_mat(
                        material, value)
                elif key == "change_to_vertex_color":
                    MaterialManipulator._map_vertex_color(material, value)
                elif key == "textures":
                    loaded_textures = self._load_textures(value)
                    self._set_textures(loaded_textures, material)
                elif hasattr(material, key):
                    # set the value
                    setattr(material, key, value)
コード例 #10
0
    def _get_random_texture(self, textures):
        """ Chooses a random texture data from the provided list.

        :param textures: List of dicts. Each dict is describing a texture data (path to images, images, etc.).
        :return: A config object of the selected texture dict.
        """
        selected_dict = choice(textures)
        selected_texture = Config(selected_dict)

        return selected_texture
コード例 #11
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():
            if key == "cf_add_modifier":
                modifier_config = Config(params_conf.get_raw_dict(key))
                # instruction about unpacking the data: key, corresponding Config method to extract the value,
                # it's default value and a postproc function
                instructions = {"name": (Config.get_string, None, str.upper),
                             "thickness": (Config.get_float, None, None)}
                # unpack
                result = self._unpack_params(modifier_config, instructions)
            elif key == "cf_set_shading":
                result = params_conf.get_string("cf_set_shading")
            elif key == "cf_add_displace_modifier_with_texture":
                displace_config = Config(params_conf.get_raw_dict(key))
                # instruction about unpacking the data: key, corresponding Config method to extract the value,
                # it's default value and a postproc function
                instructions = {"texture": (Config.get_raw_value, [], None),
                             "mid_level": (Config.get_float, 0.5, None),
                             "subdiv_level": (Config.get_int, 2, None),
                             "strength": (Config.get_float, 0.1, None),
                             "min_vertices_for_subdiv": (Config.get_int, 10000, None)}
                # unpack
                result = self._unpack_params(displace_config, instructions)
            elif key == "cf_add_uv_mapping":
                uv_config = Config(params_conf.get_raw_dict(key))
                # instruction about unpacking the data: key, corresponding Config method to extract the value,
                # it's default value and a postproc function
                instructions = {"projection": (Config.get_string, None, str.lower)}
                # unpack
                result = self._unpack_params(uv_config, instructions)
            else:
                result = params_conf.get_raw_value(key)

            params.update({key: result})

        return params
コード例 #12
0
ファイル: CameraInterface.py プロジェクト: eugval/BlenderProc
    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
コード例 #13
0
    def __init__(self, config):
        Module.__init__(self, config)

        # Clean up example scene or scene created by last run when debugging pipeline inside blender
        Initializer.cleanup()

        # setting up the GlobalStorage
        global_config = Config(self.config.get_raw_dict("global", {}))
        GlobalStorage.init_global(global_config)

        # call the init again to make sure all values from the global config where read correctly, too
        self._default_init()
コード例 #14
0
    def run(self):
        # bpy.context.scene.world["category_id"] = 0
        bpy.context.scene.render.resolution_x = self.camera_info['width']
        bpy.context.scene.render.resolution_y = self.camera_info['height']

        # Collect camera and camera object
        cam_ob = bpy.context.scene.camera
        cam = cam_ob.data
        cam['loaded_intrinsics'] = np.array(
            [[self.camera_info['fx'], 0, self.camera_info['cx']],
             [0, self.camera_info['fy'], self.camera_info['cy']], [0, 0, 1]])
        cam['loaded_resolution'] = self.camera_info['width'], self.camera_info[
            'height']

        config = Config({})
        camera_module = CameraInterface(config)
        camera_module._set_cam_intrinsics(cam, config)

        loaded_objects = []

        # only load all/selected objects here, use other modules for setting poses
        # e.g. camera.CameraSampler / object.ObjectPoseSampler
        obj_ids = list(range(self.obj_df.shape[0]))
        # if sampling is enabled
        if self.sample_objects:
            loaded_ids = {}
            loaded_amount = 0
            if self.obj_instances_limit != -1 and len(
                    obj_ids
            ) * self.obj_instances_limit < self.num_of_objs_to_sample:
                raise RuntimeError(
                    "{}'s {} split contains {} objects, {} object where requested to sample with "
                    "an instances limit of {}. Raise the limit amount or decrease the requested "
                    "amount of objects.".format(self.bop_dataset_path,
                                                self.split, len(obj_ids),
                                                self.num_of_objs_to_sample,
                                                self.obj_instances_limit))
            while loaded_amount != self.num_of_objs_to_sample:
                obj_id = random.choice(obj_ids)
                if obj_id not in loaded_ids.keys():
                    loaded_ids.update({obj_id: 0})
                # if there is no limit or if there is one, but it is not reached for this particular object
                if self.obj_instances_limit == -1 or loaded_ids[
                        obj_id] < self.obj_instances_limit:
                    cur_obj = self._load_mesh(obj_id, scale=self.scale)
                    loaded_ids[obj_id] += 1
                    loaded_amount += 1
                    loaded_objects.append(cur_obj)
        else:
            for obj_id in obj_ids:
                cur_obj = self._load_mesh(obj_id, scale=self.scale)
                loaded_objects.append(cur_obj)
        self._set_properties(loaded_objects)
コード例 #15
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)
コード例 #16
0
    def run(self):
        """ 'Selects' objects and sets according values for defined attributes/custom properties."""
        instances = self.config.get_list("instances", [])
        for instance in instances:
            # separating defined part with the selector from ambiguous part with attribute names and their values to set
            set_params = {}
            sel_objs = {}
            for key in instance.keys():
                # if its not a selector -> to the set parameters dict
                if key != 'selector':
                    set_params[key] = instance[key]
                else:
                    sel_objs[key] = instance[key]
            # create Config objects
            params_conf = Config(set_params)
            sel_conf = Config(sel_objs)
            # invoke a Getter, get a list of objects to manipulate
            objects = sel_conf.get_list("selector")

            for key in params_conf.data.keys():
                # get raw value from the set parameters config object
                result = params_conf.get_raw_value(key)

                for obj in objects:
                    # if an attribute with such name exists for this object
                    if hasattr(obj, key):
                        # set the value
                        setattr(obj, key, result)
                    # if not, then treat it as a custom property. Values will be overwritten for existing custom
                    # property, but if the name is new then new custom property will be created
                    else:
                        obj[key] = result
コード例 #17
0
    def run(self):
        """ Constructs a ground plane.
            1. Get configuration parameters.
            2. Load shader.
            3. Construct ground plane and it's material node tree.
        """

        tiles = self.config.get_list("tiles")
        for tile in tiles:
            if tile:
                ground_config = Config(tile)
                self._load_shader(ground_config)
                self._construct_ground_plane(ground_config)
コード例 #18
0
    def add_item(self, parameters):
        """ Adds a new item based on the given parameters.

        :param parameters: A dict specifying the parameters.
        """
        # Start with the default parameters
        data = deepcopy(self.default_item_parameters)
        # Overwrite default parameter values with specific parameters for this item
        data = Utility.merge_dicts(parameters, data)
        # Create config object
        config = Config(data)
        # Call function to add new item
        self.add_item_func(config)
コード例 #19
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 == "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)
                    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
コード例 #20
0
ファイル: Utility.py プロジェクト: shenlongwang/BlenderProc
    def build_provider(name, parameters):
        """ Builds up providers like sampler or getter.

        It first builds the config and then constructs the required provider.

        :param name: The name of the provider class.
        :param parameters: A dict containing the parameters that should be used.
        :return: The constructed provider.
        """
        # Import class from src.utility
        module_class = getattr(importlib.import_module("src.provider." + name), name.split(".")[-1])
        # Build configuration
        config = Config(parameters)
        # Construct provider
        return module_class(config)
コード例 #21
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)
コード例 #22
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]))
コード例 #23
0
    def _sample_position(self, room_obj):
        """ Samples a random position inside the bbox of the given room object.

        :param room_obj: The room object whose bbox is used.
        :return: A vector describing the sampled position
        """

        max = mathutils.Vector()
        min = mathutils.Vector()
        for i in range(3):
            # Check if an interval for sampling has been configured, otherwise sample inside bbox
            if len(self.position_ranges[i]) != 2:
                min[i] = room_obj["bbox"]["min"][i]
                max[i] = room_obj["bbox"]["max"][i]
            else:
                min[i] = room_obj["bbox"]["min"][i] + self.position_ranges[i][0]
                max[i] = room_obj["bbox"]["min"][i] + self.position_ranges[i][1]

        return Uniform3d(Config({"min": list(min), "max": list(max)})).run()
コード例 #24
0
    def run(self):
        """ 'Selects' entities and sets according values for defined attributes/custom properties."""
        # separating defined part with the selector from ambiguous part with attribute names and their values to set
        set_params = {}
        sel_objs = {}
        for key in self.config.data.keys():
            # if its not a selector -> to the set parameters dict
            if key != 'selector':
                set_params[key] = self.config.data[key]
            else:
                sel_objs[key] = self.config.data[key]
        # create Config objects
        params_conf = Config(set_params)
        sel_conf = Config(sel_objs)
        # invoke a Getter, get a list of entities to manipulate
        entities = sel_conf.get_list("selector")

        op_mode = self.config.get_string("mode", "once_for_each")

        for key in params_conf.data.keys():
            # get raw value from the set parameters if it is to be sampled once for all selected entities
            if op_mode == "once_for_all":
                result = params_conf.get_raw_value(key)

            for entity in entities:
                if op_mode == "once_for_each":
                    # get raw value from the set parameters if it is to be sampled anew for each selected entity
                    result = params_conf.get_raw_value(key)

                # check if the key is a requested custom property
                requested_custom_property = False
                if key.startswith('cp_'):
                    requested_custom_property = True
                    key = key[3:]

                # if an attribute with such name exists for this entity
                if hasattr(entity, key) and not requested_custom_property:
                    # set the value
                    setattr(entity, key, result)
                # if not, then treat it as a custom property. Values will be overwritten for existing custom
                # property, but if the name is new then new custom property will be created
                else:
                    entity[key] = result
        # update all entities matrices
        bpy.context.view_layer.update()
コード例 #25
0
    def initialize_modules(module_configs):
        """ Initializes the modules described in the given configuration.

        Example for module_configs:
        [{
          "module": "base.ModuleA",
          "config": {...}
        }, ...]

        Here the name contains the path to the module class, starting from inside the src directory.

        Be aware that all attributes stored in the GlobalStorage are also accessible here, even though
        they are not copied into the new config.

        :param module_configs: A list of dicts, each one describing one module.
        :return:
        """
        modules = []

        for module_config in module_configs:
            # If only the module name is given (short notation)
            if isinstance(module_config, str):
                module_config = {"module": module_config}

            # Initialize config with empty config
            config = {}
            # Check if there is a module specific config
            if "config" in module_config:
                # Overwrite with module specific config
                Utility.merge_dicts(module_config["config"], config)

            with Utility.BlockStopWatch("Initializing module " +
                                        module_config["module"]):
                # Import file and extract class
                module_class = getattr(
                    importlib.import_module("src." + module_config["module"]),
                    module_config["module"].split(".")[-1])
                # Create module
                modules.append(module_class(Config(config)))

        return modules
コード例 #26
0
    def sample_and_validate_cam_pose(self, cam, cam_ob, config):
        """ Samples a new camera pose, sets the parameters of the given camera object accordingly and validates it.

        :param cam: The camera which contains only camera specific attributes.
        :param cam_ob: The object linked to the camera which determines general properties like location/orientation
        :param config: The config object describing how to sample
        :return: True, if the sampled pose was valid
        """
        # Sample used floor obj
        floor_obj = random.choice(self.used_floors)

        # Sample/set intrinsics
        self._set_cam_intrinsics(
            cam, Config(self.config.get_raw_dict("intrinsics", {})))

        # Sample camera extrinsics (we do not set them yet for performance reasons)
        cam2world_matrix = self._cam2world_matrix_from_cam_extrinsics(config)

        # Make sure the sampled location is inside the room => overwrite x and y and add offset to z
        bounding_box = get_bounds(floor_obj)
        min_corner = np.min(bounding_box, axis=0)
        max_corner = np.max(bounding_box, axis=0)

        cam2world_matrix.translation[0] = random.uniform(
            min_corner[0], max_corner[0])
        cam2world_matrix.translation[1] = random.uniform(
            min_corner[1], max_corner[1])
        cam2world_matrix.translation[2] += floor_obj.location[2]

        # Check if sampled pose is valid
        if self._is_pose_valid(floor_obj, cam, cam_ob, cam2world_matrix):
            # Set camera extrinsics as the pose is valid
            CameraUtility.add_camera_pose(cam2world_matrix)
            return True
        else:
            return False
コード例 #27
0
    def sample_and_validate_cam_pose(self, cam, cam_ob, config):
        """ Samples a new camera pose, sets the parameters of the given camera object accordingly and validates it.

        :param cam: The camera which contains only camera specific attributes.
        :param cam_ob: The object linked to the camera which determines general properties like location/orientation
        :param config: The config object describing how to sample
        :return: True, if the sampled pose was valid
        """
        # Sample room
        room_id = random.randrange(len(self.rooms))
        room_obj, floor_obj = self.rooms[room_id]

        # Sample/set intrinsics
        self._set_cam_intrinsics(
            cam, Config(self.config.get_raw_dict("intrinsics", {})))

        # Sample camera extrinsics (we do not set them yet for performance reasons)
        cam2world_matrix = self._cam2world_matrix_from_cam_extrinsics(config)

        # Make sure the sampled location is inside the room => overwrite x and y and add offset to z
        cam2world_matrix.translation[0] = random.uniform(
            room_obj["bbox"]["min"][0], room_obj["bbox"]["max"][0])
        cam2world_matrix.translation[1] = random.uniform(
            room_obj["bbox"]["min"][1], room_obj["bbox"]["max"][1])
        cam2world_matrix.translation[2] += room_obj["bbox"]["min"][2]

        # Check if sampled pose is valid
        if self._is_pose_valid(floor_obj, cam, cam_ob, cam2world_matrix):
            # Set camera extrinsics as the pose is valid
            frame = CameraUtility.add_camera_pose(cam2world_matrix)
            cam_ob["room_id"] = room_id
            # As the room id depends on the camera pose and therefore on the keyframe, we also need to add keyframes for the room id
            cam_ob.keyframe_insert(data_path='["room_id"]', frame=frame)
            return True
        else:
            return False
コード例 #28
0
    def _add_dust_to_material(self, material: Material, value: dict):
        """
        Adds a dust film to the material, where the strength determines how much dust is used.

        This will be added right before the output of the material.

        :param material: Used material
        :param value: dict with all used keys
        """

        # extract values from the config, like strength, texture_scale and used_dust_texture
        config = Config(value)
        strength = config.get_float("strength")
        texture_scale = config.get_float("texture_scale", 0.1)
        # if no texture is used, a random noise texture is generated
        texture_nodes = config.get_list("used_dust_texture", None)

        group_node = material.new_node("ShaderNodeGroup")
        group_node.width = 250
        group = bpy.data.node_groups.new(name="Dust Material", type="ShaderNodeTree")
        group_node.node_tree = group
        nodes, links = group.nodes, group.links

        # define start locations and differences, to make the debugging easier
        x_pos, x_diff = -(250 * 4), 250
        y_pos, y_diff = (x_diff * 1), x_diff

        # Extract the normal for the current material location
        geometry_node = nodes.new('ShaderNodeNewGeometry')
        geometry_node.location.x = x_pos + x_diff * 0
        geometry_node.location.y = y_pos
        # this node clips the values, to avoid negative values in the usage below
        clip_mix_node = nodes.new("ShaderNodeMixRGB")
        clip_mix_node.inputs["Fac"].default_value = 1.0
        clip_mix_node.use_clamp = True
        clip_mix_node.location.x = x_pos + x_diff * 1
        clip_mix_node.location.y = y_pos
        links.new(geometry_node.outputs["Normal"], clip_mix_node.inputs["Color2"])

        # use only the z component
        separate_z_normal = nodes.new("ShaderNodeSeparateRGB")
        separate_z_normal.location.x = x_pos + x_diff * 2
        separate_z_normal.location.y = y_pos
        links.new(clip_mix_node.outputs["Color"], separate_z_normal.inputs["Image"])

        # this layer weight adds a small fresnel around the object, which makes it more realistic
        layer_weight = nodes.new("ShaderNodeLayerWeight")
        layer_weight.location.x = x_pos + x_diff * 2
        layer_weight.location.y = y_pos - y_diff * 1
        layer_weight.inputs["Blend"].default_value = 0.5
        # combine it with the z component
        mix_with_layer_weight = nodes.new("ShaderNodeMixRGB")
        mix_with_layer_weight.location.x = x_pos + x_diff * 3
        mix_with_layer_weight.location.y = y_pos
        mix_with_layer_weight.inputs["Fac"].default_value = 0.2
        links.new(separate_z_normal.outputs["B"], mix_with_layer_weight.inputs["Color1"])
        links.new(layer_weight.outputs["Facing"], mix_with_layer_weight.inputs["Color2"])
        # add a gamma node, to scale the colors correctly
        gamma_node = nodes.new("ShaderNodeGamma")
        gamma_node.location.x = x_pos + x_diff * 4
        gamma_node.location.y = y_pos
        gamma_node.inputs["Gamma"].default_value = 2.2
        links.new(mix_with_layer_weight.outputs["Color"], gamma_node.inputs["Color"])

        # use an overlay node to combine it with the texture result
        overlay = nodes.new("ShaderNodeMixRGB")
        overlay.location.x = x_pos + x_diff * 5
        overlay.location.y = y_pos
        overlay.blend_type = "OVERLAY"
        overlay.inputs["Fac"].default_value = 1.0
        links.new(gamma_node.outputs["Color"], overlay.inputs["Color1"])

        # add a multiply node to scale down or up the strength of the dust
        multiply_node = nodes.new("ShaderNodeMath")
        multiply_node.location.x = x_pos + x_diff * 6
        multiply_node.location.y = y_pos
        multiply_node.inputs[1].default_value = strength
        multiply_node.operation = "MULTIPLY"
        links.new(overlay.outputs["Color"], multiply_node.inputs[0])

        # add texture coords to make the scaling of the dust texture possible
        texture_coords = nodes.new("ShaderNodeTexCoord")
        texture_coords.location.x = x_pos + x_diff * 0
        texture_coords.location.y = y_pos - y_diff * 2
        mapping_node = nodes.new("ShaderNodeMapping")
        mapping_node.location.x = x_pos + x_diff * 1
        mapping_node.location.y = y_pos - y_diff * 2
        mapping_node.vector_type = "TEXTURE"
        scale_value = texture_scale
        mapping_node.inputs["Scale"].default_value = [scale_value] * 3
        links.new(texture_coords.outputs["UV"], mapping_node.inputs["Vector"])
        # check if a texture should be used
        if texture_nodes is not None and texture_nodes:
            texture_node = nodes.new("ShaderNodeTexImage")
            texture_node.location.x = x_pos + x_diff * 2
            texture_node.location.y = y_pos - y_diff * 2
            texture_node.image = random.choice(texture_nodes).image
            links.new(mapping_node.outputs["Vector"], texture_node.inputs["Vector"])
            links.new(texture_node.outputs["Color"], overlay.inputs["Color2"])
        else:
            if not texture_nodes:
                warnings.warn("No texture was found, check the config. Random generated texture is used.")
            # if no texture is used, we great a random noise pattern, which is used instead
            noise_node = nodes.new("ShaderNodeTexNoise")
            noise_node.location.x = x_pos + x_diff * 2
            noise_node.location.y = y_pos - y_diff * 2
            # this determines the pattern of the dust flakes, a high scale makes them small enough to look like dust
            noise_node.inputs["Scale"].default_value = 250.0
            noise_node.inputs["Detail"].default_value = 0.0
            noise_node.inputs["Roughness"].default_value = 0.0
            noise_node.inputs["Distortion"].default_value = 1.9
            links.new(mapping_node.outputs["Vector"], noise_node.inputs["Vector"])
            # this noise_node produces RGB colors, we only need one value in this instance red
            separate_r_channel = nodes.new("ShaderNodeSeparateRGB")
            separate_r_channel.location.x = x_pos + x_diff * 3
            separate_r_channel.location.y = y_pos - y_diff * 2
            links.new(noise_node.outputs["Color"], separate_r_channel.inputs["Image"])
            # as the produced noise image has a nice fading to it, we use a color ramp to create dust flakes
            color_ramp = nodes.new("ShaderNodeValToRGB")
            color_ramp.location.x = x_pos + x_diff * 4
            color_ramp.location.y = y_pos - y_diff * 2
            color_ramp.color_ramp.elements[0].position = 0.4
            color_ramp.color_ramp.elements[0].color = [1, 1, 1, 1]
            color_ramp.color_ramp.elements[1].position = 0.46
            color_ramp.color_ramp.elements[1].color = [0, 0, 0, 1]
            links.new(separate_r_channel.outputs["R"], color_ramp.inputs["Fac"])
            links.new(color_ramp.outputs["Color"], overlay.inputs["Color2"])

        # combine the previous color with the new dust mode
        mix_shader = nodes.new("ShaderNodeMixShader")
        mix_shader.location = (x_pos + x_diff * 8, y_pos)
        links.new(multiply_node.outputs["Value"], mix_shader.inputs["Fac"])

        # add a bsdf node for the dust, this will be used to actually give the dust a color
        dust_color = nodes.new("ShaderNodeBsdfPrincipled")
        dust_color.location = (x_pos + x_diff * 6, y_pos - y_diff)
        # the used dust color is a grey with a tint in orange
        dust_color.inputs["Base Color"].default_value = [0.8, 0.773, 0.7, 1.0]
        dust_color.inputs["Roughness"].default_value = 1.0
        dust_color.inputs["Specular"].default_value = 0.0
        links.new(dust_color.outputs["BSDF"], mix_shader.inputs[2])

        # create the input and output nodes inside of the group
        group_output = nodes.new("NodeGroupOutput")
        group_output.location = (x_pos + x_diff * 9, y_pos)
        group_input = nodes.new("NodeGroupInput")
        group_input.location = (x_pos + x_diff * 7, y_pos - y_diff * 0.5)

        # create sockets for the outside of the group match them to the mix shader
        group.outputs.new(mix_shader.outputs[0].bl_idname, mix_shader.outputs[0].name)
        group.inputs.new(mix_shader.inputs[1].bl_idname, mix_shader.inputs[1].name)
        group.inputs.new(multiply_node.inputs[1].bl_idname, "Dust strength")
        group.inputs.new(mapping_node.inputs["Scale"].bl_idname, "Texture scale")

        # link the input and output to the mix shader
        links.new(group_input.outputs[0], mix_shader.inputs[1])
        links.new(mix_shader.outputs[0], group_output.inputs[0])
        links.new(group_input.outputs["Dust strength"], multiply_node.inputs[1])
        links.new(group_input.outputs["Texture scale"], mapping_node.inputs["Scale"])

        # remove the connection between the output and the last node and put the mix shader in between
        node_connected_to_the_output, material_output = material.get_node_connected_to_the_output_and_unlink_it()

        # place the group node above the material output
        group_node.location = (material_output.location.x - x_diff, material_output.location.y + y_diff)

        # connect the dust group
        material.link(node_connected_to_the_output.outputs[0], group_node.inputs[0])
        material.link(group_node.outputs[0], material_output.inputs["Surface"])

        # set the default values
        group_node.inputs["Dust strength"].default_value = strength
        group_node.inputs["Texture scale"].default_value = [texture_scale] * 3
コード例 #29
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)
コード例 #30
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])