Script that generates mesh

I am writing a script that generates mesh.

I’ve been using this:
http://wiki.blender.org/index.php/Doc:2.6/Manual/Extensions/Python/Geometry
as a template. The template code runs fine for me, but when I try to use my own generated list of vertices, I get problems.

My script seems to generate polygons in exactly the configuration I want… but there’s still a problem:
If I try to edit the mesh in almost any way, it freezes (although I am able to edit the positions of vertices just fine).

Is this because the above method of generating mesh creates mesh that’s missing some necessary data?
I noticed the mesh.update(calc_edges=True) command, and I’m wondering if I need to update anything else?

One thing to note is that my code generates a pattern of triangular islands (whose corners share the same position). Might this cause any problems?

I’d appreciate if anyone would try out my code (below): The first two subroutines are slightly modified form the pattern generating algorithm published here:
http://preshing.com/20110831/penrose-tiling-explained/
I’m basically trying to generate a penrose tile pattern.

EDIT: Try the code I pasted in a subsequent reply - I cut stuff out to simplify troubleshooting.

I tried your script under Win Xp, Blender 2.68.
It generates the Penrose. No problems switching in and out of Edit mode. I confirm the freeze while trying to edit the mesh.
Making Blender show face normals, I noticed that they are quite a mess (some upwards, some downwards). I am neither a Python nor a Blender code expert, so I can’t tell if the normals can be the reason of the freeze. For sure they have a weird look :slight_smile:

Thanks for checking!

Yeah, the reason the normals are flipped is because sometimes I write a face using the vertices in clockwise order, and sometimes counteclockwise. I knew this would happen, so my plan was to “remove duplicates vertices” and “recalculate normals”… but I can’t do that because of the crashing.

EDIT:

Ok, I’ve simplified my code. The code only generates a pie shape now, and the normals are all pointing the same way. Editing the mesh still crashes it.


import bpy
import math
from mathutils import Vector

# Create wheel of triangles around the origin
def createWheel():
    triangles = []
    
    for i in range(10):
        theta_i = i*2.0*math.pi/10.0
        theta_ip1 = (i+1)*2.0*math.pi/10.0
        
        x_i = math.cos(theta_i)
        x_ip1 = math.cos(theta_ip1)
        
        y_i = math.sin(theta_i)
        y_ip1 = math.sin(theta_ip1)
        
        A = Vector((0.0,0.0,0.0))
        B = Vector((x_i,y_i,0.0))
        C = Vector((x_ip1,y_ip1,0.0))

        triangles.append((A, B, C))
    return triangles
    


# Generate map of tiling
listTriangles = createWheel()

# Construct the lists necessary for generation of geometry
listVertices = []
listFaces = []
for triangle in listTriangles:
    # write the vertex coords to the list, and remember the vertex indices
    # In Blender, the mesh data stores each vertex as a tuple of 3 floats.
    newVertex1 = (triangle[0][0],triangle[0][1],triangle[0][2])
    newVertex2 = (triangle[1][0],triangle[1][1],triangle[1][2])
    newVertex3 = (triangle[2][0],triangle[2][1],triangle[2][2])
    
    listVertices.append(newVertex1)
    newVertex1_i = len(listVertices) - 1
    listVertices.append(newVertex2)
    newVertex2_i = len(listVertices) - 1
    listVertices.append(newVertex3)
    newVertex3_i = len(listVertices) - 1
    # Define the faces by index numbers. Each faces is defined by 4 consecutive integers.
    # For triangles you need to repeat the first vertex also in the fourth position.
    newFace = (newVertex1_i,newVertex2_i,newVertex3_i,newVertex1_i)
    listFaces.append(newFace)
    

# Build the mesh in Blender's API

mesh = bpy.data.meshes.new("penroseMesh")   # create a new mesh  
 
ob = bpy.data.objects.new("Penrose", mesh)      # create an object with that mesh
ob.location = Vector((0,0,0)) #by.context.scene.cursor_location   # position object at 3d-cursor
bpy.context.scene.objects.link(ob)                # Link object to scene
 
# Fill the mesh with verts, edges, faces 
mesh.from_pydata(listVertices,[],listFaces)   # edges or faces should be [], or you ask for problems
mesh.update(calc_edges=True)    # Update mesh with new data

