Skip to main content

Creating a 3D Mesh with Python and Blender

About Us
Published by JET BI
30 September 2024
194

Building a 3d Mesh using python and blender

The Blender application is a leading free tool for 3D modeling for various applications. The 3d models can be saved in a specific format for later use in various developments for example, in game development.


The application's start window is shown below. It contains 3 initial objects, each of which has specified properties: size, color, coordinates, etc. These objects can also be built using a Python script, specifying all the necessary parameters for each object. Let's take a closer look at this.

To work with Blender via the Python programming language, you need to use the bpy library. The first step to creating a new object using bpy is to import the bpy module. To do this, click on the Scripting tab, then New and add the next code line:

  • import bpy




This will give you access to the Blender workspace through code, the ability to manipulate current objects and create new ones. This library contains many methods for creating standard shapes, but to build a complex object using coordinates, you need to create a Mesh object. To do this, use the method bpy.data.meshes.new(<Mesh name>). Then create a new object which contains this mesh - bpy.data.objects.new(<Object name>, mesh)


 

Next, to build a Mesh figure using the available coordinates, you need to use the method mesh.from_pydata(verts, edges, faces), where verts, edges and faces are arrays of coordinates of the vertices, edges and surfaces of the figure, respectively.

First parameter - verts - is used to get all points which are included in the mesh. For example, it can be populated with these values:

  • verts = [ (0,0,0), (0,2,0), (0,1,2), (0,3,2) ]

The other two parameters are needed to connect the points with lines or planes, depending on what should be in the figure. For example, if you fill the faces parameter with these values, it will build 2 flat triangles connecting points numbered 1, 2, and 3, as well as 2, 3, and 4 (according to the index numbers of the verts array):

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

This is enough to build a parallelogram shape using coordinates, but you can add lines connecting the selected points so that you can then edit them manually as object elements.

  • edges = [ (0,1), (1,2), (2,0), (1,3), (3, 2) ] 

Full code fragment is provided below:


And the result looks like this:


This example shows how to build a flat parallelogram figure using fixed coordinates of points. The same code is suitable for building more complex objects of any shape in 3D space, you just need to set the coordinates of the points and connect them. In our example, I will change the coordinates to turn the 2D shape into 3D.



This script builds a 3D figure consisting of 8 points and 6 faces, and each face is a quadrilateral. The final 3D figure looks like this



In the same way, you can build more complex 3D objects. To do this, it is enough to know the coordinates of the points and the order in which they are connected to build flat faces. Each face can be edited using Blender's manual tools if necessary. However, it is worth remembering that a large number of figure elements requires a lot of memory. Therefore, it is advisable to use an algorithm to reduce the array of coordinates used to obtain the desired object.

If you need to save a 3D object in a file, you can do it manually or use a method of bpy library. It contains many parameters, but only one required:

  • bpy.ops.wm.obj_export(filepath="<path to target file with .obj format>")

Below there is an example of creating a 3D object obtained from a png image. In addition to bpy, this will require the bmesh, cv2, mathutils and numpy libraries.

As example, we’ll use this image of human with green background

 

The first step is to get a list of coordinates of all points of the contour of the object on a green background. This will require the cv2 library. As a result, a list of coordinates of human contour points will be obtained. This list will be used later when building a mesh.


The second step is to build a mesh from the points coordinates. The result should be a human outline like this.

 

The following code is used to get this result

 

Now it is necessary to obtain a closed image of a person by adding a face to the resulting contour. Use the method:



The result now looks like this:

 

To complete the construction of a 3D object, it is necessary to extrude  a plane with the selected thickness value. Use the method bpy.ops.mesh.extrude_region_move:


As the result, you’ll get something like this:


 

The full code will be provided below.

 

Prerequisites for code execution:

  1. Need to install library cv2 which may not be pre-installed in Blender. You can try to use this code in Blender:

import pip

pip.main(['install', 'cv2'])

  1. If the installation is not successful, you will need to install the module from Terminal and add it to the Blender application. To install the cv2 module, open your Terminal in VS Code, where you execute Python scripts. Execute this command

pip install opencv-python

Then you need to open your path which contains all python libraries. For example, my path looks like this: C:\Users\jbUser\AppData\Local\Programs\Python\Python312\Lib\site-packages and it should include 2 folders named cv2 and opencv_python-4.9.0.80.dist-info. Copy these folders to the folder which contains all installed python libraries of your Blender app. In my case, it’s: C:\Program Files\Blender Foundation\Blender 4.2\4.2\python\lib\site-packages

After that, libraries should work in Blender scripts. If it shows an error, may need to use other version of python or its libraries.

 

Script for Blender is provided here:

import bpy
import bmesh
import mathutils
import numpy as np
import cv2

def remove_green_background(image_path):

    image = cv2.imread(image_path)

    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    lower_green = np.array([40 - 30, 40, 40]) 
    upper_green = np.array([40 + 30, 255, 255]) 

    mask = cv2.inRange(hsv, lower_green, upper_green)

    mask_inv = cv2.bitwise_not(mask)
    
    result = cv2.bitwise_and(image, image, mask=mask_inv)

    contours, _ = cv2.findContours(mask_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    if contours:

        largest_contour = max(contours, key=cv2.contourArea)
        
        contour_coords = largest_contour.reshape(-1, 2).tolist()
        return contour_coords
    else:
        return None

def build_from_contour(points, export_path, HEIGHT, SCALE):
    mesh = bpy.data.meshes.new("HumanMesh")
    obj = bpy.data.objects.new("HumanObj", mesh)

    bpy.context.collection.objects.link(obj)

    bm = bmesh.new()

    verts = [bm.verts.new((x, y, 0)) for x, y in points]
    
    center = mathutils.Vector((0, 0, 0))
    for v in verts:
        center += v.co
    center /= -len(verts) * SCALE
    
    if len(verts) > 2:
        for i in range(len(verts)):
            bm.edges.new((verts[i], verts[(i + 1) % len(verts)]))

    bm.to_mesh(mesh)
    bm.free()
    bpy.context.view_layer.objects.active = obj
    
    bpy.ops.object.mode_set(mode='EDIT')

    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.mesh.edge_face_add()

    bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={"use_normal_flip":False, 
                                                            "use_dissolve_ortho_edges":False, 
                                                            "mirror":False}, 
                                                            TRANSFORM_OT_translate={"value":(0, 0, -HEIGHT), 
                                                            "orient_type":'NORMAL', 
                                                            "orient_matrix":((-1, 0, 0), (0, -1, 0), (0, 0, 1)), 
                                                            "orient_matrix_type":'NORMAL', 
                                                            "constraint_axis":(False, False, True), 
                                                            "mirror":False, 
                                                            "use_proportional_edit":False, 
                                                            "proportional_edit_falloff":'SMOOTH', 
                                                            "proportional_size":1, 
                                                            "use_proportional_connected":False, 
                                                            "use_proportional_projected":False, 
                                                            "snap":False, 
                                                            "snap_elements":{'INCREMENT'}, 
                                                            "use_snap_project":False, 
                                                            "snap_target":'CLOSEST', 
                                                            "use_snap_self":True, 
                                                            "use_snap_edit":True, 
                                                            "use_snap_nonedit":True, 
                                                            "use_snap_selectable":False, 
                                                            "snap_point":(0, 0, 0), 
                                                            "snap_align":False, 
                                                            "snap_normal":(0, 0, 0), 
                                                            "gpencil_strokes":False, 
                                                            "cursor_transform":False, 
                                                            "texture_space":False, 
                                                            "remove_on_cancel":False, 
                                                            "use_duplicated_keyframes":False, 
                                                            "view2d_edge_pan":False, 
                                                            "release_confirm":False, 
                                                            "use_accurate":False, 
                                                            "use_automerge_and_split":False})

    bpy.ops.object.mode_set(mode='OBJECT')
    
    obj.location = center
    obj.scale /= SCALE
    bpy.ops.wm.obj_export(filepath = export_path)

# Change path to your image with green background
IMAGE_PATH = 'C:\Work\Projects\PythonScripts\VideoFrames\Frames\Green_Human.avi\\0000000000.png'
#Change path to your target path to save .obj file
EXPORT_PATH = 'C:\Work\Projects\PythonScripts\VideoFrames\Frames\Human.obj'
HEIGHT = 50
SCALE = 10
points = remove_green_background(IMAGE_PATH)
if points != None:
    build_from_contour(points, EXPORT_PATH, HEIGHT, SCALE)


In this example, the background color is green because it is brighter than the colors of the subject. For better quality, avoid wearing green clothing when photographing the subject.

This approach allows you to build a 3D object using only the image contour points in order to optimize the load of the Blender application. The script greatly simplifies the process of building an object, replacing manual actions with tools for transforming the shape of objects.

 


Alexander Zherebilo
Salesforce developer
image
Expertise
Question to the expert
image

We have available resources to start working on your project within 5 business days

1 UX Designer

image

1 Admin

image

2 QA engineers

image

1 Consultant

image

The Hiring Process

1. Submitting a resume for a vacancy

2. Communication with a recruiter

3. HR Interview

4. Technical interview

5. Conversation with the HR director

6. Offer

7. Welcome aboard!

Clutch review

Related Articles
All articles
image
Why Your Salesforce Flows Are Agentforce's Biggest Problem
This article argues that the most underestimated risk in Agentforce deployments isn't data quality — it's the automation layer: years of overlapping Flows, Process Builder processes, Apex triggers, and managed package logic that no one has reviewed end-to-end. It explains why AI agents inherit automation complexity without the tribal knowledge human admins carry, why technical debt only becomes visible after an agent hits it in production, and why a clean demo is no indicator of production readiness. The article closes with a concrete, tool-by-tool inventory approach using Flow Trigger Explorer, Salesforce Optimizer, Setup Audit Trail, Apex Debug Logs, Agent Builder, and Health Check — scoped to the specific processes the agent will actually use rather than the whole org.
23 July 2026
image
How to Wire Multiple Salesforce Projects in One Org Without Breaking Everything
This article maps the real integration patterns that emerge when multiple Salesforce projects — both managed packages and unpackaged code — share a single org. It covers four concrete patterns: attaching custom triggers to package-owned objects, calling global members exposed by managed packages, writing directly into another project's objects, and runtime-guarded reads of package data. It then addresses access control for authenticated and guest users, including the Master-Detail wall and the without sharing elevation pattern. The piece closes with eight concrete risks (compile-time dependencies that block uninstall, upgrade coupling, silent cascade failures, access invisible to admins) and six actionable recommendations for keeping cross-project coupling manageable.
08 July 2026
image
GraphQL in LWC: Queries, Mutations, and When to Use Apex Instead
This article explains how GraphQL works inside Lightning Web Components, covering the query and mutation syntax developers need to fetch and modify Salesforce data efficiently. It walks through filtering, sorting, and pagination in queries, shows how to create, update, and delete records with mutations via executeMutation(), and details the lightning/graphql module setup with the graphql wire adapter. The piece also covers practical use cases (dashboards, record detail pages, mobile apps) and weighs GraphQL's benefits against its current limitations compared to Apex.
26 June 2026