Subscribe to our free newsletter

To make sure you won't miss any valuable content we share with our community.

Different Kinds of Lattice Structure Using Blender

In this article, we are going to directly use the Blender modifiers to create an aesthetic costume lattice structure in Blender. But, as we have said before, the purpose of creating these kinds of lattice structures is art and aesthetics, because the end result is more stochastic rather than deterministic. We start with adding a simple shape like a cube, cylinder, or cone and use modifiers to change the formation of the mesh, then use the Wireframe modifier to make a lattice form out of our mesh, after that, by using other modifiers like Multiresolution or smooth, we post-process our design.

Using Blender Modifiers to Create A Lattice Structure

In this 3rd part of our tutorial, we are going to directly use the Blender modifiers to create an aesthetic costume lattice structure but as we have said before the purpose of creating this kind of lattice structure is art and aesthetics, because the end result is more stochastic rather than deterministic.

blender

lattice structure

We start with adding a simple shape like a cube, cylinder, or cone and use modifiers to change the formation of the mesh, then use the Wireframe modifier to make a lattice form out of our mesh, after that, by using other modifiers like Multiresolution or smooth, we post-process our design. With that said, let’s get started:
    1. creating a cone:
Click on add button, on top of the page and go to mesh >> cone. And you can see that a cone like below is created:

lattice

    2. Modifying the object:
In the modifiers section, choose the multiresolution modifier and by using Multiresulotion >> Subdivide, you can subdivide (increase) the number of meshes on the object like the below photo:

lattice in blender

    3. Scaling:
Now, scale the the modified cone in x-y direction To get a shape like below:

blender

    4. Decreasing the number of meshes:
In the modifiers section, select the Decimate modifier and by using the Decimate >> Unsubdivide, decrease the number of meshes:

blender

    5. Creating a lattice form:
In the modifiers section, find the wireframe and use it to create lattice form of the object like below:

blender

    6. Finding the right thickness:
Change the thickness of the wireframe according to the size of the object. This part is a little bit tricky because if they are too thin, the wireframe will shrink at the time of smoothing and if they are too thick, they may not create a nice structure or holes may shrink instead of the wireframes.

blender

    7. Smoothing the object:
Use the Multiresolution modifier once again to make the object smoother:

lattice structure

    8. Final scaling:
Scale the object according to your design preferences.

lattice structure in blender

Notice that the procedure of designing the lattice structure is not the same for all the objects. It is very optional and based on your taste which method to choose.

In the next part, we are going to create a lattice structure using Blender for a different object with a few differences in the procedure.

Tricky Parts of Designing Lattice Structures in Blender

In this section, we are going to directly use the Blender modifiers to create aesthetic costume lattice structures but as we have said before the purpose of creating this kind of lattice structure is art and aesthetics, because the end result is more stochastic rather than deterministic. The main focus of this section is to teach you the tricky parts of designing these structures.

Mesh structures:

The most important part of designing every lattice structure is the mesh formation and there are many ways to change the way the meshes are formed. Different mesh formations are as follows:
  1. Triangular mesh
  2. Voxel mesh
  3. Quadrimesh
  4. Blocks
  5. Smooth
And so on.

The trickiest part about designing lattice structures is finding the best option for forming the meshes.

blender

Lattice Structure for A Clyinder

The next design we are going to work on is the lattice structure for a cylinder. The recommended steps are as follows:
    1. Creating a cylinder:
From the top of the page, click on Add >> Mesh >> Cylinder and you will be able to see that we have a cylinder created in the origin.

blender

    2. Remesh the object in voxel form:
By using the Remesh modifier from the modifiers section, you can change the formation of the meshes from triangular to voxel-based.

lattice structure

    3. Change the size of the voxels:
By selecting the remesh modifier from the modifiers section, you can remesh the object from triangular to voxel-based and also decrease the number of meshes leading to making it larger.

blender

    4. Subdivide the mesh
Using the Multi-resolution modifier, make the object smoother.

blender

    5. Unsubdivide the mesh:
Using the decimate modifier, unsubdivided the mesh so that we will have larger holes in our lattice structure. The reason why we subdivide and then unsubdivided is that the shape of the meshes changes and becomes more beautiful.

lattice structure in blender

    6. Wireframe modifier:
And then, use the Wireframe modifier to form the lattice shape.

lattice structure in blender

    7. thickness of the wireframe:
Change the thickness of the wireframe to reach the optimal shape.

lattice structure

    8. Another subdivision:
Again, using the Multi-resolution modifier, subdivide the mesh to get a smooth shape.

lattice structure using blender

