parkcheolhee-lab

Voxelate

What is a Voxel ? 🧊

In computer graphics, a voxel, also known as a 3D pixel, is a 3D cube located on a three-dimensional grid used to create 3D models. Similar to a pixel in a 2D image, a voxel can contain a specific location inside a 3D grid and has a color value assigned to it. All individual voxels are the same size, however voxels can then be combined in the same grid at different positions with different colors to create what is known as a voxel model.
What is a voxel ?
From the left, Original shape · Voxelated shape

In addition, a voxel is also used as the smallest unit composing an architectural space. This is because voxels have no deadspace and can be used for all architectural purposes and reasonable. Therefore It's being used in many architectural offices. The process of architecture below shows how to apply voxelization to the building. This article covers automation of voxelization and how change to the voxelated shape when given specific shape. And let's put some information in the voxel.

mvrdv's RED7

3D grid & conditions map

First we should create grids and to create a grid, need a bounding box of given shape. The angle of bounding box sets sun faced like the picture 4 above. Through the segments X and Y of the bounding box facing the sun, we can get a 3D voxel grid of the desired size that fully fills the bounding box. Then we check if the voxel is inside the given shape, filter only the voxel that is inside the shape.

This grid of voxels can be represented as a List, one of Python's data structures. And the data structure List can express multi-dimensional data. Let's take a look at a simple example below.

The given simple conditions are as follows. â‘  voxel size: 2.0 â‘¡ given shape: sphere that radius: 20.0. As mentioned earlier, in the last picture below you can see the voxel data structure represented as a List in Python. Data of 0 means empty voxels and data of 1 means filled voxels.

3D grid & conditions map
From the left, Given sphere · Voxelated sphere · Conditions map(9th voxels grid)


Determining the voxel's condition

Now, we can determine each voxel condition through the data we have. Examples include interior voxels, exterior voxels, corner voxels, and roof voxels etc.


    def __get_voxels_condition_list(self):
        inside_grid_centroid_integer = [int(b) for b in self.inside_grid_centroid]
        voxel_3d_list = self.__get_reshaped_list(inside_grid_centroid_integer, *self.cols)
        
        
        roof_3d_list = self.__get_default_3d_list()
        exterior_3d_list = self.__get_default_3d_list()
        exterior_both_3d_list = self.__get_default_3d_list()
        exterior_corner_3d_list = self.__get_default_3d_list()
        exterior_corner_u_3d_list = self.__get_default_3d_list()
        exterior_corner_o_3d_list = self.__get_default_3d_list()
        
        for zi, z_list in enumerate(voxel_3d_list):
            prev_zi = (zi - 1) % len(voxel_3d_list)
            next_zi = (zi + 1) % len(voxel_3d_list)
            
            for yi, y_list in enumerate(z_list):
                prev_yi = (yi - 1) % len(z_list)
                next_yi = (yi + 1) % len(z_list)
                
                for xi, x in enumerate(y_list):
                    prev_x = bool(y_list[(xi - 1) % len(y_list)])
                    next_x = bool(y_list[(xi + 1) % len(y_list)])
                    back_x = bool(z_list[prev_yi][xi])
                    frnt_x = bool(z_list[next_yi][xi])
                    
                    exterior_count = [prev_x, next_x, back_x, frnt_x].count(False)
                    
                    is_roof = bool(x) and not bool(voxel_3d_list[next_zi][yi][xi])
                    is_exterior_both = bool(x) and (not prev_x and not next_x) or (not back_x and not frnt_x)
                    is_exterior = bool(x) and any([not prev_x, not next_x, not back_x, not frnt_x]) and not is_exterior_both
                    is_exterior_corner = bool(x) and exterior_count == 2 and not is_exterior_both
                    is_exterior_corner_u = bool(x) and exterior_count == 3
                    is_exterior_corner_o = bool(x) and exterior_count == 4
                    
                    if is_roof:
                        roof_3d_list[zi][yi][xi] = 1
                    if is_exterior:
                        exterior_3d_list[zi][yi][xi] = 1
                    if is_exterior_both:
                        exterior_both_3d_list[zi][yi][xi] = 1
                    if is_exterior_corner:
                        exterior_corner_3d_list[zi][yi][xi] = 1
                    if is_exterior_corner_o:
                        exterior_corner_o_3d_list[zi][yi][xi] = 1
                    if is_exterior_corner_u:
                        exterior_corner_u_3d_list[zi][yi][xi] = 1
                        
        interior_3d_list = self.__get_default_3d_list()
        for zi, (z_list, e_list) in enumerate(zip(voxel_3d_list, exterior_3d_list)):
            for yi, (z, e) in enumerate(zip(z_list, e_list)):
                for xi, (zx, ex) in enumerate(zip(z, e)):
                    interior_3d_list[zi][yi][xi] = zx - ex
        
        grid_3d_list = self.__get_reshaped_list(self.grid, *self.cols)
        
        return (
            voxel_3d_list, 
            grid_3d_list, 
            roof_3d_list, 
            exterior_3d_list, 
            exterior_both_3d_list,
            exterior_corner_3d_list, 
            exterior_corner_o_3d_list,
            exterior_corner_u_3d_list,
            interior_3d_list
        )


