Пример #1
0
    def assign_materials_to_floor_wall_ceiling(self):
        """
        Assigns materials to the floor, wall and ceiling. These are randomly selected from the CCMaterials. This means
        it is required that the CCMaterialLoader has been executed before, this module is run.
        """

        # first create a uv mapping for each of the three objects
        for obj in [self.floor_obj, self.wall_obj, self.ceiling_obj]:
            if obj is not None:
                bpy.ops.object.select_all(action='DESELECT')
                obj.select_set(True)
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='SELECT')
                bpy.ops.uv.cube_project(cube_size=1.0)
                bpy.ops.object.mode_set(mode='OBJECT')

        # only select non see through materials
        config = {
            "conditions": {
                "cp_is_cc_texture": True,
                "cf_principled_bsdf_Alpha_eq": 1.0
            }
        }
        material_getter = Material(Config(config))
        all_cc_materials = material_getter.run()

        def assign_material(cur_obj: bpy.types.Object,
                            material: bpy.types.Material):
            """
            First all materials are removed and then the given material is assigned

            :param cur_obj: Current object
            :param material: Current material, which will be assigned
            """
            # first remove all existing
            if cur_obj.material_slots:
                for i in range(len(cur_obj.material_slots)):
                    bpy.ops.object.material_slot_remove({'object': cur_obj})
            # add the new one
            cur_obj.data.materials.append(material)

        if all_cc_materials:
            assign_material(self.floor_obj, random.choice(all_cc_materials))
            assign_material(self.wall_obj, random.choice(all_cc_materials))
            if self.ceiling_obj is not None and self.assign_material_to_ceiling:
                assign_material(self.ceiling_obj,
                                random.choice(all_cc_materials))
        else:
            warnings.warn(
                "There were no CCMaterials found, which means the CCMaterialLoader was not executed first!"
                "No materials have been assigned to the walls, floors and possible ceiling."
            )
Пример #2
0
    def run(self):
        # use a loader module to load objects
        bpy.ops.object.select_all(action='SELECT')
        previously_selected_objects = set(bpy.context.selected_objects)
        module_list_config = self.config.get_list("used_loader_config")
        modules = Utility.initialize_modules(module_list_config)
        for module in modules:
            print("Running module " + module.__class__.__name__)
            module.run()
        bpy.ops.object.select_all(action='SELECT')
        loaded_objects = list(
            set(bpy.context.selected_objects) - previously_selected_objects)

        # only select non see through materials
        config = {
            "conditions": {
                "cp_is_cc_texture": True,
                "cf_principled_bsdf_Alpha_eq": 1.0
            }
        }
        material_getter = MaterialProvider(Config(config))
        all_cc_materials = Material.convert_to_materials(material_getter.run())

        RandomRoomConstructor.construct(
            used_floor_area=self.used_floor_area,
            interior_objects=MeshObject.convert_to_meshes(loaded_objects),
            materials=all_cc_materials,
            amount_of_extrusions=self.amount_of_extrusions,
            fac_from_square_room=self.fac_from_square_room,
            corridor_width=self.corridor_width,
            wall_height=self.wall_height,
            amount_of_floor_cuts=self.amount_of_floor_cuts,
            only_use_big_edges=self.only_use_big_edges,
            create_ceiling=self.create_ceiling,
            assign_material_to_ceiling=self.assign_material_to_ceiling,
            placement_tries_per_face=self.tries_per_face,
            amount_of_objects_per_sq_meter=self.amount_of_objects_per_sq_meter)
Пример #3
0
    def run(self):
        """ Randomizes materials for selected objects.
            1. For each object assign a randomly chosen material from the pool.
        """
        self.randomization_level = self.config.get_float(
            "randomization_level", 0.2)
        self._objects_to_manipulate = self.config.get_list(
            'manipulated_objects', get_all_mesh_objects())
        self._materials_to_replace_with = self.config.get_list(
            "materials_to_replace_with", get_all_materials())
        self._obj_materials_cond_to_be_replaced = self.config.get_raw_dict(
            "obj_materials_cond_to_be_replaced", {})
        op_mode = self.config.get_string("mode", "once_for_each")

        # if there were no materials selected throw an exception
        if not self._materials_to_replace_with:
            print(
                "Warning: No materials selected inside of the MaterialRandomizer!"
            )
            return

        if op_mode == "once_for_all":
            random_material = np.random.choice(self._materials_to_replace_with)

        # walk over all objects
        for obj in self._objects_to_manipulate:
            if hasattr(obj, 'material_slots'):
                # walk over all materials
                for material in obj.material_slots:
                    use_mat = True
                    if self._obj_materials_cond_to_be_replaced:
                        use_mat = len(
                            Material.perform_and_condition_check(
                                self._obj_materials_cond_to_be_replaced, [],
                                [material.material])) == 1
                    if use_mat:
                        if np.random.uniform(0, 1) <= self.randomization_level:
                            if op_mode == "once_for_each":
                                random_material = np.random.choice(
                                    self._materials_to_replace_with)
                            # select a random material to replace the old one with
                            material.material = random_material