You might want to have your own costume lattice shape to be applied to an object. In that case, we have a more complex process of placing the lattice objects on the main object and smoothing the end result. That kind of lattice is better to be performed with the aid of python blender API. In the next chapter, we work on the dependencies that will lead us to the costume shape lattice. The benefit of this kind of lattice is that the dimension of the main object will not be affected by the other modifiers that try to remesh it.

Translating Shapes of the Lattice Structure Using Blender

So far we have learned many ways to create lattice structures, but what we haven’t learned about is how we can create these holes with the shape that we want and the places that we choose to be. In this section, we are going to create a tool to translate our desired shape to the specified vertex of an object and then we will Boolean the difference between the translated object from the main object. We use many of the functions that we used in Creating a tool in Blender to instantly translate an object to the point we show with the cursor article.

lattice structure in blender

Python Scripts

So let’s get started:

import  bpy
import bmesh
import math

####################################################################
#####                Utility Functions
####################################################################
class BOOLEAN_TYPE:
    UNION = 'UNION'
    DIFFERENCE = 'DIFFERENCE'
    INTERSECT = 'INTERSECT'
    
def make_boolean(obj1, obj2, boolean_type):
    if not obj1 or not obj2:
        return

    modifier = obj1.modifiers.new(name='booly', type='BOOLEAN')
    modifier.object = obj2
    modifier.operation = boolean_type

    res = bpy.ops.object.modifier_apply({"object": obj1}, apply_as='DATA', mod-ifier=modifier.name)

    assert "FINISHED" in res, "Error"

def solidify(obj,offset,thickness):
 
    bpy.ops.object.modifier_add(type='SOLIDIFY')
    bpy.context.object.modifiers["Solidify"].offset = offset
    bpy.context.object.modifiers["Solidify"].thickness = thickness
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Solidify")


The above function will convert a solid object to a shell of its shape.

def object_closest_point_mesh(p, obj):

    result, location, normal, face_index = obj.closest_point_on_mesh(p)
    assert result, "Can't find closest point on mesh"
    location = location.to_tuple()
    normal = normal.to_tuple()
    return location + normal  # return tuple of 6 floats

def obj_transform(filename, obj_name, size, location, angle):
    
    ob = bpy.context.scene.objects[obj_name]       # Get the object
    bpy.ops.object.select_all(action='DESELECT') # Deselect all objects
    bpy.context.view_layer.objects.active = ob   # Make the cube the active object
    ob.select_set(True)             

    obj = bpy.data.objects[obj_name]
    obj.location = location

    bpy.ops.transform.rotate(value=angle, orient_axis='Z',
        orient_type='GLOBAL',
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        constraint_axis=(False, False, True))
                                                            
def object_put_part2(part_name, point, obj, scale, obj_name):
    vx,vy,vz,a,b,c = object_closest_point_mesh(point, obj)
    a1 = math.atan2(b, a)
    obj_transform(part_name, obj_name, scale, (point[0], point[1], point[2]), a1)

def get_vertex():
    bm = bmesh.new()
    ob = bpy.context.active_object
    bm = bmesh.from_edit_mesh(ob.data)

    points = []
    for v in bm.verts:
        if (v.select == True):
            obMat = ob.matrix_world
            points.append(obMat @ v.co)
        
    for p in points:
        pOb = bpy.data.objects.new("VertexPoint", None)
        bpy.context.collection.objects.link(pOb)
        pOb.location = p
    return p

def delete_object(objName):
    
    bpy.ops.object.select_all(action='DESELECT')
    bpy.data.objects[objName].select_set(True) # Blender 2.8x
    bpy.ops.object.delete()

def get_object_by_name(obj_name):

    assert obj_name in bpy.data.objects, "Error getting object by name:{}".format(obj_name)
    obj = bpy.data.objects[obj_name]
    return obj

####################################################################
########             Main Panel
####################################################################

class MainPanel(bpy.types.Panel):
    bl_label = "Object Adder"
    bl_idname = "VIEW_PT_MainPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Design Automation'
    
    def draw(self, context):
        layout = self.layout
        layout.scale_y = 1.2
        
        row = layout.row()
        row.label(text= "Design Automation", icon= 'OBJECT_ORIGIN')
        row = layout.row()
        row.operator("wm_function.myop", text= "Create single Lattice")           

####################################################################
####                  Main UI ّFunctions                  
####################################################################

