We’re going to build a simple triangle just to get some basic blender concepts across.

The Blender Python API: bpy

The Blender python module, bpy, provides access (through the blender API) to a wide range of functionalities and data within the Blender development environment, including:

  • Creating, modifying, and manipulating 3D objects, meshes, materials, and textures.
  • Managing scenes, cameras, lights, and animation.
  • Rendering images and animations.
  • Importing and exporting data in various file formats.
  • Accessing and modifying properties and settings of Blender’s interface and tools.
  • Automating repetitive tasks and creating custom tools using Python scripting.

By utilizing the bpy module you can extend Blender’s capabilities and automate various tasks, making it a powerful tool for 3D content creation and customization.

Install The Blender bpy Module (Optional Step)

The Blender install provides a built-in Python interpreter that includes the bpy module. You can create, load, save and run scripts from the built-in interpreter. The built-in interpreter is a bit basic.

If you would like the option of having access to your favourite development tools in an external IDE you can install the bpy module so your IDE can autocomplete for example.

(venv) ubuntu@goodboy:~# python3 -m pip install bpy 

However, you must load and run your scripts from within the Blender built-in interpreter.

Building A Simple Script

Import modules bpy, and bmesh

We’re going to start by building a simple script called simpletriangle.py

First we import the bpy module,

import bpy

Then we import the bmesh module,

import bmesh

The bmesh module is a Python module provided by Blender, specifically for working with BMesh data. BMesh is a versatile data structure within Blender that represents mesh geometry. The bmesh module allows you to create, modify, and manipulate meshes programmatically using Python scripts.

The bmesh module is installed as part of the Blender install process, and as part of the optional module install step above.

Create a new mesh data block

We are going to create a mesh data block called ‘MyFirstMesh’. This data block will hold vertices, edges, and faces data of your creations.

mymesh = bpy.data.meshes.new("MyFirstMesh")
  • bpy.data.meshes’ refers to the collection of mesh data in Blender.
  • .new(“MyFirstMesh”) is a method that creates a new empty mesh data block with the given name “MyFirstMesh” and returns a reference to it.
  • We then assign the newly created mesh data block to the variable mymesh, allowing us to access and manipulate it further.
  • If you wanted to you could also reference your mesh by using bpy.data.meshes[‘MyFirstMesh’]

You can create as many meshes as you like.

Populating your mesh data block with bmesh

We are going to build a very simple virtex and face representation of a triangle.

First we create a BMesh object,

bm = bmesh.new()

Then we define the vertices by adding vertex coordinates to our bmesh object,

v1 = bm.verts.new((0, 0, 0))
v2 = bm.verts.new((1, 0, 0))
v3 = bm.verts.new((0, 1, 0))

Then we define the face by adding the vertices that bound the face to our bmesh object,

bm.faces.new((v1, v2, v3))

Once we are satisfied with the data points we have constructed in our BMesh data structure it is time to transfer this data over to our mesh.

This is done with the following command,

bm.to_mesh(mymesh)

After calling this function, the mesh data block mymesh will contain the updated mesh geometry.

Our BMesh object has completed its purpose and we no longer need the object or that data we placed in it. We should clear all the data by calling this function,

bm.free()

After calling this function, the BMesh object is no longer accessible or usable. It is important to call this function when you are done working with the BMesh object to release memory resources.

Visualizing Mesh Data

So far we have covered the process to manipulate data points and transfer these data points to a mesh.
This process does not automatically update the mesh in the Blender scene or viewport, your design is not visible.

If we want to visualise and see our design, we need to assign the updated mydata data block to an object. We then need to link that object to the scene collection.

So here we create a new object called MyObject and assign it to our mesh mymesh.

obj = bpy.data.objects.new("MyObject", mymesh)
  • ‘bpy.data.objects’ refers to the collection of objects in Blender.
  • .new(“MyObject”, mymesh) is a function that creates a new object with the given name “MyObject” and associates it with the provided mesh we created ‘mymesh‘.
  • We then assign the newly created object to the variable obj, allowing us to access and manipulate it further.
  • If we wanted to, we could also reference the object using bpy.data.objects[‘MyObject’]

We then need a handle on the current scene from the Blender context. We can do this with the following command,

scene = bpy.context.scene
  • bpy.context provides access to the current context in Blender, which includes various information and settings relevant to the current state of the program.
  • scene is a reference to the current scene in Blender.
  • bpy.context.scene retrieves the current scene from the Blender context.
  • We then assign it to the variable scene so we can conveniently add, access, and manipulate properties and objects within the current scene.

