Beispiel #1
0
def make_mesh_from_verts_and_faces(verts, faces, convex_props):
    """Creates a new mesh object from provided vertices and faces and their properties."""
    new_mesh = bpy.data.meshes.new(convex_props['name'])
    new_bm = bmesh.new()

    # MAKE VERTICES
    _mesh.bm_make_vertices(new_bm, verts)

    # MAKE FACES
    _mesh.bm_make_faces(new_bm, faces, [])

    new_bm.to_mesh(new_mesh)
    new_mesh.update()
    new_bm.free()

    # Add the mesh as an object into the scene with this utility module.
    new_object = bpy_object_utils.object_data_add(bpy.context, new_mesh).object
    new_object = set_collider_props(new_object, convex_props)
    new_object.location = convex_props['location']
    new_object.rotation_euler = convex_props['rotation']

    return new_object
Beispiel #2
0
def make_mesh_from_verts_and_faces(verts, faces, convex_props):
    """Creates a new mesh object from provided vertices and faces and their properties."""
    new_mesh = bpy.data.meshes.new(convex_props["name"])
    new_bm = bmesh.new()

    # MAKE VERTICES
    _mesh.bm_make_vertices(new_bm, verts)

    # MAKE FACES
    _mesh.bm_make_faces(new_bm, faces, [])

    new_bm.to_mesh(new_mesh)
    new_mesh.update()
    new_bm.free()

    # Add the mesh as an object into the scene with this utility module.
    new_object = bpy_object_utils.object_data_add(bpy.context, new_mesh).object
    new_object = set_collider_props(new_object, convex_props)
    new_object.location = convex_props["location"]
    new_object.rotation_euler = convex_props["rotation"]

    return new_object