To determine the ROOF voxel, simply check the condition of the upper voxel. Likewise, to determine the CORNER voxel, simply check previous voxel, next voxel, front voxel, back voxel. The diagram below shows how red voxels are estimated to be CORNER voxels. Please refer to the link above for detailed code related to determining the voxel condition.
Determining the voxel's condition
From the left, Target voxel · Check surround voxel conditions

Voxel unit matching by condition

Several predefined VoxelUnits can be matched through the condition of each voxel determined based on the conditions map. You can design and apply a unit suitable for the desired voxel size, and if you wish, you can resize the voxel size and apply it. And for voxel matching, it also supports rotation function by calculating the angle of the voxel.
VoxelUnits
From the left, Exterior · Exterior both · Exterior corner · Exterior corner O · Exterior corner U



    class VoxelUnits:
        def __init__(self, exterior_unit, exterior_both_unit, exterior_corner_unit, exterior_corner_o_unit, exterior_corner_u_unit):
            self.unit_bounding_box = gh.BoundingBox(exterior_unit, WORLD_XY).box
            self.exterior_unit_centroid = gh.Volume(self.unit_bounding_box).centroid
            
            self.voxel_face_edges = gh.DeconstructBrep(self.unit_bounding_box.ToBrep().Faces[0].ToBrep()).edges
            self.scale_factor = self.voxel_size / self.voxel_face_edges[0].GetLength()
            
            self.rotated_exterior_unit = self.__get_scaled_unit(exterior_unit)
            self.rotated_exterior_both_unit = self.__get_scaled_unit(exterior_both_unit)
            self.rotated_exterior_corner_unit = self.__get_scaled_unit(exterior_corner_unit)
            self.rotated_exterior_corner_u_unit = self.__get_scaled_unit(exterior_corner_u_unit)
            self.rotated_exterior_corner_o_unit = self.__get_scaled_unit(exterior_corner_o_unit)
            
        def __get_joined_mesh(self, unit):
            return gh.MeshJoin(rg.Mesh.CreateFromBrep(unit, rg.MeshingParameters(0)))
            
        def __get_rotated_unit(self, unit):
            return gh.Rotate(
                self.__get_joined_mesh(unit),
                self.converted_sun_facing_angle, 
                self.exterior_unit_centroid
            ).geometry
            
        def __get_scaled_unit(self, unit):
            return gh.Scale(
                self.__get_rotated_unit(unit),
                self.exterior_unit_centroid,
                self.scale_factor
            ).geometry if self.is_needed_resize else self.__get_rotated_unit(unit)


Finally, I'll wrap up the article by showing some examples of each of the above VoxelUnits applied. See the link for details.

RED7
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape


79&PARK
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape


KING TORONTO
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape


VANCOUVER HOUSE
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape