Menger Sponge

Hi there,

I am keen on modelling a level-4 Menger Sponge (http://en.wikipedia.org/wiki/Menger_sponge)

  1. What is the best way to do this?

I am considering 3-D printing this between 7.5-10cm a side.

  1. Which print service would you guys recommend between Shapeways and Sculpteo ?
    Or other ?

please help.

This was a fun challenge :). I’ve been trying to do this with a bunch of array modifiers applied to a single cube. This way you can modify the initial cube and all cubes in your sponge will update. It took me a while to come up with this setup but I finally got it. I’ve attached a screenshot and my blend file. If you want to use this for 3D printing you need to clean up after you apply all modifiers. There are faces inside the structure that you don’t need.

Attachments

menger_sponge.blend (460 KB)


I wanted to give a try with booleans to see if it could make it easy.
And i struggled a lot more than i thought simply because Blender booleans are really bad.

Anyways, here’s what i did :

First add a plane and add 26 loop cuts (CTRL+R)
http://i.imgur.com/EI02vgA.jpg

Then add 26 loops cuts on the other axis
http://i.imgur.com/VuSsq6G.jpg

Go into Face selection mode and select those faces then delete them
http://i.imgur.com/XBX8wwq.jpg

Select those faces and delete them
http://i.imgur.com/tfeSx39.jpg

Go into Edge selection mode then click on the bottom menu on Select -> Non manifold
http://i.imgur.com/MigWRX9.jpg

Unselect the boundary of the mesh
http://i.imgur.com/xTSVVcw.jpg

Press CTRL+I to invert the edge selection and press X -> Edge to delete them
http://i.imgur.com/yuywP9U.jpg

Extrude those on an axis , and make sure the mesh will be longer than a default cube

Select all and press F so Blender automatically close all those unclosed tubes (required of the booleans would do a bad job)

Select all , press W -> Remove Doubles and press CTRL+N to recalculate the normals just in case (always good reflex to have to do this regularly during your modelling sessions to avoid nasty surprises)

Now on another layer add a Cube and go to edit mode, then subdivide that cube 3 or 4 times (or the booleans would fail very badly)
http://i.imgur.com/58T7s8e.jpg

Check with the 2 layers if you have your tubular object placed correctly with your cube
http://i.imgur.com/mB8Adnc.jpg

Ok, enable only the layer with the cube so it’s easier to see what’s going on, then add a Boolean modifier
Set the modifier to Difference , then at the target select the tubular object
http://i.imgur.com/Yu4vocB.jpg

That’s good, apply the Boolean modifier.
Now is where the boolean would fail if we use it for the other sides of the cube, to avoid that problem, add a Remesh modifier set to Sharp and the octree set to 6 (you don’t want to use 5 or later boolean would just miss some faces)
http://i.imgur.com/iAZEn4d.jpg

Apply the Remesh modifier

Back to the tubular object, rotate it on an axis so we’ll be able to boolan another side of the cube, after rotating it make sure to press CTRL+A -> Rotation&Scale to apply the rotation and scale in case you scaled the object instead of edit mode before (or good luck with the booleans :smiley: )

http://i.imgur.com/m0esqot.jpg

Select only the layer with the cube to make it easier to see, then add a boolean modifier to it, set it to Difference , then target the tubular object again
It will take a longer time for you to wait until it’s done (due to the increased amount of vertices on the cube, thanks or no thanks to the remesh modifier, and because Blender booleans are slow unfortunately) but it will work

Apply the boolean modifier now
http://i.imgur.com/yaQxaLD.jpg

Next step, Remesh modifier again, with the same settings as before, or next booleans would fail.
http://i.imgur.com/v5DsQJ6.jpg

Apply the remesh modifier.
Back to the tubular object, rotate it again so it get into the last side of the cube, then press CTRL+A -> Rotation&Scale to apply rotation&scale
http://i.imgur.com/Sc3OGkQ.jpg

Look only on the layer with the Cube, again so you see easier, add a Boolean modifier, set it to Difference and target again the tubular object, then wait until Blender is finished with the slow booleans
Apply the boolean modifier (and wait again, slow , slow booleans …)

http://i.imgur.com/h7IiN2q.jpg

Now to complete it, add a final Remesh modifier, same setting as before, and apply that modifier
http://i.imgur.com/nTrtX1f.jpg

This last remesh modifier is to get rid of some bad boolean topology that could have screwed up a face or two.

Now you object is finished.
http://i.imgur.com/0xaTIn8.jpg
http://i.imgur.com/m4JhB18.jpg

Oh, I just noticed you want a level 4 sponge. The two examples above only show level 3. I’ve added a fourth level using my method but the blend file takes a few minutes to open and each operation gets really slow because of so many layered array modifiers and the bevel modifier I have on top of that. I think the easiest way and maybe the best method performance wise is just to copy paste a bunch of cubes in edit mode and keep copying until you’ve created your level 4 shape. The other two methods mentioned are cooler though :smiley:

Oh , it looks like i completely missed the “level 4” mention.
I guess the boolean+remesh method could work there too, but it would probably take much more time to build the tubular object, and wait a lot more for the slow booleans to make their effect