class WM_Function_myOp(bpy.types.Operator):
    """Go to edit mode and determine the point then Click the button"""
    bl_label = "Our customized function"
    bl_idname = "wm_function.myop"
    
    scale = bpy.props.FloatProperty(name= "Enter the scale of lattice", default= 1)
    Lattice_Name = bpy.props.StringProperty(name= "Enter the scale of lattice", default= '')
    
    def execute(self, context):
        
        Scale = self.scale
        Lattice_Name = self.Lattice_Name      
        point = get_vertex()            
        bpy.ops.object.editmode_toggle()  
        obj = get_object_by_name('Icosphere')          
        solidify(obj, 0, 0.02)
        obj2 = get_object_by_name('%s'%Lattice_Name)        
        object_put_part2('%s'%Lattice_Name, point, obj, Scale, '%s'%Lattice_Name)
        make_boolean(obj,obj2,'DIFFERENCE')
        delete_object("VertexPoint")
        return {'FINISHED'}
    
    def invoke(self, context, event):
        
        return context.window_manager.invoke_props_dialog(self)  


What We Have Done in the Scripts Above

In the above def execute function, at first get the vertex of the object, then we will create the shell of the main object. After that, we get the name of the lattice shape that has been imported or created by the user. In the next step, we will translate the lattice to the specified point by the user and finally we apply the boolean difference function to the objects. Do not forget the below scripts related to registering and unregistering the classes.

####################################################################
#####                     Register and Unregister
####################################################################
        
def register():
    bpy.utils.register_class(MainPanel)
    bpy.utils.register_class(WM_Function_myOp)
                                               
def unregister():
    bpy.utils.unregister_class(MainPanel)
    bpy.utils.unregister_class(WM_Function_myOp)
                                                         
if __name__ == "__main__":
    register()


How to Use the Translating Tool

Now, it is time to test the panel we have designed and created. To do so, select a vertex on the object in edit mode:

lattice structure using blender

Then, simply click on the button on the panel (Create single lattice).

lattice structure in blender

And here we go! You can now see the lattice structure with the exact shape that you want on the object.

Wrapping Up

In this tutorial, we have managed to design an aesthetic internal lattice structure manually without the need for any coding python scripts. The design process is easy, however, you need to repeat and practice it in order to easily deal with the tricky parts.

Then, we got familiar with the tricks of creating aesthetic internal lattice structures and how to mesh formations such as triangular, voxel-based, and other ones that can influence the beauty of the design of these objects.

Moreover, we have managed to design a panel using python scripts (Blender Python API) to create any number of lattices with any shape and any size on our object. The only thing that the user interface wants from the user is the vertex on which the lattice is going to be placed. We can use this panel to create our custom shape lattice structure with accurate dimensions.

Download this Article in PDF format

Stuck in Creating Lattice Structure?

It’s completely normal if you feel lack of knowledge when facing complicated articles. However, don’t worry! We’de thought about it before. So, you can read our first tutorial about lattice structure using Blender too.

Arashtad Serivces
We work on 3D Models, 3D Apps, and everything about 3D in the world of programming.
Why not telling your ideas?
We surely can help
3D Development

Creating Different Kinds of Internal Lattice Structure: A Complete Tutorial

In this article, we are going to design a scientific and accurate kind of lattice structure for a cube or other geometrical 3D shapes using Blender. The difference between a scientific and an aesthetic lattice structure is that in a scientific lattice structure we need accurate dimensions as opposed to aesthetic lattice structures where beauty is the priority. We use different methods for modeling the 2 kinds of lattice structures.

An introduction to Internal Lattice Structure

Internal lattice structures are used for many different purposes. And there are many ways to design them in Blender or Meshmixer software, one of which is to use wireframes which gives you a low-quality lattice structure even if you utilize the modifier. It would mostly be useful for artistic purposes meaning that you cannot precisely determine the size of the holes and channels and the whole object itself.

We usually design our lattice structures this way when we want some aesthetics in our design but it lacks accuracy. The tool we use for this type of modeling is a wireframe modifier next to some other tools that can be used according to the preferences of the design.

As you can see, the above photo is the wireframe of a cube that is shrunk and has been made so modern and artistic that way but the problem is that it cannot be used for the cases that require size accuracy.

The design that we are going to work on is cube-like below. A kind of lattice structure that we can set its height, width, and length and we also determine the size of the holes and channels precisely.

We know that designing each one of these, takes many hours manually. However, if we design it through the code and design a panel with a button that receives all of the sizes and creates such an object, it takes only a few seconds to design any of these objects by any size, not to mention that, if we want a sphere shape to have such kinds of lattice structures we can simply intersect that object with this cube using the boolean modifier.

