When working with geometry in Blender using the Blender Python API, we may need to create a new polygon in the current mesh that lies in the same plane as one of the existing polygons of the mesh.
For simplicity, we’ll assume we’re in object editing mode and don’t switch modes.
First, create a BMesh object and copy the geometry of the currently active mesh into it. Also recalculate the vertex table for correct indexing.
|
1 2 |
bm = bmesh.from_edit_mesh(bpy.context.object.data) bm.verts.ensure_lookup_table() |
Set the initial coordinates for the new polygon. Let’s say it’s a square centered at (0.0, 0.0) and has a side length of 2. We define the initial coordinates in the 2D space of the polygon’s plane.
|
1 2 3 4 5 6 7 8 |
new_face_cos = [ Vector((-1.0, -1.0)), Vector((-1.0, 1.0)), Vector((1.0, 1.0)), Vector((1.0, -1.0)) ] # [Vector((-1.0, -1.0)), Vector((-1.0, 1.0)), Vector((1.0, 1.0)), Vector((1.0, -1.0))] |
To manipulate mesh coordinates in the global coordinate system, we’ll get a pointer to our mesh’s transformation matrix and also make an inverted copy of it.
|
1 2 |
world_matrix = bpy.context.object.matrix_world world_matrix_inv = bpy.context.object.matrix_world.inverted() |
Again, for simplicity of example, let’s assume our original mesh consists of just one triangular face, in the plane of which we need to construct a new polygon. In real cases, we can take, for example, three selected points on any polygon of the mesh.
Get the coordinates of three points of the mesh polygon in the global coordinate system.
|
1 2 3 4 5 6 7 |
p0 = world_matrix @ bm.verts[0].co p1 = world_matrix@ bm.verts[1].co p2 = world_matrix @ bm.verts[2].co # <Vector (1.7921, 1.1420, 0.9210)> # <Vector (1.3010, 1.0365, 0.7220)> # <Vector (1.4724, 1.3446, 0.8692)> |
Now, based on these three points, we can calculate the “basis”—the transformation matrix defined by these three points.
|
1 2 3 4 5 6 |
vec_x = (p1 - p0).normalized() vec_temp = (p2 - p0).normalized() vec_z = vec_x.cross(vec_temp).normalized() vec_y = vec_z.cross(vec_x).normalized() rot_matrix = Matrix((vec_x, vec_y, vec_z)).transposed() basis = rot_matrix.to_4x4() |
Add an offset to the basis so that the starting point of the basis coincides with the first of the three points of our original mesh polygon.
|
1 |
basis.translation = p0 |
Now we have a ready-made transformation matrix for recalculating coordinates into the plane of the original polygon.
|
1 2 3 4 5 6 |
print(basis) # <Matrix 4x4 (-0.9090, -0.2745, 0.3136, 1.7921) # (-0.1951, 0.9452, 0.2617, 1.1420) # (-0.3683, 0.1766, -0.9128, 0.9210) # ( 0.0000, 0.0000, 0.0000, 1.0000)> |
Сonvert the 2D coordinates of our new polygon into 3D coordinates using the basis.
|
1 2 3 4 5 6 7 8 9 10 11 |
world_cos = [] for dx, dy in new_face_cos: local_vector = Vector((dx, dy, 0.0)) world_pos1 = basis @ local_vector world_pos = world_matrix_inv @ world_pos1 world_cos.append(world_pos) # [Vector((-0.4627564549446106, -0.079158253967762, -1.2500395774841309)), # Vector((0.6229296326637268, 1.405601143836975, -0.4646884799003601)), # Vector((-0.48234763741493225, 1.3329771757125854, 1.2005704641342163)), # Vector((-1.5680336952209473, -0.1517820954322815, 0.415219247341156))] |
Add new points to the BMesh using the calculated coordinates.
|
1 |
bm_verts = [bm.verts.new(pos) for pos in world_cos] |
And fill them with a polygon.
|
1 |
bm.faces.new(bm_verts) |
For correct display, recalculate the normals.
|
1 |
bmesh.ops.recalc_face_normals(bm, faces=bm.faces) |
That’s all. Now we can return the updated geometry from the BMesh object to the active mesh in the scene and clear the BMesh we no longer need.
|
1 2 |
bmesh.update_edit_mesh(bpy.context.object.data) bm.free() |
This creates a new polygon in the mesh that lies exactly in the plane of the original polygon from which we took three points.
Note that the center of the new polygon lies at the point with index 0 on the original polygon, as this is what we set as the center of the coordinate system in the basis.

.blend file on Patreon