Beispiel #3
0
def _create_piece(
    context,
    name,
    mesh_vertices,
    mesh_normals,
    mesh_tangents,
    mesh_rgb,
    mesh_rgba,
    mesh_scalars,
    object_skinning,
    mesh_uv,
    mesh_uv_aliases,
    mesh_tuv,
    mesh_faces,
    mesh_face_materials,
    mesh_edges,
    terrain_points_trans,
    materials_data,
):
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_normals",
                      mesh_normals)
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_tangents",
                      mesh_tangents)
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_tuv", mesh_tuv)

    context.window_manager.progress_begin(0.0, 1.0)
    context.window_manager.progress_update(0)

    import_scale = _get_scs_globals().import_scale
    mesh = bpy.data.meshes.new(name)

    # COORDINATES TRANSFORMATION
    transformed_mesh_vertices = [
        _convert_utils.change_to_scs_xyz_coordinates(vec, import_scale)
        for vec in mesh_vertices
    ]

    context.window_manager.progress_update(0.1)

    # VISUALISE IMPORTED NORMALS (DEBUG)
    # NOTE: NOT functional for PIM version 7 since mesh normals are not provided in per vertex fashion!
    # visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scale)

    bm = bmesh.new()

    # VERTICES
    _mesh_utils.bm_make_vertices(bm, transformed_mesh_vertices)
    context.window_manager.progress_update(0.2)

    # FACES
    # for fac_i, fac in enumerate(mesh_faces): print('     face[%i]: %s' % (fac_i, str(fac)))
    mesh_faces, back_faces = _mesh_utils.bm_make_faces(bm, mesh_faces, [])
    context.window_manager.progress_update(0.3)

    # SHARP EDGES
    # print('mesh_edges: %s' % str(mesh_edges))
    for edge in bm.edges:
        edge_verts = [edge.verts[0].index, edge.verts[1].index]
        edge_verts_inv = [edge.verts[1].index, edge.verts[0].index]
        if edge_verts in mesh_edges or edge_verts_inv in mesh_edges:
            # print('edge: %s' % str(edge_verts))
            edge.smooth = False
    context.window_manager.progress_update(0.4)

    # UV LAYERS
    if mesh_uv:
        for uv_layer_name in mesh_uv:
            _mesh_utils.bm_make_uv_layer(7, bm, mesh_faces, uv_layer_name,
                                         mesh_uv[uv_layer_name])
    context.window_manager.progress_update(0.5)

    # VERTEX COLOR
    mesh_rgb_final = {}
    if mesh_rgba:
        mesh_rgb_final.update(mesh_rgba)
    if mesh_rgb:
        mesh_rgb_final.update(mesh_rgb)

    for vc_layer_name in mesh_rgb_final:
        max_value = mesh_rgb_final[vc_layer_name][0][0][0] / 2

        for vc_entry in mesh_rgb_final[vc_layer_name]:
            for v_i in vc_entry:
                for i, value in enumerate(v_i):
                    if max_value < value / 2:
                        max_value = value / 2

        if max_value > mesh.scs_props.vertex_color_multiplier:
            mesh.scs_props.vertex_color_multiplier = max_value

        _mesh_utils.bm_make_vc_layer(7, bm, vc_layer_name,
                                     mesh_rgb_final[vc_layer_name],
                                     mesh.scs_props.vertex_color_multiplier)

    bm.to_mesh(mesh)
    mesh.update()
    bm.free()

    # NORMALS - has to be applied after bmesh creation as they are set directly to mesh
    if _get_scs_globals().import_use_normals:

        mesh.create_normals_split()

        # first set normals directly to loops
        for poly_i, poly in enumerate(mesh.polygons):

            for poly_loop_i, loop_i in enumerate(poly.loop_indices):

                curr_n = _convert_utils.scs_to_blend_matrix() @ Vector(
                    mesh_normals[poly_i][poly_loop_i])
                mesh.loops[loop_i].normal[:] = curr_n

        # then we have to go trough very important step they say,
        # as without validation we get wrong result for some normals
        mesh.validate(clean_customdata=False
                      )  # *Very* important to not remove lnors here!

        # set polygons to use smooth representation
        mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons))

        # finally fill clnors from loops normals and apply them (taken from official Blenders scripts)
        clnors = array.array('f', [0.0] * (len(mesh.loops) * 3))
        mesh.loops.foreach_get("normal", clnors)
        mesh.normals_split_custom_set(tuple(zip(*(iter(clnors), ) * 3)))
        mesh.use_auto_smooth = True

        mesh.free_normals_split()
    else:
        # set polygons to use smooth representation only
        mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons))

    context.window_manager.progress_update(0.6)

    # Create object out of mesh and link it to active layer collection.
    obj = bpy.data.objects.new(mesh.name, mesh)
    obj.scs_props.object_identity = obj.name
    obj.location = (0.0, 0.0, 0.0)
    context.view_layer.active_layer_collection.collection.objects.link(obj)

    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj

    # SCALAR LAYERS
    if mesh_scalars:
        for sca_layer_name in mesh_scalars:
            vertex_group = obj.vertex_groups.new(name=sca_layer_name)
            for val_i, val in enumerate(mesh_scalars[sca_layer_name]):
                val = float(val[0])
                if val != 0.0:
                    vertex_group.add([val_i], val, "ADD")
    context.window_manager.progress_update(0.7)

    # TERRAIN POINTS (VERTEX GROUPS)
    for vertex_i, vertex_pos in enumerate(mesh_vertices):

        tp_entries = terrain_points_trans.get(vertex_pos)

        # add current vertex to all combinations of variants/nodes
        # from found terrain points transitional structures
        for tp_entry in tp_entries:

            # first 6 chars in vertex group name will represent variant index
            # this way we will be able to identify variant during vertex groups
            # cleanup if this vertex will be set to multiple variants
            vg_name = str(tp_entry.variant_i).zfill(
                6) + _OP_consts.TerrainPoints.vg_name_prefix + str(
                    tp_entry.node_i)

            if vg_name not in obj.vertex_groups:
                obj.vertex_groups.new(name=vg_name)

            vertex_group = obj.vertex_groups[vg_name]
            vertex_group.add([vertex_i], 1.0, "REPLACE")

    # SKINNING (VERTEX GROUPS)
    if object_skinning:
        if name in object_skinning:
            for vertex_group_name in object_skinning[name]:
                vertex_group = obj.vertex_groups.new(name=vertex_group_name)
                for vertex_i, vertex in enumerate(
                        object_skinning[name][vertex_group_name]):
                    weight = object_skinning[name][vertex_group_name][vertex]
                    if weight != 0.0:
                        vertex_group.add([vertex], weight, "ADD")
        else:
            lprint('\nE Missing skin group %r! Skipping...', name)

    # ADD EDGE SPLIT MODIFIER
    bpy.ops.object.shade_smooth()
    bpy.ops.object.modifier_add(type='EDGE_SPLIT')
    bpy.context.object.modifiers["EdgeSplit"].use_edge_angle = False
    bpy.context.object.modifiers["EdgeSplit"].name = "ES_" + name

    # MATERIALS
    used_mat_indices = set()
    # print('\n  mesh_face_materials:\n%s' % str(mesh_face_materials))
    for mat_index in mesh_face_materials:
        used_mat_indices.add(mat_index)
    # print('  used_mats:\n%s' % str(used_mats))
    context.window_manager.progress_update(0.8)

    # ADD MATERIALS TO SLOTS
    # print('  materials_data:\n%s' % str(materials_data))
    mat_index_to_mat_slot_map = {}
    if len(materials_data) > 0:
        for used_mat_idx in used_mat_indices:
            material_name = materials_data[used_mat_idx][0]
            bpy.ops.object.material_slot_add()  # Add a material slot
            last_slot = obj.material_slots.__len__() - 1

            # now as we created slot and we know index of it, write down indices of material slots to dictionary
            # for later usage by assigning faces to proper slots
            mat_index_to_mat_slot_map[used_mat_idx] = last_slot

            # print('    used_mat: %s (%i) => %s : %s' % (str(used_mat), mat_i, str(last_slot), str(material)))
            obj.material_slots[last_slot].material = bpy.data.materials[
                material_name]  # Assign a material to the slot

            # NOTE: we are setting texture aliases only first time to avoid duplicates etc.
            # So we assume that pieces which are using same material will also have same uv aliases alignment
            used_material = bpy.data.materials[material_name]
            if "scs_tex_aliases" not in used_material:

                alias_mapping = {}
                for uv_lay in mesh_uv_aliases[used_mat_idx]:

                    import re

                    for alias in mesh_uv_aliases[used_mat_idx][uv_lay]:
                        numbers = re.findall("\d+", alias)
                        number = numbers[len(numbers) - 1]
                        alias_mapping[number] = uv_lay

                used_material["scs_tex_aliases"] = alias_mapping

    mesh = obj.data
    context.window_manager.progress_update(0.9)

    # APPLY MATERIAL SLOT INDICES TO FACES
    for face_i, face in enumerate(mesh.polygons):
        face.material_index = mat_index_to_mat_slot_map[
            mesh_face_materials[face_i]]
    context.window_manager.progress_update(1.0)

    return obj