The only problem that we face when we design such kind of an object, is that in Blender boolean difference is not as robust as it should be and as a result, when we want to boolean difference a ton of these square-shaped cylinders, we face an object with so many open meshes and the result will be horrible. The fix here is to use a utility function we have written called makeUnionOpt which is an optimized function that uses joining as a way to boolean union several objects. Notice that instead of boolean difference, we can use union in other words instead of cutting through a large cube, we make our large lattice structure using a grid of small square-shaped cylinders that are placed in a sequence.

In this part of the tutorial, we focus on the utility functions that will help us design our main lattice structure.

import  bpy

####################################################################
#####                Utility Functions
####################################################################

def rotation_X(object,D_yz):
        context = bpy.context
        scene = context.scene    
        cube = scene.objects.get(object)    
        bpy.ops.transform.rotate(value= D_yz, orient_axis='X',
                                    orient_type='GLOBAL',
                                    orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
                                    constraint_axis=(True, False,False ))        

def rotation_Y(object,D_xz):
        context = bpy.context
        scene = context.scene    
        cube = scene.objects.get(object)    
        bpy.ops.transform.rotate(value= D_xz, orient_axis='Y',
                                    orient_type='GLOBAL',
                                    orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
                                    constraint_axis=(False, True,False ))
                                    
def rotation_Z(object,D_xy):
        context = bpy.context
        scene = context.scene    
        cube = scene.objects.get(object)    
        bpy.ops.transform.rotate(value= D_xy, orient_axis='Z',
                                    orient_type='GLOBAL',
                                    orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
                                    constraint_axis=(False, False,True ))
                                    


The above functions will determine the rotation of any object that is given in all different directions.

def make_cube(name,Features):
     
    lx = Features[0]
    ly = Features[1]  
    lz = Features[2]
        
    dx = Features[3]
    dy = Features[4]  
    dz = Features[5]
        
    rx = Features[6]
    ry = Features[7]
    rz = Features[8]
        
    bpy.ops.mesh.primitive_cube_add(location=(0,0,0))
    bpy.ops.transform.resize(value=(dx, dy, dz))
    for obj in bpy.context.selected_objects:
        obj.name = name            
    rotation_X(name,rx)
    rotation_Y(name,ry)               
    rotation_Z(name,rz)
                           
    context = bpy.context
    scene = context.scene    
    cube = scene.objects.get(name)         
    cube.location = (lx,ly,lz)


The above function will make a cube with the given name, location on all 3 axes, direction, and their dimension in length, width, and height (The size).

def get_object_by_name(obj_name):
    assert obj_name in bpy.data.objects, "Error getting object by name: {}".format(obj_name)
    obj = bpy.data.objects[obj_name]
    
    return obj


The above function will select an object from the list, using its name.

def make_custom_context(*object_names, base_context=None, mode=None):
    if base_context is not None:
        ctx = base_context
    else:
        ctx = {}
    if mode is not None:
        assert mode in ('OBJECT', 'EDIT'), "Wrong mode used"
        ctx['mode'] = mode
    objs = [get_object_by_name(obj_name) for obj_name in object_names]
    ctx['active_object'] = ctx['object'] = objs[0]
    ctx['selected_editable_objects'] = ctx['selected_objects'] = objs
    ctx['editable_objects'] = ctx['selectable_objects'] = ctx['visible_objects'] = objs

    return ctx

def makeUnionOpt(*object_names):
    ctx = bpy.context.copy()
    if object_names:
        ctx = make_custom_context(*object_names, base_context=ctx, mode='OBJECT')
    bpy.ops.object.join(ctx)  # mostly the same as export/import combination


Using the 2 functions above, we will be able to boolean union a lot of objects altogether at once without bringing up any open or destroyed meshes. In the next part, we will make lattice structures using the above utility functions.

Creating the Main Panel of Lattice Structure

In this second part of our tutorial, we want to create a panel in Blender to be able to easily design any shape of the lattice structure that we want with any size.

In the main panel, here we set the required button and the parameters that we want from the user.

####################################################################
########             Main Panel
####################################################################

class MainPanel(bpy.types.Panel):
    bl_label = "Object Adder"
    bl_idname = "VIEW_PT_MainPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Design Automation'
    
    def draw(self, context):
        layout = self.layout
        layout.scale_y = 1.2
        
        row = layout.row()
        row.label(text= "Design Automation", icon= 'OBJECT_ORIGIN')
        row = layout.row()
        row.operator("wm_function.myop", text= "Create Cube with internal lattice structures")           

####################################################################
####                  Main UI ّFunctions                  
####################################################################