Thank you for you help!
However my Macbook is a bit faulty and crashes quite often, So I will probably use the least computationally heavy method. (Copying cubes you mentioned)

once you create a lvl 1 sponge though I would need a command to get rid of overlapping faces, How do I do this ?

Have you guys had experience with 3D printing ? where can I enquire to find the printer services with the latest and best printers ?
I am thinking Shapeways or Sculpteo ?

This intrigued me so i wrote a python script to generate a Menger Sponge. I would appreciate some ideas about improving its performance because at levels > 1 it is pretty slow. I didn’t even try level 3!!

Also, the resulting object has lots of extra interior faces and I don’t know how to get rid of them programmatically!

Here is the script:


import bpy
import os

def createCube(x, y, z, width):
    global firstCube
    
    bpy.ops.mesh.primitive_cube_add(radius=width/2, view_align=False, enter_editmode=False, location=(x, y, z))

    #Set to Edit mode after the first cube is created so each additional cube is part of the same object
    # and not a separate object.
    if firstCube:
        bpy.ops.object.mode_set(mode='EDIT')
        firstCube = False

    #Uncomment the line below to add a small bevel to each cube. This increases the generation time!
    #bpy.ops.mesh.bevel(offset=0.005, vertex_only=False)


#Parameters:
#   x,y,z = location to place the completed sponge.
#   size = the size of the sponge
#   level = The number of levels to recurse.  Numbers greater than 1 are very slow!    
def createSponge(x, y, z, size, level):
    for w in range(0,3):
        for d in range(0,3):
            for h in range(0,3):
                w1 = (w * size/3) + x
                d1 = (d * size/3) + y
                h1 = (h * size/3) + z
                
                if not((w == 1 and h == 1) or (w == 1 and d == 1) or (h == 1 and d == 1)):
                    if level > 0:
                        createSponge(w1, d1, h1, size/3, level-1)
                    else:
                        createCube(w1, d1, h1, size/3)


#After the first cube is created, we will switch to Edit mode.  This boolean variable helps us
#know when the first cube has been created
firstCube = True

#Create the Menger Sponge.  This is a recursive method which calls itself for each level of the
#sponge to be created.  Setting level to 2 takes a long time!  I didn't even try 3!!!
createSponge(0, 0, 0, 2, 1)

#Remove all double vertices
bpy.ops.mesh.select_all()
bpy.ops.mesh.remove_doubles()

#Set back to object mode
bpy.ops.object.mode_set(mode='OBJECT')

Here’s one. I didn’t try level 5 since level 4 has 576000 faces




  • Starts from active/selected object which has to have a unique name without periods and object scale needs to be 1,1,1
  • Cleanup doesn’t always work (remove doubles, interior faces)

import bpy

mask1 = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (2.0, 0.0, 0.0), 
         (0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (2.0, 0.0, 1.0), 
         (0.0, 0.0, 2.0), (1.0, 0.0, 2.0), (2.0, 0.0, 2.0)]
         
mask2 = [(0.0, 1.0, 0.0), (0.0, 0.0, 0.0), (2.0, 1.0, 0.0), 
         (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), 
         (0.0, 1.0, 2.0), (0.0, 0.0, 0.0), (2.0, 1.0, 2.0)]
         
mask3 = [(0.0, 2.0, 0.0), (1.0, 2.0, 0.0), (2.0, 2.0, 0.0), 
         (0.0, 2.0, 1.0), (0.0, 0.0, 0.0), (2.0, 2.0, 1.0), 
         (0.0, 2.0, 2.0), (1.0, 2.0, 2.0), (2.0, 2.0, 2.0)]
         
obj = bpy.context.active_object
size = obj.dimensions

def duplicate(mask):
    for i in mask:
        if i != (0.0, 0.0, 0.0):
            bpy.context.scene.objects.active = obj
            bpy.ops.object.select_pattern(pattern=obj.name, extend=False)
            bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, \
                                          TRANSFORM_OT_translate={"value":(size[0] * i[0], size[1] * i[1], size[2] * i[2])})

def cleanup():
    bpy.context.scene.objects.active = obj
    bpy.ops.object.select_pattern(pattern=obj.name+"*", extend=False)
    bpy.ops.object.join()
    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.remove_doubles(threshold=0.001, use_unselected=True)
    bpy.ops.mesh.select_all(action='DESELECT')
    bpy.ops.mesh.select_mode(type='FACE')
    bpy.ops.mesh.select_interior_faces()
    bpy.ops.mesh.delete(type='FACE')
    bpy.ops.mesh.select_mode(type='VERT')
    bpy.ops.object.mode_set(mode='OBJECT')
    
duplicate(mask1)
duplicate(mask2)
duplicate(mask3)
cleanup()

Edit: here is a level 6 rendered
(Original resolution here)


Very nice !
How much poly is the level 6 ?

I had 230.4 million but a fraction of that is overlapping faces I didn’t care about.

At level 7 render resolution becomes an issue because showing all levels and fitting the whole thing in a render would need a very high resolution image. That is 2k high so level 7 would need 6k image to show the lowest level at that detail (smallest holes are just few pixels).

My blender would have exploded from just trying to display the level 6 :smiley:

Thank you for this script.