Пример #4
0
    def _randomize_materials(self, entity, value):
        """ Replaces each material of an entity with certain probability.

        :param entity: An object to modify. Type: bpy.types.Object.
        :param value: Configuration data. Type: dict.
        """
        if hasattr(entity, 'material_slots'):
            if entity.material_slots:
                for mat in entity.material_slots:
                    use_mat = True
                    if value["obj_materials_cond_to_be_replaced"]:
                        use_mat = len(Material.perform_and_condition_check(value["obj_materials_cond_to_be_replaced"], [],
                                                                           [mat.material])) == 1
                    if use_mat:
                        if np.random.uniform(0, 1) <= value["randomization_level"]:
                            mat.material = choice(value["materials_to_replace_with"])
            elif value["add_to_objects_without_material"]:
                # this object didn't have a material before
                if np.random.uniform(0, 1) <= value["randomization_level"]:
                    entity.data.materials.append(choice(value["materials_to_replace_with"]))
Пример #5
0
    def _randomize_materials(self, entity, value):
        """ Replaces each material of an entity with certain probability.

        :param entity: An object to modify. Type: bpy.types.Object.
        :param value: Configuration data. Type: dict.
        """
        if len(value["materials_to_replace_with"]) != 1:
            raise RuntimeError("getter.Material returned more than one or no substitute material, namely this much: {}. "
                               "Please, make sure you enabled sampling in the Providers config by using "
                               "'random_samples': 1 as a config parameter, and that conditions are not too strict such "
                               "that some materials can meet them".format(len(value["materials_to_replace_with"])))
        if hasattr(entity, 'material_slots'):
            for mat in entity.material_slots:
                use_mat = True
                if value["obj_materials_cond_to_be_replaced"]:
                    use_mat = len(Material.perform_and_condition_check(value["obj_materials_cond_to_be_replaced"], [],
                                                                       [mat.material])) == 1
                if use_mat:
                    if np.random.uniform(0, 1) <= value["randomization_level"]:
                        mat.material = value["materials_to_replace_with"][0]
Пример #6
0
    def find_cc_material_by_name(material_name: str, custom_properties: dict):
        """
        Finds from all loaded materials the cc material, which has the given material_name and the given
        custom_properties.

        :param material_name: Name of the searched material
        :param custom_properties: Custom properties, which have been assigned before
        :return: bpy.types.Material: Return the searched material, if not found returns None
        """
        # find used cc materials with this name
        cond = {"cp_is_cc_texture": True, "cp_asset_name": material_name}
        for key, value in custom_properties.items():
            cond[key] = value
        new_mats = Material.perform_and_condition_check(cond, [])
        if len(new_mats) == 1:
            new_mat = new_mats[0]
            return new_mat
        elif len(new_mats) > 1:
            raise Exception("There was more than one material found!")
        else:
            # the material was not even loaded
            return None
