コード例 #1
0
    def new_material(self, name: str):
        """ Creates a new material and adds it to the object.

        :param name: The name of the new material.
        """
        new_mat = Material.create(name)
        self.add_material(new_mat)
        return new_mat
コード例 #2
0
    def _create_mesh_objects_from_file(data: dict,
                                       ceiling_light_strength: float,
                                       mapping: dict,
                                       json_path: str) -> List[MeshObject]:
        """
        This creates for a given data json block all defined meshes and assigns the correct materials.
        This means that the json file contains some mesh, like walls and floors, which have to built up manually.

        It also already adds the lighting for the ceiling

        :param data: json data dir. Must contain "material" and "mesh"
        :param ceiling_light_strength: Strength of the emission shader used in the ceiling.
        :param mapping: A dict which maps the names of the objects to ids.
        :param json_path: Path to the json file, where the house information is stored.
        :return: The list of loaded mesh objects.
        """
        # extract all used materials -> there are more materials defined than used
        used_materials = []
        for mat in data["material"]:
            used_materials.append({
                "uid": mat["uid"],
                "texture": mat["texture"],
                "normaltexture": mat["normaltexture"],
                "color": mat["color"]
            })

        created_objects = []
        for mesh_data in data["mesh"]:
            # extract the obj name, which also is used as the category_id name
            used_obj_name = mesh_data["type"].strip()
            if used_obj_name == "":
                used_obj_name = "void"
            if "material" not in mesh_data:
                warnings.warn(
                    f"Material is not defined for {used_obj_name} in this file: {json_path}"
                )
                continue
            # create a new mesh
            obj = MeshObject.create_with_empty_mesh(used_obj_name,
                                                    used_obj_name + "_mesh")
            created_objects.append(obj)

            # set two custom properties, first that it is a 3D_future object and second the category_id
            obj.set_cp("is_3D_future", True)
            obj.set_cp("category_id", mapping[used_obj_name.lower()])

            # get the material uid of the current mesh data
            current_mat = mesh_data["material"]
            used_mat = None
            # search in the used materials after this uid
            for u_mat in used_materials:
                if u_mat["uid"] == current_mat:
                    used_mat = u_mat
                    break
            # If there should be a material used
            if used_mat:
                if used_mat["texture"]:
                    raise Exception(
                        "The material should use a texture, this was not implemented yet!"
                    )
                if used_mat["normaltexture"]:
                    raise Exception(
                        "The material should use a normal texture, this was not implemented yet!"
                    )
                # if there is a normal color used
                if used_mat["color"]:
                    # Create a new material
                    mat = Material.create(name=used_obj_name + "_material")
                    # create a principled node and set the default color
                    principled_node = mat.get_the_one_node_with_type(
                        "BsdfPrincipled")
                    principled_node.inputs[
                        "Base Color"].default_value = mathutils.Vector(
                            used_mat["color"]) / 255.0
                    # if the object is a ceiling add some light output
                    if "ceiling" in used_obj_name.lower():
                        mat.make_emissive(ceiling_light_strength,
                                          keep_using_base_color=False,
                                          emission_color=mathutils.Vector(
                                              used_mat["color"]) / 255.0)

                    # as this material was just created the material is just append it to the empty list
                    obj.add_material(mat)

            # extract the vertices from the mesh_data
            vert = [float(ele) for ele in mesh_data["xyz"]]
            # extract the faces from the mesh_data
            faces = mesh_data["faces"]
            # extract the normals from the mesh_data
            normal = [float(ele) for ele in mesh_data["normal"]]

            # map those to the blender coordinate system
            num_vertices = int(len(vert) / 3)
            vertices = np.reshape(np.array(vert), [num_vertices, 3])
            normal = np.reshape(np.array(normal), [num_vertices, 3])
            # flip the first and second value
            vertices[:, 1], vertices[:, 2] = vertices[:,
                                                      2], vertices[:,
                                                                   1].copy()
            normal[:, 1], normal[:, 2] = normal[:, 2], normal[:, 1].copy()
            # reshape back to a long list
            vertices = np.reshape(vertices, [num_vertices * 3])
            normal = np.reshape(normal, [num_vertices * 3])

            # add this new data to the mesh object
            mesh = obj.get_mesh()
            mesh.vertices.add(num_vertices)
            mesh.vertices.foreach_set("co", vertices)
            mesh.vertices.foreach_set("normal", normal)

            # link the faces as vertex indices
            num_vertex_indicies = len(faces)
            mesh.loops.add(num_vertex_indicies)
            mesh.loops.foreach_set("vertex_index", faces)

            # the loops are set based on how the faces are a ranged
            num_loops = int(num_vertex_indicies / 3)
            mesh.polygons.add(num_loops)
            # always 3 vertices form one triangle
            loop_start = np.arange(0, num_vertex_indicies, 3)
            # the total size of each triangle is therefore 3
            loop_total = [3] * num_loops
            mesh.polygons.foreach_set("loop_start", loop_start)
            mesh.polygons.foreach_set("loop_total", loop_total)

            # the uv coordinates are reshaped then the face coords are extracted
            uv_mesh_data = [
                float(ele) for ele in mesh_data["uv"] if ele is not None
            ]
            # bb1737bf-dae6-4215-bccf-fab6f584046b.json includes one mesh which only has no UV mapping
            if uv_mesh_data:
                uv = np.reshape(np.array(uv_mesh_data), [num_vertices, 2])
                used_uvs = uv[faces, :]
                # and again reshaped back to the long list
                used_uvs = np.reshape(used_uvs, [2 * num_vertex_indicies])

                mesh.uv_layers.new(name="new_uv_layer")
                mesh.uv_layers[-1].data.foreach_set("uv", used_uvs)
            else:
                warnings.warn(
                    f"This mesh {obj.name} does not have a specified uv map!")

            # this update converts the upper data into a mesh
            mesh.update()

            # the generation might fail if the data does not line up
            # this is not used as even if the data does not line up it is still able to render the objects
            # We assume that not all meshes in the dataset do conform with the mesh standards set in blender
            #result = mesh.validate(verbose=False)
            #if result:
            #    raise Exception("The generation of the mesh: {} failed!".format(used_obj_name))

        return created_objects