class WM_Function_myOp(bpy.types.Operator):
    """Click to apply our customized function"""
    bl_label = "Our customized function"
    bl_idname = "wm_function.myop"
    
    ls = bpy.props.FloatProperty(name= "Enter the size of lattice", default= 0.25)
    cw = bpy.props.FloatProperty(name= "Enter the WIDTH of the object", default= 2.5)
    cl = bpy.props.FloatProperty(name= "Enter the LENGTH of the object", default= 5.0)
    ch = bpy.props.FloatProperty(name= "Enter the HEIGHT of the object", default= 1.0)

    def execute(self, context):
        
        LATTICE_SIZE = self.ls
        CUBE_WIDTH = self.cw
        CUBE_LENGTH = self.cl
        CUBE_HEIGHT = self.ch        
        return {'FINISHED'}
    
    def invoke(self, context, event):       
        return context.window_manager.invoke_props_dialog(self)

After receiving the required parameters from the user, we will determine the number of square cylinders or rods in all 3 axis:

nRodx = (CUBE_LENGTH / LATTICE_SIZE)+2
nRody = (CUBE_WIDTH / LATTICE_SIZE)+2
nRodz = (CUBE_HEIGHT / LATTICE_SIZE)+2


We also apply a modification in the width, length, and height of the cube, as we want to place all of the holes inside the cube.

CUBE_WIDTH = CUBE_WIDTH + LATTICE_SIZE*3
CUBE_LENGTH = CUBE_LENGTH + LATTICE_SIZE*3
CUBE_HEIGHT = CUBE_HEIGHT + LATTICE_SIZE*3


And we also determine the height of each square rod:

rodHeightx = CUBE_LENGTH - LATTICE_SIZE*2
rodHeighty = CUBE_WIDTH - LATTICE_SIZE*2
rodHeightz = CUBE_HEIGHT - LATTICE_SIZE*2         


And the starting point of where we want to start placing our rods:

startx = CUBE_LENGTH/2 - LATTICE_SIZE*1.5
starty = CUBE_WIDTH/2 - LATTICE_SIZE*1.5
startz = CUBE_HEIGHT/2 - LATTICE_SIZE*1.5


Now, we use for loops to create and places all the cubes on XY plane:

for i in range(int(nRodx/2)):
     for j in range(int(nRody/2)):
          make_cube("Cube",(startx-2*i*LATTICE_SIZE, starty-2*j*LATTICE_SIZE, 0, LATTICE_SIZE/2,                
                    LATTICE_SIZE/2, rodHeightz/2, 0, 0, 0))
          if((i+j) != 0):
               makeUnionOpt('Cube','Cube.001')


Placing the vertical cubic cylinders: If we run the code up to here, we will get the following result. We will be able to see a large set of cubic cylinders that appear all at once, when we click the button (Create Cube with internal lattice structures):

Placing the Horizontal Cubic Cylinders

We are not done yet. To get the complete lattice structure we should place the horizontal cubic cylinders. To do so, we should write the following for loops in the def execute() function after the for loops written for the vertical rods. we should place the rods in XZ plane (horizontal cubic cylinders) using the following script:

for i in range(int(nRodx/2)):
     for j in range(int(nRodz/2)):
          make_cube("C",(startx-2*i*LATTICE_SIZE, 0, startz-2*j*LATTICE_SIZE,LATTICE_SIZE/2,rodHeighty/2,      
                    LATTICE_SIZE/2, 0, 0, 0))
          if((i+j) != 0):
               makeUnionOpt('C', 'C.001')


The result up to here will be like this:

The above result looks much closer to the expected result. We only need another set of rods normal to YZ plane.

Completing the Project

And finally, we should place the rods on YZ plane to get the complete model of internal lattice structure. The following code containing for loops should be writ-ten after the 2 previous set of for loops to serve our purpose:

for i in range(int(nRody/2)):
     for j in range(int(nRodz/2)):
          make_cube("Cu", (0, starty-2*i*LATTICE_SIZE, startz-2*j*LATTICE_SIZE, rodHeightx/2,      
                         LATTICE_SIZE/2, LATTICE_SIZE/2, 0, 0, 0))
           if((i+j) != 0):
                makeUnionOpt('Cu','Cu.001')
          

We also boolean union all three set of rods using the utility boolean union function we wrote in the last section:

makeUnionOpt('Cu','C','Cube')										

The result will be like this:

And do not forget to close the project to be able to use the panel for creating all the different shapes of lattice all around the object:


####################################################################
#####                     Register and Unregister
####################################################################  
         
def register():
    bpy.utils.register_class(MainPanel)
    bpy.utils.register_class(WM_Function_myOp)
                                                   
def unregister():
    bpy.utils.unregister_class(MainPanel)
    bpy.utils.unregister_class(WM_Function_myOp)
                                                           