Пример #7
0
    def run(self):
        self._folder_path = Utility.resolve_path(self.config.get_string("folder_path", "resources/cctextures"))
        self._used_assets = self.config.get_list("used_assets", [])
        self._add_cp = self.config.get_raw_dict("add_custom_properties", {})
        self._preload = self.config.get_bool("preload", False)
        self._fill_used_empty_materials = self.config.get_bool("fill_used_empty_materials", False)

        if self._preload and self._fill_used_empty_materials:
            raise Exception("Preload and fill used empty materials can not be done at the same time, check config!")

        x_texture_node = -1500
        y_texture_node = 300
        if os.path.exists(self._folder_path) and os.path.isdir(self._folder_path):
            for asset in os.listdir(self._folder_path):
                if self._used_assets:
                    skip_this_one = True
                    for used_asset in self._used_assets:
                        if asset.startswith(used_asset):
                            skip_this_one = False
                            break
                    if skip_this_one:
                        continue
                current_path = os.path.join(self._folder_path, asset)
                if os.path.isdir(current_path):
                    base_image_path = os.path.join(current_path, "{}_2K_Color.jpg".format(asset))
                    if not os.path.exists(base_image_path):
                        continue

                    if self._fill_used_empty_materials:
                        # find used cc materials with this name
                        cond = {"cp_is_cc_texture": True, "cp_asset_name": asset}
                        for key, value in self._add_cp.items():
                            cond[key] = value
                        new_mats = Material.perform_and_condition_check(cond, [])
                        if len(new_mats) == 1:
                            new_mat = new_mats[0]
                        elif len(new_mats) > 1:
                            raise Exception("There was more than one material found!")
                        else:
                            # the material was not even loaded
                            continue
                        # check amount of usage of this material
                        if new_mat.users == 0:
                            # no one is using this material skip loading
                            continue
                        # only loads materials which are actually used
                        print("Fill material: {}".format(asset))
                    else:
                        # create a new material with the name of the asset
                        new_mat = bpy.data.materials.new(asset)
                        new_mat["is_cc_texture"] = True
                        new_mat["asset_name"] = asset
                        new_mat.use_nodes = True
                        for key, value in self._add_cp.items():
                            cp_key = key
                            if key.startswith("cp_"):
                                cp_key = key[len("cp_"):]
                            else:
                                raise Exception("All cp have to start with cp_")
                            new_mat[cp_key] = value
                    if self._preload:
                        continue
                    collection_of_texture_nodes = []

                    nodes = new_mat.node_tree.nodes
                    links = new_mat.node_tree.links

                    principled_bsdf = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled")
                    output_node = Utility.get_the_one_node_with_type(nodes, "OutputMaterial")

                    base_color = nodes.new('ShaderNodeTexImage')
                    base_color.image = bpy.data.images.load(base_image_path, check_existing=True)
                    base_color.location.x = x_texture_node
                    base_color.location.y = y_texture_node
                    collection_of_texture_nodes.append(base_color)

                    links.new(base_color.outputs["Color"], principled_bsdf.inputs["Base Color"])

                    principled_bsdf.inputs["Specular"].default_value = 0.333

                    ambient_occlusion_image_path = base_image_path.replace("Color", "AmbientOcclusion")
                    if os.path.exists(ambient_occlusion_image_path):
                        ao_color = nodes.new('ShaderNodeTexImage')
                        ao_color.image = bpy.data.images.load(ambient_occlusion_image_path, check_existing=True)
                        ao_color.location.x = x_texture_node
                        ao_color.location.y = y_texture_node * 2
                        collection_of_texture_nodes.append(ao_color)

                        math_node = nodes.new(type='ShaderNodeMixRGB')
                        math_node.blend_type = "MULTIPLY"
                        math_node.location.x = x_texture_node * 0.5
                        math_node.location.y = y_texture_node * 1.5
                        math_node.inputs["Fac"].default_value = 0.333

                        links.new(base_color.outputs["Color"], math_node.inputs[1])
                        links.new(ao_color.outputs["Color"], math_node.inputs[2])
                        links.new(math_node.outputs["Color"], principled_bsdf.inputs["Base Color"])

                    metalness_image_path = base_image_path.replace("Color", "Metalness")
                    if os.path.exists(metalness_image_path):
                        metalness_texture = nodes.new('ShaderNodeTexImage')
                        metalness_texture.image = bpy.data.images.load(metalness_image_path, check_existing=True)
                        metalness_texture.location.x = x_texture_node
                        metalness_texture.location.y = y_texture_node * 0
                        collection_of_texture_nodes.append(metalness_texture)

                        links.new(metalness_texture.outputs["Color"], principled_bsdf.inputs["Metallic"])

                    roughness_image_path = base_image_path.replace("Color", "Roughness")
                    if os.path.exists(roughness_image_path):
                        roughness_texture = nodes.new('ShaderNodeTexImage')
                        roughness_texture.image = bpy.data.images.load(roughness_image_path, check_existing=True)
                        roughness_texture.location.x = x_texture_node
                        roughness_texture.location.y = y_texture_node * -1
                        collection_of_texture_nodes.append(roughness_texture)

                        links.new(roughness_texture.outputs["Color"], principled_bsdf.inputs["Roughness"])

                    alpha_image_path = base_image_path.replace("Color", "Opacity")
                    if os.path.exists(alpha_image_path):
                        alpha_texture = nodes.new('ShaderNodeTexImage')
                        alpha_texture.image = bpy.data.images.load(alpha_image_path, check_existing=True)
                        alpha_texture.location.x = x_texture_node
                        alpha_texture.location.y = y_texture_node * -2
                        collection_of_texture_nodes.append(alpha_texture)

                        links.new(alpha_texture.outputs["Color"], principled_bsdf.inputs["Alpha"])

                    normal_image_path = base_image_path.replace("Color", "Normal")
                    normal_y_value = y_texture_node * -3
                    if os.path.exists(normal_image_path):
                        normal_texture = nodes.new('ShaderNodeTexImage')
                        normal_texture.image = bpy.data.images.load(normal_image_path, check_existing=True)
                        normal_texture.location.x = x_texture_node
                        normal_texture.location.y = normal_y_value
                        collection_of_texture_nodes.append(normal_texture)
                        direct_x_mode = True
                        if direct_x_mode:

                            separate_rgba = nodes.new('ShaderNodeSeparateRGB')
                            separate_rgba.location.x = 4.0/5.0 * x_texture_node
                            separate_rgba.location.y = normal_y_value
                            links.new(normal_texture.outputs["Color"], separate_rgba.inputs["Image"])

                            invert_node = nodes.new("ShaderNodeInvert")
                            invert_node.inputs["Fac"].default_value = 1.0
                            invert_node.location.x = 3.0/5.0 * x_texture_node
                            invert_node.location.y = normal_y_value

                            links.new(separate_rgba.outputs["G"], invert_node.inputs["Color"])

                            combine_rgba = nodes.new('ShaderNodeCombineRGB')
                            combine_rgba.location.x = 2.0/5.0 * x_texture_node
                            combine_rgba.location.y = normal_y_value
                            links.new(separate_rgba.outputs["R"], combine_rgba.inputs["R"])
                            links.new(invert_node.outputs["Color"], combine_rgba.inputs["G"])
                            links.new(separate_rgba.outputs["B"], combine_rgba.inputs["B"])

                            current_output = combine_rgba.outputs["Image"]
                        else:
                            current_output = normal_texture.outputs["Color"]

                        normal_map = nodes.new("ShaderNodeNormalMap")
                        normal_map.inputs["Strength"].default_value = 1.0
                        normal_map.location.x = 1.0 / 5.0 * x_texture_node
                        normal_map.location.y = normal_y_value
                        links.new(current_output, normal_map.inputs["Color"])
                        links.new(normal_map.outputs["Normal"], principled_bsdf.inputs["Normal"])


                    displacement_image_path = base_image_path.replace("Color", "Displacement")
                    if os.path.exists(displacement_image_path):
                        displacement_texture = nodes.new('ShaderNodeTexImage')
                        displacement_texture.image = bpy.data.images.load(displacement_image_path, check_existing=True)
                        displacement_texture.location.x = x_texture_node
                        displacement_texture.location.y = y_texture_node * -4
                        collection_of_texture_nodes.append(displacement_texture)

                        displacement_node = nodes.new("ShaderNodeDisplacement")
                        displacement_node.inputs["Midlevel"].default_value = 0.5
                        displacement_node.inputs["Scale"].default_value = 0.05
                        displacement_node.location.x = x_texture_node * 0.5
                        displacement_node.location.y = y_texture_node * -4
                        links.new(displacement_texture.outputs["Color"], displacement_node.inputs["Height"])
                        links.new(displacement_node.outputs["Displacement"], output_node.inputs["Displacement"])


                    if len(collection_of_texture_nodes) > 0:
                        texture_coords = nodes.new("ShaderNodeTexCoord")
                        texture_coords.location.x = x_texture_node * 1.4
                        mapping_node = nodes.new("ShaderNodeMapping")
                        mapping_node.location.x = x_texture_node * 1.2

                        links.new(texture_coords.outputs["UV"], mapping_node.inputs["Vector"])
                        for texture_node in collection_of_texture_nodes:
                            links.new(mapping_node.outputs["Vector"], texture_node.inputs["Vector"])