Beispiel #1
0
def transform_mesh(mesh: bpy.types.Mesh, matrix: mathutils.Matrix):
    # There is a disparity in terms of how negative scaling is displayed in Blender versus how it is
    # applied (Ctrl+A) in that the normals are different. Even though negative scaling is evil, we
    # prefer to match the visual behavior, not the non-intuitive apply behavior. So, we'll need to
    # flip the normals if the scaling is negative. The Blender documentation even "helpfully" warns
    # us about this.
    mesh.transform(matrix)
    if matrix.is_negative:
        mesh.flip_normals()
Beispiel #2
0
def delete_mesh(mesh: bpy.types.Mesh, clear_users=True):
    if mesh.users > 0:
        print(
            f"Not deleting mesh '{mesh.name}' because it still has "
            f"{mesh.users} users."
        )
        return False
    try:
        if clear_users:
            mesh.user_clear()
        bpy.data.meshes.remove(mesh)
        return True
    except Exception as e:
        raise Warning(f"Mesh '{mesh.name}' not deleted because of this "
                      f"exception: {e}")
        return False
Beispiel #3
0
def gen_mesh_wall(context: bpy.types.Context, wall_loops: list,
                  section_mesh: bpy.types.Mesh) -> bpy.types.Object:
    """
    Creates the wall object
    All walls will be generated, and there is no need to duplicate/move them
    Args:
        context: bpy.types.Context
        wall_loops: list(list(tuple(x,y,z))) - list of wall loops, result of gen_layout.
        section_mesh: cross section/side profile of the wall
    Returns:
        The wall object
    """

    bm = bmesh.new()
    for loop in wall_loops:
        mesh = Utils.extrude_along_edges(section_mesh.copy(), loop, False)
        bm.from_mesh(mesh)
    # end for

    # check if the object for walls already exists
    obj = bpy.data.objects.get("PBGWalls")
    if obj is not None:
        context.scene.objects.unlink(obj)
        bpy.data.objects.remove(obj)
    # end if

    m = bpy.data.meshes.new("PBGWall")
    bm.to_mesh(m)
    bm.free()

    # link the created object to the scene
    obj = bpy.data.objects.new("PBGWalls", m)
    context.scene.objects.link(obj)
    return obj
Beispiel #4
0
def update_mesh_data(mesh: bpy.types.Mesh):
    global mario_geo
    vcol = mesh.vertex_colors.active
    for i in range(mario_geo.numTrianglesUsed):
        mesh.vertices[3 * i +
                      0].co.x = mario_geo.position_data[9 * i +
                                                        0] / SM64_SCALE_FACTOR
        mesh.vertices[3 * i +
                      0].co.z = mario_geo.position_data[9 * i +
                                                        1] / SM64_SCALE_FACTOR
        mesh.vertices[
            3 * i +
            0].co.y = -mario_geo.position_data[9 * i + 2] / SM64_SCALE_FACTOR
        mesh.vertices[3 * i +
                      1].co.x = mario_geo.position_data[9 * i +
                                                        3] / SM64_SCALE_FACTOR
        mesh.vertices[3 * i +
                      1].co.z = mario_geo.position_data[9 * i +
                                                        4] / SM64_SCALE_FACTOR
        mesh.vertices[
            3 * i +
            1].co.y = -mario_geo.position_data[9 * i + 5] / SM64_SCALE_FACTOR
        mesh.vertices[3 * i +
                      2].co.x = mario_geo.position_data[9 * i +
                                                        6] / SM64_SCALE_FACTOR
        mesh.vertices[3 * i +
                      2].co.z = mario_geo.position_data[9 * i +
                                                        7] / SM64_SCALE_FACTOR
        mesh.vertices[
            3 * i +
            2].co.y = -mario_geo.position_data[9 * i + 8] / SM64_SCALE_FACTOR
        mesh.uv_layers.active.data[mesh.loops[3 * i + 0].index].uv = (
            mario_geo.uv_data[6 * i + 0], mario_geo.uv_data[6 * i + 1])
        mesh.uv_layers.active.data[mesh.loops[3 * i + 1].index].uv = (
            mario_geo.uv_data[6 * i + 2], mario_geo.uv_data[6 * i + 3])
        mesh.uv_layers.active.data[mesh.loops[3 * i + 2].index].uv = (
            mario_geo.uv_data[6 * i + 4], mario_geo.uv_data[6 * i + 5])
        vcol.data[3 * i + 0].color = (mario_geo.color_data[9 * i + 0],
                                      mario_geo.color_data[9 * i + 1],
                                      mario_geo.color_data[9 * i + 2], 1.0)
        vcol.data[3 * i + 1].color = (mario_geo.color_data[9 * i + 3],
                                      mario_geo.color_data[9 * i + 4],
                                      mario_geo.color_data[9 * i + 5], 1.0)
        vcol.data[3 * i + 2].color = (mario_geo.color_data[9 * i + 6],
                                      mario_geo.color_data[9 * i + 7],
                                      mario_geo.color_data[9 * i + 8], 1.0)
    mesh.update()
