0
$\begingroup$

I would like to create some interesting shapes with a script. I'm still pretty new to python and I'm having trouble understanding how to get BMesh to behave the way I want. Here is what I'm trying to accomplish.

  1. Randomly inset faces a random length, with some not being inset at all.

  2. Advanced function: randomly inset again excluding the 4 faces around original insets (if that makes sense)

    import bpy
    import bmesh
    from random import randint, random
    
    obj = bpy.context.active_object
    
    bm = bmesh.new()
    bm.from_mesh(obj.data,face_normals=True)
    
    for faces in bm.faces:
        if randint(0,1) > 0:
            bmesh.ops.inset_region(bm, faces=bm.faces, thickness=0.4, depth=randint(0,1))
    
    bm.to_mesh(obj.data)
    bm.free()
    obj.data.update()
    

enter image description here

Here is an idea of what I'm going for generated from a cube.

$\endgroup$
2
$\begingroup$

You can't loop through bm.faces, because you are creating more faces in the loop, which are also iterated over. Instead make a copy of the faces

faces_copy = [f for f in bm.faces]

Now, your code would give us the following result.

enter image description here

Notable problems - Insets, even at 0 depth. - Your example image shows and inset with 0 depth and then a random extrusion.

Let's implement that. Luckily, after a inset_region operation, the face index stays the same so we can simply extrude it by insetting another time and moving the face's vertices along the face normal.

bmesh.ops.inset_region(bm, faces = [face], thickness=0.4, depth=0)
bmesh.ops.inset_region(bm, faces = [face], thickness=0, depth=0)
bmesh.ops.translate(bm, verts = face.verts, vec = face.normal)

enter image description here

Now lets add the randomness:

import random
#[...]

bmesh.ops.inset_region(bm, faces = [face], thickness=random.uniform(0.1, 0.9), depth=0)
bmesh.ops.inset_region(bm, faces = [face], thickness=0, depth=0)
bmesh.ops.translate(bm, verts = face.verts, vec = face.normal * random.uniform(0.1, 4))

Iterations

We need to catch the newly "extruded" faces. Luckily inset_region returns geometry. We can create a new face list like this:

geom = bmesh.ops.inset_region(bm, faces = [face], thickness=0, depth=0)
new_faces = geom['faces'] # list of bmesh faces

Create an empty list for the new faces. After the insetting and extrusion, we'll add the active face and the extruded side faces.

new_faces.extend(geom['faces'])
new_faces.append(face)

Complete code (with two iterations):

import bpy
import bmesh
import random
from mathutils import Vector

obj = bpy.context.active_object

bm = bmesh.new()
bm.from_mesh(obj.data,face_normals=True)

# faces, which could be extruded
faces_copy = [f for f in bm.faces]
new_faces = []
# number of iterations goes here
for i in range(0, 2):
    for face in faces_copy:
        do_inset = random.randint(0,1)
        if do_inset:
            bmesh.ops.inset_region(bm, faces = [face], thickness=random.uniform(0.1, 0.9), depth=0)
            geom = bmesh.ops.inset_region(bm, faces = [face], thickness=0, depth=0)
            bmesh.ops.translate(bm, verts = face.verts, vec = face.normal * random.uniform(0.1, 4))
            new_faces.extend(geom['faces'])
            new_faces.append(face)
    faces_copy = new_faces
    new_faces = []

bm.to_mesh(obj.data)
bm.free()
obj.data.update()

enter image description here

The concept is pretty cool, but it already show, that we need to implement a method to take the faces size into account before using bmesh.ops.translate.

$\endgroup$
  • $\begingroup$ You are amazing! Thank you so much for your help. I had been spending days looking at other peoples code and documentation to try and understand this. Thank you for taking time to explain everything. $\endgroup$ – admbro May 7 '18 at 22:58
  • $\begingroup$ @adminbromo You're welcome, I fiddled around a bit more with it and tried to add an intersection detection. It's not ready to be included in the answer, but may it'll help you anyways. $\endgroup$ – Leander May 7 '18 at 23:01
  • $\begingroup$ Unless i'm missing something, you can copy a list by taking a "full slice" e.g. faces_copy = bm.faces[:]; probably better than a list comprehension. $\endgroup$ – StarWeaver May 8 '18 at 3:26
  • $\begingroup$ @StarWeaver Feel free to edit. I didn't use the slice notation since OP mentioned being new to python and it's even less clear what happens there. Also bm.faces is a BMFaceSeq and understanding why the list notation works on a collection is also not very beginner-friendly. $\endgroup$ – Leander May 8 '18 at 10:28
  • $\begingroup$ Are there any resources that would help a new user like myself understand bmesh (or blender scripting in general). I have an okay understanding of python. Where I have trouble is understanding syntax and how to move things around within lists. I guess what I'm asking, are there any best practices or resources that you would recommend. One of the problems I run into is older versions of code that don't line up with newer versions. Especially when working with things like pie menus etc... Please don't feel like you need to answer this, I figured I would ask. Thanks again! $\endgroup$ – admbro May 8 '18 at 13:18

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.