Beispiel #1
0
    def marchingCubes(cls,
                      voxel_model: VoxelModel,
                      smooth: bool = False,
                      color: Tuple[float, float, float,
                                   float] = (0.8, 0.8, 0.8, 1)):
        """
        Generate a mesh object from a VoxelModel object using a marching cubes algorithm.

        This meshing approach is best suited to high resolution models where some smoothing is acceptable.

        Args:
            voxel_model: VoxelModel object to be converted to a mesh
            smooth: Enable smoothing
            color: Mesh color in the format (r, g, b, a)
        
        Returns:
            None
        """
        voxel_model_fit = voxel_model.fitWorkspace().getOccupied()
        voxels = voxel_model_fit.voxels.astype(np.uint16)
        x, y, z = voxels.shape
        model_offsets = voxel_model_fit.coords

        voxels_padded = np.zeros((x + 2, y + 2, z + 2))
        voxels_padded[1:-1, 1:-1, 1:-1] = voxels

        if smooth:
            voxels_padded = mcubes.smooth(voxels_padded)
            levelset = 0
        else:
            levelset = 0.5

        verts, tris = mcubes.marching_cubes(voxels_padded, levelset)

        # Shift model to align with origin
        verts = np.subtract(verts, 0.5)
        verts[:, 0] = np.add(verts[:, 0], model_offsets[0])
        verts[:, 1] = np.add(verts[:, 1], model_offsets[1])
        verts[:, 2] = np.add(verts[:, 2], model_offsets[2])

        verts_colors = generateColors(len(verts), color)

        return cls(voxels_padded, verts, verts_colors, tris,
                   voxel_model.resolution)
Beispiel #2
0
    def simpleSquares(cls,
                      voxel_model: VoxelModel,
                      color: Tuple[float, float, float, float] = None):
        """
        Generate a mesh object from a VoxelModel object using large square faces.

        This function can greatly reduce the file size of generated meshes. However, it may not correctly recognize
        small (1 voxel) model features and currently produces files with a nonstandard vertex arrangement. Use at your own
        risk.

        ----

        Example:

        ``mesh1 = vf.Mesh.simpleSquares(model1)``

        ----

        Args:
            voxel_model: VoxelModel object to be converted to a mesh
            color: Mesh color in the format (r, g, b, a), None to use voxel colors
        
        Returns:
            Mesh
        """
        voxel_model_fit = voxel_model.fitWorkspace()
        voxel_model_array = voxel_model_fit.voxels.astype(np.uint16)
        model_materials = voxel_model_fit.materials
        model_offsets = voxel_model_fit.coords

        x_len, y_len, z_len = voxel_model_array.shape

        # Determine vertex types
        vert_type = np.zeros((x_len + 1, y_len + 1, z_len + 1), dtype=np.uint8)
        vert_color = np.zeros((x_len + 1, y_len + 1, z_len + 1, 4),
                              dtype=np.float32)
        for x in tqdm(range(x_len), desc='Finding voxel vertices'):
            for y in range(y_len):
                for z in range(z_len):
                    if voxel_model_array[x, y, z] > 0:
                        vert_type[x:x + 2, y:y + 2,
                                  z:z + 2] = 1  # Type 1 = occupied/exterior

                        if color is None:
                            r = 0
                            g = 0
                            b = 0

                            for i in range(voxel_model.materials.shape[1] - 1):
                                r = r + model_materials[voxel_model_array[
                                    x, y,
                                    z]][i + 1] * material_properties[i]['r']
                                g = g + model_materials[voxel_model_array[
                                    x, y,
                                    z]][i + 1] * material_properties[i]['g']
                                b = b + model_materials[voxel_model_array[
                                    x, y,
                                    z]][i + 1] * material_properties[i]['b']

                            r = 1 if r > 1 else r
                            g = 1 if g > 1 else g
                            b = 1 if b > 1 else b

                            a = 1 - model_materials[voxel_model_array[x, y,
                                                                      z]][1]

                            voxel_color = np.array([r, g, b, a])
                        else:
                            voxel_color = np.array(color)

                        for cx in range(x, x + 2):
                            for cy in range(y, y + 2):
                                for cz in range(z, z + 2):
                                    vert_color[cx, cy, cz, :] = voxel_color

        for x in tqdm(range(1, x_len), desc='Finding interior vertices'):
            for y in range(1, y_len):
                for z in range(1, z_len):
                    vert_type = markInterior(vert_type, x, y, z)

        for x in tqdm(range(0, x_len + 1), desc='Finding feature vertices'):
            for y in range(0, y_len + 1):
                for z in range(0, z_len + 1):
                    vert_type = markInsideCorner(vert_type, x, y, z)

        # Initialize arrays
        vi = 0  # Tracks current vertex index
        verts = []
        colors = []
        tris = []
        quads = []
        vert_index = np.multiply(np.ones_like(vert_type, dtype=np.int32), -1)

        for x in tqdm(range(x_len + 1), desc='Meshing'):
            for y in range(y_len + 1):
                for z in range(z_len + 1):
                    dirs = [[1, 1, 0], [1, 0, 1], [0, 1, 1]]
                    for d in dirs:
                        vi, vert_type, vert_index, new_verts, new_colors, new_tris, new_quads = findSquare(
                            vi, vert_type, vert_index, vert_color,
                            voxel_model_array, x, y, z, d[0], d[1], d[2])
                        verts += new_verts
                        colors += new_colors
                        tris += new_tris
                        quads += new_quads

        verts = np.array(verts, dtype=np.float32)
        colors = np.array(colors, dtype=np.float32)
        tris = np.array(tris, dtype=np.uint32)
        quads = np.array(quads, dtype=np.uint32)

        # Shift model to align with origin
        verts[:, 0] = np.add(verts[:, 0], model_offsets[0])
        verts[:, 1] = np.add(verts[:, 1], model_offsets[1])
        verts[:, 2] = np.add(verts[:, 2], model_offsets[2])

        return cls(voxel_model_array, verts, colors, tris,
                   voxel_model.resolution)