Beispiel #5
0
    def export_mesh(self, mesh: bpy.types.Mesh, vertex_groups: List[bpy.types.VertexGroup], bone_names: List[str])->MeshStore:

        def get_texture_layer(layers):
            for l in layers:
                if l.active:
                    return l

        mesh.update(calc_tessface=True)
        uv_texture_faces = get_texture_layer(mesh.tessface_uv_textures)
        store = MeshStore(mesh.name, mesh.vertices,
                          mesh.materials, vertex_groups, bone_names)
        for i, face in enumerate(mesh.tessfaces):
            submesh = store.get_or_create_submesh(face.material_index)
            for triangle in store.add_face(face, uv_texture_faces.data[i] if uv_texture_faces else None):
                for fv in triangle:
                    submesh.indices.append(fv)

        self.mesh_stores.append(store)
        return store
Beispiel #6
0
    def __init__(self, mesh: bpy.types.Mesh, o2w: mathutils.Matrix):
        self.name = mesh.name
        self.bbox = BBox()
        self.o2p = Mat4()
        self.verts = []
        self.colrs = []
        self.norms = []
        self.tex0s = []
        self.nuggets = []

        # TODO
        # Handle nested meshes
        # Handle normal generation

        # If the mesh has no geometry...
        if not mesh.polygons:
            return

        # Determine the geometry format
        geom = EGeom.Vert | EGeom.Norm
        geom = (geom | EGeom.Colr) if len(mesh.vertex_colors) != 0 else geom
        geom = (geom
                | EGeom.Tex0) if mesh.uv_layers.get("UVMap") != None else geom

        # Initialise the verts lists
        self.verts = [Vec3] * len(mesh.vertices)
        self.norms = [Vec3] * len(mesh.vertices)
        for i, v in enumerate(mesh.vertices):
            self.verts[i] = self.bbox.encompass(Vec3(v.co.x, v.co.y, v.co.z))
            self.norms[i] = Vec3(v.normal.x, v.normal.y, v.normal.z)

        # Initialise the colours
        if geom & EGeom.Colr:
            vertex_colors_layer0 = mesh.vertex_colors[0].data
            self.colrs = [Col4] * len(vertex_colors_layer0)
            for i, c in enumerate(vertex_colors_layer0):
                self.colrs[i] = Col4(c[0], c[1], c[2], c[3])

        # Initialise the UVs
        if geom & EGeom.Tex0:
            uvs = mesh.uv_layers.get("UVMap").data
            self.tex0s = [Vec2] * len(uvs)
            for i, t in enumerate(uvs):
                self.tex0s[i] = Vec2(t.uv.x, t.uv.y)

        # Set the stride based on the number of verts
        stride = 2 if len(self.verts) < 0x10000 else 4

        # Ensure the polygons have been triangulated
        if not mesh.loop_triangles:
            mesh.calc_loop_triangles()

        # Partition the triangles by material index
        nuggets = {}
        for tri in mesh.loop_triangles:
            if not tri.material_index in nuggets:
                nuggets[tri.material_index] = []
            nuggets[tri.material_index].extend(tri.vertices)

        # Create nuggets for each material
        for mat_idx, vidx in nuggets.items():
            if len(vidx) == 0: continue

            # Find the material, fallback to a default empty material
            mat = mesh.materials[mat_idx] if mat_idx < len(
                mesh.materials) else {
                    "name": "null"
                }  # bpy.data.materials[0]

            # Save a nugget for the geometry that uses this material
            nugget = Nugget(topo=ETopo.TriList,
                            geom=geom,
                            mat=mat.name,
                            stride=stride,
                            vidx=vidx)
            self.nuggets.append(nugget)

        # Object transform
        self.o2p = Mat4(
            Vec3(o2w[0][0], o2w[0][1], o2w[0][2]),
            Vec3(o2w[1][0], o2w[1][1], o2w[1][2]),
            Vec3(o2w[2][0], o2w[2][1], o2w[2][2]),
            Vec3(o2w.translation[0], o2w.translation[1], o2w.translation[2]))
        return
def create_mesh_geometry(mesh: bpy.types.Mesh) -> List[GeometrySegment]:
    """ Creates a list of GeometrySegment objects from a Blender mesh.
        Does NOT create triangle strips in the GeometrySegment however. """

    if mesh.has_custom_normals:
        mesh.calc_normals_split()

    mesh.validate_material_indices()
    mesh.calc_loop_triangles()

    material_count = max(len(mesh.materials), 1)

    segments: List[GeometrySegment] = [
        GeometrySegment() for i in range(material_count)
    ]
    vertex_cache: List[Dict[Tuple[float],
                            int]] = [dict() for i in range(material_count)]
    vertex_remap: List[Dict[Tuple[int, int],
                            int]] = [dict() for i in range(material_count)]
    polygons: List[Set[int]] = [set() for i in range(material_count)]

    if mesh.vertex_colors.active is not None:
        for segment in segments:
            segment.colors = []

    for segment, material in zip(segments, mesh.materials):
        segment.material_name = material.name

    def add_vertex(material_index: int, vertex_index: int, loop_index: int,
                   use_smooth_normal: bool, face_normal: Vector) -> int:
        nonlocal segments, vertex_remap

        vertex_cache_miss_index = -1
        segment = segments[material_index]
        cache = vertex_cache[material_index]
        remap = vertex_remap[material_index]

        vertex_normal: Vector

        if use_smooth_normal or mesh.use_auto_smooth:
            if mesh.has_custom_normals:
                vertex_normal = mesh.loops[loop_index].normal
            else:
                vertex_normal = mesh.vertices[vertex_index].normal
        else:
            vertex_normal = face_normal

        def get_cache_vertex():
            yield mesh.vertices[vertex_index].co.x
            yield mesh.vertices[vertex_index].co.y
            yield mesh.vertices[vertex_index].co.z

            yield vertex_normal.x
            yield vertex_normal.y
            yield vertex_normal.z

            if mesh.uv_layers.active is not None:
                yield mesh.uv_layers.active.data[loop_index].uv.x
                yield mesh.uv_layers.active.data[loop_index].uv.y

            if segment.colors is not None:
                for v in mesh.vertex_colors.active.data[loop_index].color:
                    yield v

        vertex_cache_entry = tuple(get_cache_vertex())
        cached_vertex_index = cache.get(vertex_cache_entry,
                                        vertex_cache_miss_index)

        if cached_vertex_index != vertex_cache_miss_index:
            remap[(vertex_index, loop_index)] = cached_vertex_index

            return cached_vertex_index

        new_index: int = len(segment.positions)
        cache[vertex_cache_entry] = new_index
        remap[(vertex_index, loop_index)] = new_index

        segment.positions.append(
            convert_vector_space(mesh.vertices[vertex_index].co))
        segment.normals.append(convert_vector_space(vertex_normal))

        if mesh.uv_layers.active is None:
            segment.texcoords.append(Vector((0.0, 0.0)))
        else:
            segment.texcoords.append(
                mesh.uv_layers.active.data[loop_index].uv.copy())

        if segment.colors is not None:
            segment.colors.append(
                list(mesh.vertex_colors.active.data[loop_index].color))

        return new_index

    for tri in mesh.loop_triangles:
        polygons[tri.material_index].add(tri.polygon_index)
        segments[tri.material_index].triangles.append([
            add_vertex(tri.material_index, tri.vertices[0], tri.loops[0],
                       tri.use_smooth, tri.normal),
            add_vertex(tri.material_index, tri.vertices[1], tri.loops[1],
                       tri.use_smooth, tri.normal),
            add_vertex(tri.material_index, tri.vertices[2], tri.loops[2],
                       tri.use_smooth, tri.normal)
        ])

    for segment, remap, polys in zip(segments, vertex_remap, polygons):
        for poly_index in polys:
            poly = mesh.polygons[poly_index]

            segment.polygons.append([
                remap[(v, l)] for v, l in zip(poly.vertices, poly.loop_indices)
            ])

    return segments
    def init_from_mesh(mesh: bpy.types.Mesh, calc_area=False, obj=None):
        """ Returns MeshData from bpy.types.Mesh """

        # Looks more like Blender's bug that we have to check that mesh has calc_normals_split().
        # It is possible after deleting corresponded object with such mesh from the scene.
        if not hasattr(mesh, 'calc_normals_split'):
            log.warn("No calc_normals_split() in mesh", mesh)
            return None

        # preparing mesh to export
        mesh.calc_normals_split()
        mesh.calc_loop_triangles()

        # getting mesh export data
        tris_len = len(mesh.loop_triangles)
        if tris_len == 0:
            return None

        data = MeshData()
        data.vertices = get_data_from_collection(mesh.vertices, 'co',
                                                 (len(mesh.vertices), 3))

        len_loop_triangles = len(mesh.loop_triangles)
        data.normals = get_data_from_collection(mesh.loop_triangles,
                                                'split_normals',
                                                (len_loop_triangles * 3, 3))

        data.uvs = []
        data.uv_indices = []

        primary_uv = mesh.rpr.primary_uv_layer
        if primary_uv:
            uvs = get_data_from_collection(primary_uv.data, 'uv',
                                           (len(primary_uv.data), 2))
            uv_indices = get_data_from_collection(mesh.loop_triangles, 'loops',
                                                  (len_loop_triangles * 3, ),
                                                  np.int32)

            if len(uvs) > 0:
                data.uvs.append(uvs)
                data.uv_indices.append(uv_indices)

            secondary_uv = mesh.rpr.secondary_uv_layer(obj)
            if secondary_uv:
                uvs = get_data_from_collection(secondary_uv.data, 'uv',
                                               (len(secondary_uv.data), 2))
                if len(uvs) > 0:
                    data.uvs.append(uvs)
                    data.uv_indices.append(uv_indices)

        data.num_face_vertices = np.full((tris_len, ), 3, dtype=np.int32)
        data.vertex_indices = get_data_from_collection(
            mesh.loop_triangles, 'vertices', (len_loop_triangles * 3, ),
            np.int32)
        data.normal_indices = np.arange(tris_len * 3, dtype=np.int32)

        if calc_area:
            data.area = sum(tri.area for tri in mesh.loop_triangles)

        # set active vertex color map
        if mesh.vertex_colors.active:
            color_data = mesh.vertex_colors.active.data
            # getting vertex colors and its indices (the same as uv_indices)
            colors = get_data_from_collection(color_data, 'color',
                                              (len(color_data), 4))
            color_indices = data.uv_indices[0] if (data.uv_indices is not None and len(data.uv_indices) > 0) else \
                get_data_from_collection(mesh.loop_triangles, 'loops',
                                         (len_loop_triangles * 3,), np.int32)

            # preparing vertex_color buffer with the same size as vertices and
            # setting its data by indices from vertex colors
            if colors[color_indices].size > 0:
                data.vertex_colors = np.zeros((len(data.vertices), 4),
                                              dtype=np.float32)
                data.vertex_colors[data.vertex_indices] = colors[color_indices]

        return data
Beispiel #9
0
def decode_base_mesh(client, obj: bpy.types.Object, mesh: bpy.types.Mesh, data,
                     index):
    bm = bmesh.new()

    position_count, index = common.decode_int(data, index)
    logger.debug("Reading %d vertices", position_count)

    for _pos_idx in range(position_count):
        co, index = common.decode_vector3(data, index)
        bm.verts.new(co)

    bm.verts.ensure_lookup_table()

    index = decode_bmesh_layer(data, index, bm.verts.layers.bevel_weight,
                               bm.verts, decode_layer_float)

    edge_count, index = common.decode_int(data, index)
    logger.debug("Reading %d edges", edge_count)

    edges_data = struct.unpack(f"{edge_count * 4}I",
                               data[index:index + edge_count * 4 * 4])
    index += edge_count * 4 * 4

    for edge_idx in range(edge_count):
        v1 = edges_data[edge_idx * 4]
        v2 = edges_data[edge_idx * 4 + 1]
        edge = bm.edges.new((bm.verts[v1], bm.verts[v2]))
        edge.smooth = bool(edges_data[edge_idx * 4 + 2])
        edge.seam = bool(edges_data[edge_idx * 4 + 3])

    index = decode_bmesh_layer(data, index, bm.edges.layers.bevel_weight,
                               bm.edges, decode_layer_float)
    index = decode_bmesh_layer(data, index, bm.edges.layers.crease, bm.edges,
                               decode_layer_float)

    face_count, index = common.decode_int(data, index)
    logger.debug("Reading %d faces", face_count)

    for _face_idx in range(face_count):
        material_idx, index = common.decode_int(data, index)
        smooth, index = common.decode_bool(data, index)
        vert_count, index = common.decode_int(data, index)
        face_vertices = struct.unpack(f"{vert_count}I",
                                      data[index:index + vert_count * 4])
        index += vert_count * 4
        verts = [bm.verts[i] for i in face_vertices]
        face = bm.faces.new(verts)
        face.material_index = material_idx
        face.smooth = smooth

    index = decode_bmesh_layer(data, index, bm.faces.layers.face_map, bm.faces,
                               decode_layer_int)

    index = decode_bmesh_layer(data, index, bm.loops.layers.uv,
                               loops_iterator(bm), decode_layer_uv)
    index = decode_bmesh_layer(data, index, bm.loops.layers.color,
                               loops_iterator(bm), decode_layer_color)

    bm.normal_update()
    bm.to_mesh(mesh)
    bm.free()

    # Load shape keys
    shape_keys_count, index = common.decode_int(data, index)
    obj.shape_key_clear()
    if shape_keys_count > 0:
        logger.debug("Loading %d shape keys", shape_keys_count)
        shapes_keys_list = []
        for _i in range(shape_keys_count):
            shape_key_name, index = common.decode_string(data, index)
            shapes_keys_list.append(obj.shape_key_add(name=shape_key_name))
        for i in range(shape_keys_count):
            shapes_keys_list[i].vertex_group, index = common.decode_string(
                data, index)
        for i in range(shape_keys_count):
            relative_key_name, index = common.decode_string(data, index)
            shapes_keys_list[i].relative_key = obj.data.shape_keys.key_blocks[
                relative_key_name]

        for i in range(shape_keys_count):
            shape_key = shapes_keys_list[i]
            shape_key.mute, index = common.decode_bool(data, index)
            shape_key.value, index = common.decode_float(data, index)
            shape_key.slider_min, index = common.decode_float(data, index)
            shape_key.slider_max, index = common.decode_float(data, index)
            shape_key_data_size, index = common.decode_int(data, index)
            for i in range(shape_key_data_size):
                shape_key.data[i].co = Vector(
                    struct.unpack("3f", data[index:index + 3 * 4]))
                index += 3 * 4
        obj.data.shape_keys.use_relative, index = common.decode_bool(
            data, index)

    # Vertex Groups
    vg_count, index = common.decode_int(data, index)
    obj.vertex_groups.clear()
    for _i in range(vg_count):
        vg_name, index = common.decode_string(data, index)
        vertex_group = obj.vertex_groups.new(name=vg_name)
        vertex_group.lock_weight, index = common.decode_bool(data, index)
        vg_size, index = common.decode_int(data, index)
        for _elmt_idx in range(vg_size):
            vert_idx, index = common.decode_int(data, index)
            weight, index = common.decode_float(data, index)
            vertex_group.add([vert_idx], weight, "REPLACE")

    # Normals
    mesh.use_auto_smooth, index = common.decode_bool(data, index)
    mesh.auto_smooth_angle, index = common.decode_float(data, index)

    has_custom_normal, index = common.decode_bool(data, index)

    if has_custom_normal:
        normals = []
        for _loop in mesh.loops:
            normal, index = common.decode_vector3(data, index)
            normals.append(normal)
        mesh.normals_split_custom_set(normals)

    # UV Maps and Vertex Colors are added automatically based on layers in the bmesh
    # We just need to update their name and active_render state:

    # UV Maps
    for uv_layer in mesh.uv_layers:
        uv_layer.name, index = common.decode_string(data, index)
        uv_layer.active_render, index = common.decode_bool(data, index)

    # Vertex Colors
    for vertex_colors in mesh.vertex_colors:
        vertex_colors.name, index = common.decode_string(data, index)
        vertex_colors.active_render, index = common.decode_bool(data, index)

    return index