if __name__ == "__main__":
    register()


Now, using the above interface, you will be able to design and modify a robust internal lattice structure in a few seconds. You can also boolean intersect it with the shape that you want and make that shape have an internal lattice structure.

Conclusion

In this tutorial, we have introduced all types of lattice structures including the ones in beauty and aesthetics are considered more important than accuracy of size and also the ones that accuracy of the dimensions is placed on top of our priorities. We have also managed to get started with the design of an internal lattice structure with accurate dimensions of height, width, and length for the object and the channels.

Finally, we have managed to complete the design tool for creating an internal lattice structure with accurate dimensions. Using the said tool which is provided in a panel, you can design a cube with a certain height, width, length, and internal lattice structures. Moreover, you can use the boolean intersect modifier to apply the said internal lattice structure on any object with any shape.

Download this Article in PDF format

web development

Check Out Our Services

In Arashtad, we’re working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
3D Development

Fixing Non-Manifold Meshes in Blender

One of the things that will make you fed up with Boolean modifiers in Blender is the non-manifold output. Unfortunately, there is no robust way to cover this issue. However, we can create a tool that can fix most (if not all) of the problematic meshes. This tool helps us fix non-manifold meshes in Blender.

Creating A Tool to fix Non-Manifold Meshes in Blender

In the photo below, you can see an example of a non-manifold result that appeared as a result of a Boolean difference of another cube from this one. If you apply another Boolean operation on this object, you will see that the object gets fully destroyed.

non-manifold meshes

Python Scripts

In the scripts below we will get benefit from the functions that will eventually create a standard mesh that is the result of remeshing from triangular meshes to voxel-based meshes and also removing the non-manifold meshes.

import  bpy
import bmesh
import math

####################################################################
#####                Utility Functions
####################################################################
   
def fixMesh(obj_name):
    make_voxel_remesh(get_object_by_name(obj_name), 0.5)
    if is_object_have_non_manifolds(get_object_by_name(obj_name)):
        print(obj_name, "have non manifolds")
        if remove_object_non_manifold_loops(obj_name, loops=2):
            print("Filled:", fill_non_manifolds(obj_name))
            obj = get_object_by_name(obj_name)
            make_smooth_remesh(obj, 9, 0.9, 1, True, True)    


The above function is the main function for fixing the meshes. At first, it remeshes the object from triangular to voxel-based. Then, it will check whether has non-manifold parts and if the answer is yes, it will remove those parts. In the end, it will smooth and remesh the object.

def is_object_have_non_manifolds(obj):
    assert obj.type == 'MESH', "Unsupported object type"
    bmo = bmesh.new()
    bmo.from_mesh(obj.data)
    have = False
    for edge in bmo.edges:
        if not edge.is_manifold:
            have = True
            break
    if not have:
        for vert in bmo.verts:
            if not vert.is_manifold:
                have = True
                break
    bmo.free()  # free and prevent further access
    return have


The above function checks whether the object has non-manifold parts or not.

def deselect_objects():
    bpy.ops.object.select_all(action='DESELECT')

def select_object_by_name(obj_name):
    get_object_by_name(obj_name).select_set(True) # Blender 2.8x

def activate_object_by_name(obj_name):
    bpy.context.view_layer.objects.active = get_object_by_name(obj_name)    

def is_object_contain_selected_vertices(obj):
    if obj.mode == "EDIT":
        bm = bmesh.from_edit_mesh(obj.data)
    else:
        bm = bmesh.new()
        bm.from_mesh(obj.data)
    selected = False
    for v in bm.verts:
        if v.select:
            selected = True
            break
    bm.free()
    return selected

def remove_object_non_manifold_loops(obj_name, loops=0):
    deselect_objects()
    select_object_by_name(obj_name)
    activate_object_by_name(obj_name)
    removed = False
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_mode(type="VERT")
    bpy.ops.mesh.select_non_manifold(extend=False)
    if is_object_contain_selected_vertices(get_object_by_name(obj_name)):
        if loops:
            for i in range(loops):
                bpy.ops.mesh.select_more()
            bpy.ops.mesh.delete(type='FACE')
        else:
            bpy.ops.mesh.delete(type='VERT')
        removed = True
    bpy.ops.mesh.select_all(action='DESELECT')
    bpy.ops.object.mode_set(mode='OBJECT')
    return removed