Beispiel #3
0
    def fromVoxelModel(cls,
                       voxel_model: VoxelModel,
                       color: Tuple[float, float, float, float] = None):
        """
        Generate a mesh object from a VoxelModel object.

        ----

        Example:

        ``mesh1 = vf.Mesh.fromVoxelModel(model1)``

        ----

        Args:
            voxel_model: VoxelModel object to be converted to a mesh
            color: Mesh color in the format (r, g, b, a), None to use voxel colors
        
        Returns:
            Mesh
        """
        voxel_model_fit = voxel_model.fitWorkspace()
        voxel_model_array = voxel_model_fit.voxels.astype(np.uint16)
        model_materials = voxel_model_fit.materials
        model_offsets = voxel_model_fit.coords

        # Find exterior voxels
        exterior_voxels_array = voxel_model_fit.difference(
            voxel_model_fit.erode(radius=1, connectivity=1)).voxels

        x_len, y_len, z_len = voxel_model_array.shape

        # Create list of exterior voxel coordinates
        exterior_voxels_coords = []
        for x in tqdm(range(x_len), desc='Finding exterior voxels'):
            for y in range(y_len):
                for z in range(z_len):
                    if exterior_voxels_array[x, y, z] != 0:
                        exterior_voxels_coords.append([x, y, z])

        # Get voxel array
        voxel_model_array[voxel_model_array < 0] = 0

        # Initialize arrays
        verts = []
        verts_colors = []
        verts_indices = np.zeros((x_len + 1, y_len + 1, z_len + 1))
        tris = []
        vi = 1  # Tracks current vertex index

        # Loop through voxel_model_array data
        for voxel_coords in tqdm(exterior_voxels_coords, desc='Meshing'):
            x, y, z = voxel_coords

            if color is None:
                r = 0
                g = 0
                b = 0

                for i in range(voxel_model.materials.shape[1] - 1):
                    r = r + model_materials[voxel_model_array[x, y, z]][
                        i + 1] * material_properties[i]['r']
                    g = g + model_materials[voxel_model_array[x, y, z]][
                        i + 1] * material_properties[i]['g']
                    b = b + model_materials[voxel_model_array[x, y, z]][
                        i + 1] * material_properties[i]['b']

                r = 1 if r > 1 else r
                g = 1 if g > 1 else g
                b = 1 if b > 1 else b

                a = 1 - model_materials[voxel_model_array[x, y, z]][1]

                voxel_color = [r, g, b, a]
            else:
                voxel_color = list(color)

            # Add cube vertices
            new_verts, verts_indices, new_tris, vi = addVerticesAndTriangles(
                voxel_model_array, verts_indices, model_offsets, x, y, z, vi)
            verts += new_verts
            tris += new_tris

            # Apply color to all vertices
            for i in range(len(new_verts)):
                verts_colors.append(voxel_color)

        verts = np.array(verts, dtype=np.float32)
        verts_colors = np.array(verts_colors, dtype=np.float32)
        tris = np.array(tris, dtype=np.uint32)

        return cls(voxel_model_array, verts, verts_colors, tris,
                   voxel_model.resolution)