We now need to link our object ‘obj’ to the collection of objects in the current scene ‘scene’ so that it is visible and manipulable within the Blender viewport.

We can do this with the following command,

scene.collection.objects.link(obj)
  • scene.collection refers to the collection of objects in the current scene.
  • .objects represents the collection of objects within the scene collection.
  • .link(obj) is a method that adds the specified object ‘obj’ to the collection, making it a part of the scene ‘scene’.

Blender Objects

In Blender, an “object” typically represents a collection of vertices, edges, and faces that form a coherent geometry. By default, an object in Blender is associated with a single mesh data block, which contains the vertex, edge, and face data for that object.

If you are solely interested in manipulating the mesh data using BMesh and don’t require visual representation or integration with the Blender scene, you can omit the creation and linking of the object.

Script In Full

The complete script will generate the following Blender object,

If you open the script inside blender, in the scripting environment,

and hit the play icon, our simple object will be rendered.

BMesh Script

Here is the complete script that contains all the components we described in detail above.
This script is the version that uses a BMesh data structure to help describe our object.

#import Blender API modules
import bpy
import bmesh

# Create a new mesh
mesh = bpy.data.meshes.new("MyMesh")

# Create a new object and assign it to the mesh.
obj = bpy.data.objects.new("MyObject", mesh)

# Get a handle to the current scene in Blender.
scene = bpy.context.scene

# Link our object to the scene.
scene.collection.objects.link(obj)

# Create a BMesh data structure object.
bm = bmesh.new()

# Define the vertices coordinates and add them to the BMesh data structure.
v1 = bm.verts.new((0, 0, 0))
v2 = bm.verts.new((1, 0, 0))
v3 = bm.verts.new((0, 1, 0))

# Define a face by stating the bounding vertices and add to the BMesh data structure.
bm.faces.new((v1, v2, v3))

# Transfer the data in the BMesh data structure to the mesh.
bm.to_mesh(mesh)

# Clear and release the memory used by the BMesh data object.
bm.free()

The .from_pydata(vertices, edges, faces) Script

You don’t need to use a BMesh data structure and could provide raw data instead.

Virtex Definitions

You can define vertex coordinates yourself as a list of (x,y,z) tuples.

Edge Definitions

You can then define edges as a list of (a,b) tuples, where a & b are the index values of the virtex tuples bounding the edge.

Face Definitions

Faces can then be defined as a list of (a,b,c) tuples, where a,b & c are the index values of the virtex tuples bounding the face.

Here is a script that replicates the same object and demonstrates how to do this using .from_pydata().

# Import Blender API modules
import bpy

# Create a new mesh
mesh = bpy.data.meshes.new("MyMesh")

# Create a new object and assign it to the mesh.
obj = bpy.data.objects.new("MyObject", mesh)

# Get a handle to the current scene in Blender.
scene = bpy.context.scene

# Link our object to the scene.
scene.collection.objects.link(obj)

# DEFINE vertices, edges, and tri_face DATA POINTS
# Define the vertices as a list of tuples
# Each tuple represents the coordinates of a vertex in three-dimensional space. (x,y,z)
vertices = [(0, 0, 0), (1, 0, 0), (0, 1, 0)]

# Define the edges, i'm going to not do this but if you did,
# this would be a list of tuples (a,b) where a and b are the index of the
# list vertices.  e.g.  edges = [(0,1) ,(1,2), (2,0)]
edges = []

# Define a face by stating the index of the bounding vertices in the list vertices.
tri_face = [(0, 1, 2)]

# Create the mesh using from_pydata
mesh.from_pydata(vertices, edges, tri_face)
# Inform the mesh data block that its data has been modified and needs to be updated.
mesh.update()

Note that the .from_pydata() method expects the data to be in the specific format described above, with consistent vertex, edge, and face indices.

Use In Unity

You can export this object as an FBX file and import it in to unity.

BUT… you will not be able to apply materials or textures.

To do this we will need to know about,

  • defining and applying materials to faces in blender.
  • vertex winding order and the calculation of the normal vector using the cross product.
  • face loops, half edges, and associated loop vertices
  • UV coordinates (U,V are 2D coordinates, I swear they used something that looks like UltraViolet to confuse us)

I’ll be covering this and more in the next article.

Leave a Reply