def fill_non_manifolds(obj_name):
    deselect_objects()
    select_object_by_name(obj_name)
    filled = False
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_mode(type="VERT")
    bpy.ops.mesh.select_non_manifold(extend=False)
    if is_object_contain_selected_vertices(get_object_by_name(obj_name)):
        bpy.ops.mesh.fill(use_beauty=True)
        bpy.ops.mesh.normals_make_consistent(inside=False)
        bpy.ops.mesh.faces_shade_smooth()
        filled = True
    bpy.ops.mesh.select_all(action='DESELECT')
    bpy.ops.object.mode_set(mode='OBJECT')
    return filled


The above function will fill the open meshes.

def get_object_by_name(obj_name):
    assert obj_name in bpy.data.objects, "Error getting object by name:	{}".format(obj_name)
    obj = bpy.data.objects[obj_name]
    return obj

def make_voxel_remesh(obj, voxel_size, adaptivity=0, use_smooth_shade=True):
    modifier = obj.modifiers.new(name='remesh', type='REMESH')
    modifier.mode = 'VOXEL'
    modifier.voxel_size = voxel_size
    modifier.adaptivity = adaptivity
    modifier.use_smooth_shade = use_smooth_shade
    res = bpy.ops.object.modifier_apply({"object": obj}, apply_as='DATA', modifier=modifier.name)
    assert "FINISHED" in res, "Error"


The above function will remesh the object to a voxel-based object.

def make_smooth_remesh(obj, octree_depth=9, scale=0.9, threshold=1, use_smooth_shade=True,                         
        use_remove_disconnected=True):
    modifier = obj.modifiers.new(name='remesh', type='REMESH')
    modifier.mode = 'SMOOTH'
    modifier.use_smooth_shade = use_smooth_shade
    modifier.octree_depth = octree_depth
    modifier.scale = scale
    modifier.use_remove_disconnected = use_remove_disconnected
    modifier.threshold = threshold
    res = bpy.ops.object.modifier_apply({"object": obj}, apply_as='DATA', modifier=modifier.name)
    assert "FINISHED" in res, "Error"

####################################################################
########             Main Panel
####################################################################

class MainPanel(bpy.types.Panel):
    bl_label = "Object Adder"
    bl_idname = "VIEW_PT_MainPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Design Automation'
    
    def draw(self, context):
        layout = self.layout
        layout.scale_y = 1.2
        
        row = layout.row()
        row.label(text= "Design Automation", icon= 'OBJECT_ORIGIN')
        row = layout.row()
        row.operator("wm_function.myop", text= "Fix Mesh")           

####################################################################
####                  Main UI ّFunctions                  
####################################################################

class WM_Function_myOp(bpy.types.Operator):
    """Go to edit mode and determine the point then Click the button"""
    bl_label = "Our customized function"
    bl_idname = "wm_function.myop"
     
    def execute(self, context):
        
        fixMesh('Cube')
        return {'FINISHED'}
    
    def invoke(self, context, event):
        
        return context.window_manager.invoke_props_dialog(self)  


####################################################################
#####                     Register and Unregister
####################################################################

         
def register():
    bpy.utils.register_class(MainPanel)
    bpy.utils.register_class(WM_Function_myOp)
                                            
    
def unregister():
    bpy.utils.unregister_class(MainPanel)
    bpy.utils.unregister_class(WM_Function_myOp)
   
                                                       
    
if __name__ == "__main__":
    register()



Now if we run the code and click the Fix Mesh button in the panel, we will see that mesh of the cube has been fixed.

meshes in Blender

Fixing Non-Manifold Meshes: Done!

In this tutorial, we have managed to write the scripts of the utility functions that would help us remove or fix the non-manifold meshes in Blender. We have applied these utility functions in our main panel class and as a result, we have a fixed output.

Download this Article in PDF format

web developement

Check Out Our Services

In Arashtad, we’re working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
3D Development

How to Apply Boolean Union of Many Objects in Blender

This tutorial covers most (if not all) of the problems concerning the Boolean union of multiple objects in Blender. If you have worked with a Boolean modifier in Blender more than just a try, you must have faced the circumstances in which the result of union would have been a non-manifold mesh or you have wanted to Boolean union a great number of objects and you have ended up with a simple modifier that only does the Boolean union only for a couple of objects that you wouldn’t be sure if the result of the union would have non-manifold mesh or not. This article guides you with different ways to solve this issue.

Boolean Union of Multiple Objects in Blender

Suppose you have a lot of cubes that you want them all to merge into one object, if the number of cubes is n, you will have to apply the Boolean union modifier n-1 times, which takes a lot of time. You may use the Python Blender API and do this job in a loop. This solution is OK until you face with the non-manifold result caused by the Boolean modifier. That is when the issue begins.