You might create invalid geometry, call validate() to fix. Note that it might remove bad things of mesh.

mesh.from_pydata(listVertices,[],listFaces)   # edges or faces should be [], or you ask for problems
mesh.validate(True)
mesh.update(calc_edges=True)    # Update mesh with new data

Ah-Ha! That works! In trying this command, I also discovered the problem validate() was removing from my mesh too!

The sample code I was using tells you to write a triangle like this:

faces=[ (0,1,4,0), (1,2,4,1), (2,3,4,2), (3,0,4,3)]

…note that the first and last index are repeated to indicate that the poly is a triangle. Was this sample code written before BMesh? I ask because if I remove the repeated index, and only enter three vertex indices, my problems disappear.

Is my code only working by accident, or does this sample code needs to be updated?

Anyways, check it out. My code generates Penrose tiling! After running the algorithm, you need to remove double vertices and recalculate the face normals to get them all pointing the same way.


import bpy
import math
from mathutils import Vector

subDivIterations = 5

goldenRatio = (1 + math.sqrt(5)) / 2

def subdivide(triangles):
    result = []
    for color, A, B, C in triangles:
        if color == 0:
            # Subdivide red triangle
            P = A + (B - A) / goldenRatio
            result += [(0, C, P, B), (1, P, C, A)]
        else:
            # Subdivide blue triangle
            Q = B + (A - B) / goldenRatio
            R = B + (C - B) / goldenRatio
            result += [(1, R, C, A), (1, Q, R, B), (0, R, Q, A)]
    return result

# Create wheel of red triangles around the origin
def createWheel():
    triangles = []
    
    for i in range(10):
        theta_i = i*2.0*math.pi/10.0
        theta_ip1 = (i+1)*2.0*math.pi/10.0
        
        x_i = math.cos(theta_i)
        x_ip1 = math.cos(theta_ip1)
        
        y_i = math.sin(theta_i)
        y_ip1 = math.sin(theta_ip1)
        
        A = Vector((0.0,0.0,0.0))
        B = Vector((x_i,y_i,0.0))
        C = Vector((x_ip1,y_ip1,0.0))
        
        if i % 2 == 0:
            B, C = C, B  # Make sure to mirror every second triangle
        triangles.append((0, A, B, C))
    return triangles
    


# Generate map of tiling
listTriangles = createWheel()
for x in range(subDivIterations):
    listTriangles = subdivide(listTriangles)

# Construct the lists necessary for generation of geometry
listVertices = []
listFaces = []
for triangle in listTriangles:
    # write the vertex coords to the list, and remember the vertex indices
    # In Blender, the mesh data stores each vertex as a tuple of 3 floats.
    newVertex1 = (triangle[1][0],triangle[1][1],triangle[1][2])
    newVertex2 = (triangle[2][0],triangle[2][1],triangle[2][2])
    newVertex3 = (triangle[3][0],triangle[3][1],triangle[3][2])
    
    listVertices.append(newVertex1)
    newVertex1_i = len(listVertices) - 1
    listVertices.append(newVertex2)
    newVertex2_i = len(listVertices) - 1
    listVertices.append(newVertex3)
    newVertex3_i = len(listVertices) - 1
    # write to the list of edges
    # Define the faces by index numbers. Each faces is defined by 4 consecutive integers.
    # For triangles you need to repeat the first vertex also in the fourth position.
    newFace = (newVertex1_i,newVertex2_i,newVertex3_i)
    listFaces.append(newFace)
    

# Build the mesh in Blender's API

mesh = bpy.data.meshes.new("penroseMesh")   # create a new mesh  
 
ob = bpy.data.objects.new("Pyramid", mesh)      # create an object with that mesh
ob.location = Vector((0,0,0)) #by.context.scene.cursor_location   # position object at 3d-cursor
bpy.context.scene.objects.link(ob)                # Link object to scene
 
# Fill the mesh with verts, edges, faces 
mesh.from_pydata(listVertices,[],listFaces)   # edges or faces should be [], or you ask for problems
mesh.validate(True)
#mesh.update(calc_edges=True)    # Update mesh with new data


repeating a vert index for a triangle seems like a leftover from pre-2.63 times. The .vertices_raw property always held 4 vertex indices, no matter if tris or quad, while .vertices had 3 indices for a triangle and 4 for a quad. Now that we have Ngon support, you shouldn’t repeat any index, just provide all indices ones, no matter if tris, quad or ngon.

