2
$\begingroup$

enter image description here

The image is a work in progress. It consists of a large number of extruded cubes. All the cubes are joined in a single mesh. (It's a mathematical representation of a photograph)

Here's the problem: making this object by using the extrude option turned it into a non manifold nightmare. The base of the cubes form a nice unified mesh. But the tops are not attached to each other. I have included an image to illustrate my point:
enter image description here
In the image I moved the top of one of the cubes to the left. So the cubes are all right next to each other but not correctly joined. (hope it's clear what I am trying to say)

So basically I have 3 questions: - How can I extrude faces without running into this problem?
- Is there a better way to make this object? - Is there an efficient way to fix my current object?

$\endgroup$
  • $\begingroup$ Seems to be the problem here is not extrusion but finding a good way to turn an image into a voxelized mesh where the height of the upper faces is determined by some function of rgb for that pixel. People looking for something along the lines of "3d voxel mesh from image" would not likely click on 'extruding problem'. It seems you could do more with the title.. $\endgroup$ – zeffii Jun 29 '15 at 6:56
2
$\begingroup$

Here's a quick script I wrote to do the same thing:

from collections import defaultdict

import bpy

scene = bpy.context.scene

color_map = defaultdict(list)
filename = "wvb_flat_200_200.png"
img = bpy.data.images[filename]
scale = 0.3


def idx_to_co(idx, width):
    r = int(idx / width)
    c = idx % width
    return r, c

def co_to_idx(r, c, width):
    return r * width + c


def rgba_from_index(idx, pxs):
    start_raw_index = idx * 4
    return pxs[start_raw_index:start_raw_index+4]


def sv_main():

    pixels = img.pixels
    pxs = list(pixels)

    w = width = img.size[0]
    h = height = img.size[1]

    num_pixels = len(pxs)
    gl = grouped_list = [pxs[i:i+4] for i in range(num_pixels)[::4]]

    for c in range(w):
        for r in range(h):
            idx = co_to_idx(r, c, w)
            rgba = rgba_from_index(idx, pxs)
            color_map[tuple(rgba[:3])].append([r*scale, -c*scale, 0.0])


def create_repr_plane(obj_name, mesh_name):

    # here I would consider doing:   (scale / 2) * 0.98
    s = scale/2  
    verts = []
    v_add = verts.extend
    faces = []
    f_add = faces.extend

    def get_verts(x, y, z):
        lowest = -1
        return [
            (s+x, s+y, lowest),
            (s+x, -s+y, lowest),
            (-s+x, -s+y, lowest),
            (-s+x, s+y, lowest),
            (s+x, s+y, z),
            (s+x, -s+y, z),
            (-s+x, -s+y, z),
            (-s+x, s+y, z)
        ]

    def get_faces(o):
        return [
            (0+o, 1+o, 2+o, 3+o),
            (4+o, 7+o, 6+o, 5+o),
            (0+o, 4+o, 5+o, 1+o),
            (1+o, 5+o, 6+o, 2+o),
            (2+o, 6+o, 7+o, 3+o),
            (4+o, 0+o, 3+o, 7+o)
        ]

    # for each color
    total_idx = 0
    for i, (k, v) in enumerate(color_map.items()):

        z = 0.2126 * k[0] + 0.7152 * k[1] + 0.0722 * k[2]
        # for every pixel with this colour
        for j, vtx in enumerate(v):
            x, y = vtx[:2]
            v_add(get_verts(x, y, z))

            f_add(get_faces(total_idx))
            total_idx += 8

    # print(verts, faces)

    profile_mesh = bpy.data.meshes.new(obj_name)
    profile_mesh.from_pydata(verts, [], faces)
    profile_mesh.update()

    profile_object = bpy.data.objects.new(obj_name, profile_mesh)

    scene.objects.link(profile_object)
    return profile_object


sv_main()


obj = create_repr_plane("dupli_object", "dupli_mesh")

It creates cubes in a single mesh and pushes the Z depth down to a constant point, but uses the rgb components and converts them to a greyscale to get the upper point of the cube.

enter image description here

From here remesh modifier isn't really an option as it will be very processor intensive. If you want manifold geometry perhaps make the cubes slightly less wide (x,y) than their spread distance x, y, making the cubes not touch eachother. That might be a simpler solution if this is for 3d printing or milling.. It's going to be a tonne of geometry either way.

$\endgroup$
0
$\begingroup$

There are two basic problems in this question. One is with respect to future episodes, and the second is whether to fix the current episode.

To me, the key to for future workflow lies in not extruding faces, because extruding the face results in the creation of the four faces on each side of each extruded face which descends to the mesh base, and partially duplicates the similar face on the adjoining face. This creates your problem. I submit that the key here is extruding vertices, and making a face after all four of the constituent vertices have been extruded.

Starting with one corner, at that corner you will have one (if the plane that would be extruded happens to have vertices which have the same co-ordinates as the base plane) or two vertices with the same x,y co-ordinates. So you extrude (x,y,z') from (x,y,z) so that z' has the appropriate height value.

Next you move to an adjacent vertex in the base plane. (x',y,z) [or x, y', z]. At this location, there may be zero (if both adjoining planes are at the same z value as the base), one (if both adjoining planes have the same value, but different from the base), or two additional co-ordinate values along the z axis, and if there are two additional values, either may be greater than the other. So extrude from the vertex (x',y, z) along the z axis to reach the lower location with the lower z value of the other two, which will be (x', y, z'), and then extrude again to the other higher z value, (x', y, z''). One of these two extruded z values will be the same height as the z value of the previous base vertex, so create the edge between those two vertices, and create a face between the newly created edge and the edge below it. Repeat this process for each vertex on the edge, until the opposite corner, where there will be only zero or one additional vertices.

Moving to the next row of vertices, (x, y', z), repeat the steps outlined in the previous paragraph for the first vertex in the row, remembering that in this case the first vertex in the row is equivalent to the vertices other than the two corner vertices in the first row. Now when moving to the adjacent vertex along the x axis, (x' y' z), there are four faces which will each have a vertex which maps to the same (x' y') co-ordinates, so there will be as many as four separate vertices to be extruded along the z axis, although it is possible of two or more of the faces happen to be at the same height, there will be fewer than four extruded vertices.

Determine the height for the four extruded vertices, (x', y', z'), (x', y', z''), (x', y', z'''), and (x', y', z''''), sort these four z values, and extrude from the base mesh vertex to the lowest z value. If this z value matches the z value of one of the adjacent vertices already extruded, make the edge. Extrude from the lowest extruded vertex to the other three in order of z value, drawing edges as needed. When an edge is drawn, check to see if that edge completes a face, and make the face as well. Repeat the process outlined in the above paragraph and this one for each successive row of vertices.

The last row of vertices will be like the first row.

I think this will provide you with what your are looking for, and have the beneficial side effect of providing the possibility of providing a very interesting animation to look at, as the process proceeds across the image. Unfortunately, while my analytical skills are up to devising the method, my programming skills are not up to providing the code.

As far as a method to correct your current mesh, this, too, is based upon vertices and edges instead of faces. Again, processing occurs along the vertices of the base mesh. On the perimeter sides of the base mesh, delete all edges where both the ends of the edge lie on the z axis, then construct edges between each two adjacent vertices (adjacent means that given two vertices with co-ordinates (x, y, z) and (x, y, z') there is not vertex with a value between the value of z and the value of z'). The same process is repeated for vertices on the interior of the base mesh (that is vertices which are part of four edges), except that the lowest edge, that the edge between the base mesh and the vertex with the next highest value along the z axis is NOT drawn.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.