To solve the above problem manually (without any scripting), you can get benefit from a useful hack in Blender and which is exporting the objects altogether. To do this, all you have to do is to select all the objects and export them (do not forget to check the Selection Only box). If you import the exported file, you will see that all of the objects have merged into one object.

There is also another way to do this in a more standard and sophisticated way and that is through scripting. The benefit of this method is that we can use it next to the rest of our code for more complex functionalities rather than just a Boolean union.

Now imagine that we want to create a button that Boolean union any number of cubes at once without any need to export or import anything. The following code will serve this purpose for us:

IMPORTANT NOTE:

Remember that the scripts we are using here are related to Blender version 2.83 and if you are working with any other versions, it is probable that the scripts might differ a little bit, but we will show you ways to find the proper functions if there are any differences at all.

Python Scripts

The below scripts will create a panel inside which you can Boolean union a large number of objects that have been imported in Blender or designed by the user. Notice that the main purpose of the below code is to make you familiar with the procedure and for other Boolean unions of another object with different names and numbers you should modify the code a bit. However, the utility functions remain the same.


import  bpy

####################################################################
#####                Utility Functions
####################################################################

def make_custom_context(*object_names, base_context=None, mode=None):
    if base_context is not None:
        ctx = base_context
    else:
        ctx = {}

    if mode is not None:
        assert mode in ('OBJECT', 'EDIT'), "Wrong mode used"
        ctx['mode'] = mode

    objs = [get_object_by_name(obj_name) for obj_name in object_names]
    ctx['active_object'] = ctx['object'] = objs[0]
    ctx['selected_editable_objects'] = ctx['selected_objects'] = objs
    ctx['editable_objects'] = ctx['selectable_objects'] = ctx['visible_objects'] = objs
    return ctx

def makeUnionOpt(*object_names):
    ctx = bpy.context.copy()
    if object_names:
        ctx = make_custom_context(*object_names, base_context=ctx, mode='OBJECT')
    bpy.ops.object.join(ctx)  # mostly the same as export/import combination

def get_object_by_name(obj_name):
    assert obj_name in bpy.data.objects, "Error getting object by name: {}".format(obj_name)
    obj = bpy.data.objects[obj_name]    
    return obj

def deselect_objects():
    bpy.ops.object.select_all(action='DESELECT')

####################################################################
########             Main Panel
####################################################################

class MainPanel(bpy.types.Panel):
    bl_label = "Object Adder"
    bl_idname = "VIEW_PT_MainPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Design Automation'
    
    def draw(self, context):
        layout = self.layout
        layout.scale_y = 1.2
        
        row = layout.row()
        row.label(text= "Design Automation", icon= 'OBJECT_ORIGIN')
        row = layout.row()
        row.operator("wm_function.myop", text= "Boolean Union of multiple cubes")           

####################################################################
####                  Main UI ّFunctions                  
####################################################################

class WM_Function_myOp(bpy.types.Operator):
    """Click to apply our customized function"""
    bl_label = "Our customized function"
    bl_idname = "wm_function.myop"
    
    n = bpy.props.IntProperty(name= "Enter the size of the Cube", default = 20)
  
    def execute(self, context):   
        n = self.n                    
        cubes = ["Cube" for i in range(n)]
        #cubes[0] = "Cube"      
        if(n >= 10 and n <= 99):
            for i in range (9):
                cubes[i+1] = "Cube.00%d"%(i+1)
          
            for i in range (n-10):
                cubes[i+10] = "Cube.0%d"%(i+10)
                
        elif(n < 10 and n >= 2):
            for i in range (9):
                cubes[i+1] = "Cube.00%d"%(i+1)
        
        cubes = tuple(cubes)      
        makeUnionOpt(cubes)

        return {'FINISHED'}
    
    def invoke(self, context, event):    
        return context.window_manager.invoke_props_dialog(self)  

####################################################################
#####                     Register and Unregister
####################################################################   
        
def register():
    bpy.utils.register_class(MainPanel)
    bpy.utils.register_class(WM_Function_myOp)
                                                  
def unregister():
    bpy.utils.unregister_class(MainPanel)
    bpy.utils.unregister_class(WM_Function_myOp)
                                                              
if __name__ == "__main__":
    register()
 

And as you can see, we have got one object at the end with the help of just a button.

Last Word

In this tutorial, we have managed to Boolean union a large number of objects using the Boolean union other than the one in the modifiers section. This type of union will join the objects and the good thing about this type of union is that the output will not have the non-manifold meshes especially when we have overlap between the objects. We have also provided a manual method so that you can avoid the scripts.

Download this Article in PDF format

web developement

Check Out Our Services

In Arashtad, we’re working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
3D Development