Here’s your script with remove_doubles and recalc_face_normals built-in (using bmesh module):

import bpy
import bmesh
import math
from mathutils import Vector

subDivIterations = 5

goldenRatio = (1 + math.sqrt(5)) / 2

def subdivide(triangles):
    result = []
    for color, A, B, C in triangles:
        if color == 0:
            # Subdivide red triangle
            P = A + (B - A) / goldenRatio
            result += [(0, C, P, B), (1, P, C, A)]
        else:
            # Subdivide blue triangle
            Q = B + (A - B) / goldenRatio
            R = B + (C - B) / goldenRatio
            result += [(1, R, C, A), (1, Q, R, B), (0, R, Q, A)]
    return result

# Create wheel of red triangles around the origin
def createWheel():
    triangles = []
    
    for i in range(10):
        theta_i = i*2.0*math.pi/10.0
        theta_ip1 = (i+1)*2.0*math.pi/10.0
        
        x_i = math.cos(theta_i)
        x_ip1 = math.cos(theta_ip1)
        
        y_i = math.sin(theta_i)
        y_ip1 = math.sin(theta_ip1)
        
        A = Vector((0.0,0.0,0.0))
        B = Vector((x_i,y_i,0.0))
        C = Vector((x_ip1,y_ip1,0.0))
        
        if i % 2 == 0:
            B, C = C, B  # Make sure to mirror every second triangle
        triangles.append((0, A, B, C))
    return triangles
    


# Generate map of tiling
listTriangles = createWheel()
for x in range(subDivIterations):
    listTriangles = subdivide(listTriangles)

# Construct the lists necessary for generation of geometry
listVertices = []
listFaces = []
for triangle in listTriangles:
    # write the vertex coords to the list, and remember the vertex indices
    # In Blender, the mesh data stores each vertex as a tuple of 3 floats.
    newVertex1 = (triangle[1][0],triangle[1][1],triangle[1][2])
    newVertex2 = (triangle[2][0],triangle[2][1],triangle[2][2])
    newVertex3 = (triangle[3][0],triangle[3][1],triangle[3][2])
    
    listVertices.append(newVertex1)
    newVertex1_i = len(listVertices) - 1
    listVertices.append(newVertex2)
    newVertex2_i = len(listVertices) - 1
    listVertices.append(newVertex3)
    newVertex3_i = len(listVertices) - 1
    # write to the list of edges
    # Define the faces by index numbers. Each faces is defined by 4 consecutive integers.
    # For triangles you need to repeat the first vertex also in the fourth position.
    newFace = (newVertex1_i,newVertex2_i,newVertex3_i)
    listFaces.append(newFace)
    

# Build the mesh in Blender's API

mesh = bpy.data.meshes.new("penroseMesh")   # create a new mesh  
 
ob = bpy.data.objects.new("Pyramid", mesh)      # create an object with that mesh
ob.location = Vector((0,0,0)) #by.context.scene.cursor_location   # position object at 3d-cursor
bpy.context.scene.objects.link(ob)                # Link object to scene
 
# Fill the mesh with verts, edges, faces 
mesh.from_pydata(listVertices,[],listFaces)   # edges or faces should be [], or you ask for problems
mesh.validate(True)
#mesh.update(calc_edges=True)    # Update mesh with new data

bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bm.to_mesh(mesh)
mesh.update()

Ok, thought so. Thanks so much for your help!

Question about your code:
Why convert to bmesh and then back to mesh? If “mesh” is obselete, why is it used?

Any idea who I should speak to regarding updating the online manual (and example template)?
It’s out of date I guess.

all meshes are bmeshes, there’s no obsolete “mesh” type. Don’t confuse bmesh meshes (meshes that can have tris, quads and ngons) and the bmesh module for python scripting!

The latter provides access to blender’s internal bmesh data structures. It’s often more convenient and easier to use compared to the standard API (bpy.*), but adds an (memory?) overhead.

I use the BMesh module in your script, 'cause you can perform the cleanup-operations on the mesh without mode changes (for bpy.ops equivalents, mesh had to be in editmode to perform them).

Ah - I was wondering how you skipped the mode changes. Ok, that’s cool. Thanks!