Build a clock with Blender and Python

Tick Tock

Article from Issue 276/2023
Author(s):

With a little help from Blender you can create your own 3D models – including animations. This article shows you how to assemble a partially automated virtual watch model with Blender and Python.

The free Blender program package for modeling, texturing, animation, and video and image editing can be found in the package repositories of most Linux distributions, and there is also a distribution-independent Snap package. Typing the snap install blender command at the command line installs the graphics suite. If you need more in the way of installation options, you can download the application directly from the Blender Foundation website [1]. While you are there, you can also access the extensive documentation, tutorials, and examples and grab the versions for Windows, macOS, and others. On top of this, because the Blender Foundation provides the source code, the program can even be adapted to run on less common operating systems. Of course, in this case you will have to compile Blender yourself.

On Linux, either open Blender in a terminal or use the Open in Terminal shortcut in the window manager of your choice. If neither succeeds, first launch Blender and then try to discover the path of the Blender installation in the Python Interactive Console by typing bpy.app.binary_path. Then enter this as the start parameter for the call in the terminal. This ensures that error messages and output from the Python command print("Hello world"), for example, also reach their target (i.e., the terminal window). This is especially important if you don't just want to use the Blender Python Console in Blender's Scripting workspace and individual commands, but also want to call Python programs you saved previously.

Desktop

Blender's user interface is divided into workspaces. Each of them hosts a different collection of editors and windows that appear at specific positions on the screen. The program lists the available workspaces on the right below the menu bar. They include Layout, Modeling, Sculpting, UV Editing, and Animation. Almost all workspaces contain the 3D viewport window and other windows.

Figure 1 shows Blender after starting with the default basic scene. Basically, you can assemble 3D scenes to your heart's content directly in the interface. There are numerous tutorials on the Internet for getting started with Blender; a description of this would go beyond the scope of this article due to the range of functions to cover. Instead, the task at hand is to enable you to write tools for yourself in the form of Python scripts that let you build complex scenes one by one in a scripted way.

Figure 1: Blender with the default basic scene in a 3D view: A perspective view on a cube, a camera, and a light source.

Clock Face

As an example, let's look at a three-dimensional clock face with raised numerals and hands. What's interesting in this context is the positions of the numerals on the dial. The only way to position them correctly is to use some trigonometric functions you may still recall from math.

But let's start by partially assembling the dial by hand, and at the same time you'll get to know some Blender Python. To do this, switch to the Scripting workspace. This will take you to a programming interface (Figure 2). On the left, you can see the familiar, somewhat reduced 3D viewport window. Below that are the Blender Python Console and the Info line. Clicking on Add | Mesh | Plane creates a square base for the dial. Parallel to this, Blender logs your manual actions in the Info window bottom left. The window content is very similar to the first line of Listing 1.

Listing 1

Blender Action

01 bpy.ops.mesh.primitive_plane_add(size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
02 bpy.ops.mesh.primitive_plane_add(size=5)
03 {'FINISHED'}
Figure 2: With the Scripting workspace enabled, the application displays the Blender Python Console and an info line next to the 3D viewport window.

One thing up front: Blender forwards keyboard input directly to whichever window you mouse over. This means that you do not need to click on the window beforehand. But, especially in the scripting workspace, you need to make sure that the mouse pointer is in the right position when entering keystrokes. Start by moving the mouse over to the 3D viewport window, press X, and confirm the Delete prompt. The square base area should disappear again and another line will be logged in the Info window.

Then build the square base area again, but this time with a different size and using command input in the Blender Python Console (Listing 1, line 2). In the 3D viewport window, the program now shows you a larger version of the square base area. Some positive feedback appears in the Blender Python Console (line 3). The Info line below contains the complete log entry including all default values that are not specified.

If you want to familiarize yourself with how variables and loops work as a small exercise before actually building the dial, why not first create a small pyramid? To do this, enter the code from Listing 2 in the Blender Python Console. Note that loops in Python are always formed by indenting the loop block. In the Blender Python Console, end the indentation using a blank line at the end of the loop.

Listing 2

Pyramid Builder

i=1
while i < 9:
  bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, align='WORLD', location=(0, 0, i-0.5), scale=(9-i, 9-i, 1))
  i+=1

Key Concepts

You can use the Python API to access the data in Blender in the same way as via the user interface. Basically, what you can access by pressing switches and buttons and selecting menu items can also be controlled using Python. All the data of the currently loaded Blender file can be accessed using the bpy.data module.

The application organizes the data in Collections, which you access either by index or string. In the Outliner, there are precisely three objects after starting the program with the standard scene: bpy.data.objects['Camera'], bpy.data.objects['Cube'], and bpy.data.objects['Light']. Objects can also be retrieved via their index numbers (e.g., the cube is bpy.data.objects[1]).

As you are probably familiar with from the dialog-oriented interface, in many cases you do not select the object by typing its name, but use the mouse instead. You can even pick any number of objects at the same time, for example, to move them together. However, only one of them acts as the active object to which all actions refer. In addition to this, all the displayed values come from the active object. Operations that you want to act on multiple objects use this object as a reference, for example, if you assign a different material.

Within Python, you can access the active object using the bpy.context.object command and access all the selected objects using bpy.context.selected_objects. However, access to this Context is read-only. To change the values, you will need to call API functions or use bpy.data.

Listing 3 shows the part of the Python script that positions the digits on the dial (Figure 3). Because the three-dimensional numerals have different widths and heights, you need to pay attention to the correct setting of the origin points of the numerals on the clock face. This ensures correct centering of the one- and two-digit numbers. Figure 4 illustrates the relationship of the trigonometric functions from Listing 4. Listing 5 shows the entire script (available online at [2]).

Listing 3

Preparations and Cleanup

import bpy
import math
import datetime
bpy.ops.object.select_pattern(pattern='text*')
bpy.ops.object.select_pattern(pattern='*hand')
bpy.ops.object.select_pattern(pattern='plane')
bpy.ops.object.delete()
hour = datetime.datetime.now().hour
minute = datetime.datetime.now().minute
hourangle = 360 / 12 * (hour + minute/60)
minuteangle = 360 / 60 * minute
fromCenter = 3
angleInc = 30 * math.pi/180
bpy.ops.mesh.primitive_plane_add(size=9, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))

Listing 4

Positioning the Digits

i = 1
while i <= 12:
  x=fromCenter * math.sin(angleInc * i)
  y=fromCenter * math.cos(angleInc * i)
  z=0.2
  bpy.ops.object.text_add(location=(x,y,z))
  ob=bpy.context.object
  ob.data.body = str(i)
  ob.modifiers.new("SOLIDIFIED TEXT","SOLIDIFY").thickness=0.2
  bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
  i+=1

Listing 5

Finished Python Script

import bpy
import math
import datetime
bpy.ops.object.select_pattern(pattern='Text*')
bpy.ops.object.select_pattern(pattern='*Pointer')
bpy.ops.object.select_pattern(pattern='plane')
bpy.ops.object.delete()
hour = datetime.datetime.now().hour
minute = datetime.datetime.now().minute
hourangle = 360 / 12 * (hour + minute/60)
minuteangle = 360 / 60 * minute
fromCenter = 3
angleInc = 30 * math.pi/180
bpy.ops.mesh.primitive_plane_add(size=9, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
i = 1
while i <= 12:
  x=fromCenter * math.sin(angleInc * i)
  y=fromCenter * math.cos(angleInc * i)
  z=0.2
  bpy.ops.object.text_add(location=(x,y,z))
  ob=bpy.context.object
  ob.data.body = str(i)
  ob.modifiers.new("SOLIDIFIED TEXT","SOLIDIFY").thickness=0.2
  bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
  i+=1
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0.05), rotation=(0, 0, 0), scale=(1, 1, 1))
bpy.context.selected_objects[0].name="hour hand"
bpy.context.selected_objects[0].dimensions=( 0.2, 2, 0.1)
bpy.ops.transform.translate(value=(0, 1, 0))
bpy.context.scene.tool_settings.use_transform_data_origin = True
bpy.ops.transform.translate(value=(0, -1, 0))
bpy.context.scene.tool_settings.use_transform_data_origin = False
bpy.ops.transform.rotate(value= angleHours * math.pi/180)
bpy.context.object.lock_location[0] = True
bpy.context.object.lock_location[1] = True
bpy.context.object.lock_location[2] = True
bpy.context.object.lock_rotation[0] = True
bpy.context.object.lock_rotation[1] = True
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0.1), rotation=(0, 0, 0), scale=(1, 1, 1))
bpy.context.selected_objects[0].name="minute hand"
bpy.context.selected_objects[0].dimensions=( 0.2, 2.6, 0.1)
bpy.ops.transform.translate(value=(0, 1.3, 0))
bpy.context.scene.tool_settings.use_transform_data_origin = True
bpy.ops.transform.translate(value=(0, -1.3, 0))
bpy.context.scene.tool_settings.use_transform_data_origin = False
bpy.ops.transform.rotate(value= angleMinutes * math.pi/180)
bpy.context.object.lock_location[0] = True
bpy.context.object.lock_location[1] = True
bpy.context.object.lock_location[2] = True
bpy.context.object.lock_rotation[0] = True
bpy.context.object.lock_rotation[1] = True
Figure 3: Blender with the fully constructed clock in the activated Scripting workspace.
Figure 4: The Blender clock with the formulas as image texture in the Layout workspace.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Free 3D animation

    You need good software and plenty of CPU power to create virtual worlds. Luckily for today’s animators, powerful PCs are inexpensive, and some excellent animation tools are absolutely free.

  • Blender 3D Animation

    Blender not only generates realistic single frames; it is also capable of capturing the natural movements of people and animals. We’ll introduce you to some of Blender’s animation features.

  • Fragrance Workshop

    Blender's massive feature set can seem overwhelming at first. Choosing a manageable project can help you get started.

  • Playful Progress: Blender 2.49 Enhances Game Engine and Panoramic View

    Blender 2.49 is still profiting from its experience developing Big Buck Bunny and the Yo Frankie! Blender game. Much of what goes into the new release is on account of the Game Engine, with its video integration and performance boost.

  • Free Software Projects

    Free software enjoys an excellent reputation in the field of 3D graphics. In addition to Blender, other projects stand out: MakeHuman creates realistic 3D humans, and Art of Illusion – with its intuitive interface – lets users enter the world of animated movies.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News