Beispiel #4
0
def _create_piece(context,
                  preview_model,
                  name,
                  ob_material,
                  mesh_vertices,
                  mesh_normals,
                  mesh_tangents,
                  mesh_rgb,
                  mesh_rgba,
                  mesh_scalars,
                  object_skinning,
                  mesh_uv,
                  mesh_tuv,
                  mesh_triangles,
                  materials_data,
                  points_to_weld_list,
                  terrain_points_trans,
                  ignore_backfaces=False):
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_tangents",
                      mesh_tangents)
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_scalars",
                      mesh_scalars)
    handle_unused_arg(__file__, _create_piece.__name__, "mesh_tuv", mesh_tuv)

    context.window_manager.progress_begin(0.0, 1.0)
    context.window_manager.progress_update(0)

    import_scale = _get_scs_globals().import_scale

    mesh = bpy.data.meshes.new(name)

    # COORDINATES TRANSFORMATION
    transformed_mesh_vertices = [
        _convert_utils.change_to_scs_xyz_coordinates(vec, import_scale)
        for vec in mesh_vertices
    ]

    context.window_manager.progress_update(0.1)

    # VISUALISE IMPORTED NORMALS (DEBUG)
    # visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scale)

    # MESH CREATION
    bm = bmesh.new()

    # VERTICES
    _mesh_utils.bm_make_vertices(bm, transformed_mesh_vertices)
    context.window_manager.progress_update(0.2)

    # FACES
    mesh_triangles, back_triangles = _mesh_utils.bm_make_faces(
        bm, mesh_triangles, points_to_weld_list)
    context.window_manager.progress_update(0.3)

    # UV LAYERS
    if mesh_uv:
        for uv_layer_name in mesh_uv:
            _mesh_utils.bm_make_uv_layer(5, bm, mesh_triangles, uv_layer_name,
                                         mesh_uv[uv_layer_name]["data"])
    context.window_manager.progress_update(0.4)

    # VERTEX COLOR
    if mesh_rgba:
        mesh_rgb_final = mesh_rgba
    elif mesh_rgb:
        mesh_rgb_final = mesh_rgb
    else:
        mesh_rgb_final = []

    for vc_layer_name in mesh_rgb_final:
        max_value = mesh_rgb_final[vc_layer_name][0][0] / 2

        for vc_entry in mesh_rgb_final[vc_layer_name]:
            for i, value in enumerate(vc_entry):
                if max_value < value / 2:
                    max_value = value / 2

        if max_value > mesh.scs_props.vertex_color_multiplier:
            mesh.scs_props.vertex_color_multiplier = max_value

        _mesh_utils.bm_make_vc_layer(5, bm, vc_layer_name,
                                     mesh_rgb_final[vc_layer_name],
                                     mesh.scs_props.vertex_color_multiplier)

    context.window_manager.progress_update(0.5)

    bm.to_mesh(mesh)
    mesh.update()
    bm.free()

    # NORMALS - has to be applied after bmesh creation as they are set directly to mesh
    if _get_scs_globals().import_use_normals:

        mesh.create_normals_split()

        # first set normals directly to loops
        for loop in mesh.loops:
            curr_n = _convert_utils.scs_to_blend_matrix() @ Vector(
                mesh_normals[loop.vertex_index])
            loop.normal[:] = curr_n

        # then we have to go trough very important step they say,
        # as without validation we get wrong result for some normals
        mesh.validate(clean_customdata=False
                      )  # *Very* important to not remove lnors here!

        # set polygons to use smooth representation
        mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons))

        # finally fill clnors from loops normals and apply them (taken from official Blenders scripts)
        clnors = array.array('f', [0.0] * (len(mesh.loops) * 3))
        mesh.loops.foreach_get("normal", clnors)
        mesh.normals_split_custom_set(tuple(zip(*(iter(clnors), ) * 3)))
        mesh.use_auto_smooth = True

        mesh.free_normals_split()
    else:
        # set polygons to use smooth representation only
        mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons))

    context.window_manager.progress_update(0.6)

    # Create object out of mesh and link it to active layer collection.
    obj = bpy.data.objects.new(mesh.name, mesh)
    obj.scs_props.object_identity = obj.name
    obj.location = (0.0, 0.0, 0.0)
    context.view_layer.active_layer_collection.collection.objects.link(obj)

    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj

    context.window_manager.progress_update(0.7)

    context.window_manager.progress_update(0.8)

    # TERRAIN POINTS (VERTEX GROUPS)
    for vertex_i, vertex_pos in enumerate(mesh_vertices):

        tp_entries = terrain_points_trans.get(vertex_pos)

        # add current vertex to all combinations of variants/nodes
        # from found terrain points transitional structures
        for tp_entry in tp_entries:

            # first 6 chars in vertex group name will represent variant index
            # this way we will be able to identify variant during vertex groups
            # cleanup if this vertex will be set to multiple variants
            vg_name = str(tp_entry.variant_i).zfill(
                6) + _OP_consts.TerrainPoints.vg_name_prefix + str(
                    tp_entry.node_i)

            if vg_name not in obj.vertex_groups:
                obj.vertex_groups.new(name=vg_name)

            vertex_group = obj.vertex_groups[vg_name]
            vertex_group.add([vertex_i], 1.0, "REPLACE")

    # SKINNING (VERTEX GROUPS)
    if object_skinning:
        if name in object_skinning:
            for vertex_group_name in object_skinning[name]:
                vertex_group = obj.vertex_groups.new(name=vertex_group_name)
                for vertex_i, vertex in enumerate(
                        object_skinning[name][vertex_group_name]):
                    weight = object_skinning[name][vertex_group_name][vertex]
                    if weight != 0.0:
                        if vertex in points_to_weld_list:
                            vertex = points_to_weld_list[vertex]
                        vertex_group.add([vertex], weight, "ADD")
        else:
            lprint('\nE Missing skin group %r! Skipping...', name)

    context.window_manager.progress_update(0.9)

    # DELETE ORPHAN VERTICES (LEFT IN THE GEOMETRY FROM SMOOTHING RECONSTRUCTION)
    if points_to_weld_list:
        bm = bmesh.new()
        bm.from_mesh(mesh)

        bm.verts.ensure_lookup_table()
        for vert_i in points_to_weld_list.keys():
            bm.verts[vert_i].select_set(True)

        verts = [v for v in bm.verts if v.select]
        if verts:
            bmesh.ops.delete(bm, geom=verts, context='VERTS')

        # APPLYING BMESH TO MESH
        bm.to_mesh(mesh)
        bm.free()

    context.window_manager.progress_update(1.0)

    # MATERIAL
    if len(materials_data) > 0 and not preview_model:
        # Assign a material to the last slot
        used_material = bpy.data.materials[materials_data[ob_material][0]]
        obj.data.materials.append(used_material)

        # NOTE: we are setting texture aliases only first time to avoid duplicates etc.
        # So we assume that pieces which are using same material will also have same  uv aliases alignement
        if "scs_tex_aliases" not in used_material:

            alias_mapping = {}
            for uv_lay in mesh_uv:
                if "aliases" in mesh_uv[uv_lay]:

                    import re

                    for alias in mesh_uv[uv_lay]["aliases"]:
                        numbers = re.findall("\d+", alias)
                        number = numbers[len(numbers) - 1]
                        alias_mapping[number] = uv_lay

            used_material["scs_tex_aliases"] = alias_mapping

    context.window_manager.progress_end()

    # if back triangles are present, then create new object with
    # back triangles and merge it to original
    if len(back_triangles) > 0 and not ignore_backfaces:

        back_obj = _create_piece(context,
                                 preview_model,
                                 "back_" + name,
                                 ob_material,
                                 mesh_vertices,
                                 mesh_normals,
                                 mesh_tangents,
                                 mesh_rgb,
                                 mesh_rgba,
                                 mesh_scalars,
                                 object_skinning,
                                 mesh_uv,
                                 mesh_tuv,
                                 back_triangles,
                                 materials_data,
                                 points_to_weld_list,
                                 terrain_points_trans,
                                 ignore_backfaces=True)

        lprint(
            "W Found %s back face(s) without it's own vertices on object %r, additional vertices were added!",
            (len(back_obj.data.polygons), obj.name))

        # creation of back face object used all original vertices
        # for proper index accessing during binding all of the data blocks to vertices.
        # Because of that we have to remove vertices which are not really used
        # in back faces mesh, so called "loose" vertices
        back_obj.data = _mesh_utils.bm_delete_loose(back_obj.data)

        # finally join back object with original
        override = context.copy()
        override["active_object"] = obj
        override["selected_editable_objects"] = (obj, back_obj)
        bpy.ops.object.join(override)

    return obj