Generate a list of Blender object information

This isn't the next killer add-on you must have, but it could prove useful if you find yourself like me in the following situation: You are working on a high poly model consisting of dozens of different meshes and at some point you need to start thinking about your polygon budget.

So you want to focus on the meshes with the largest number of tris first, but determining how many tris there are requires selecting each individual object and switching to edit mode to see the data.

Also, at some point you may want to have a checklist of all objects to verify if you unwrapped them, retopologized them or whatever, before you start moving them to an external paint program for example, so a list of objects can be convenient to keep things organized.

object_list.py


I am a coder with a bit of a list obsession, so I created a small add-on that creates a comma separated list of objects with the most common properties for those objects. The list is created as a text block in the built-in editor. Such a list is then easily copied to a spreadsheet if you like, an example is shown below:



Note that the numbers listed are for the render time object with all modifiers applied (which is especially important for subdivision modifiers). Those numbers are only calculated for meshes: I would love to do this for beveled curves as well but I didn't figure out how to do that yet.

Code


The add-on is available from my GitHub repository. Simply use this direct link to download the file object_list.py and then install it with Edit > Preferences > Addons ... in the usual manner.

Once enabled it will be available in the Object menu of the 3D viewport. For large scenese with large meshes it can potentially take quite some time depending on the power of your computer.

The list of info is stored in a Text block called Object list.csv.



Installing Python packages with pip in your Blender environment

Say you are developing Blender add-ons and you need some additional packages, perhaps to to some line profiling use the line_profiler package.

You could keep a separate Python installation outside your Blender environment (like I mentioned in this old article ) but then versions needed to be the same and you needed to tweak the import path to be able use those packages.

An easier approach is to install the necessary packages in your actual Blender Python installation, but if you use pip from the command line it will install inside you regular python environment because pip is a python script that installs in whatever Python installation it is part of.

Fortunately it is rather straight forward to bootstrap your Blender Python environment to get its own pip and then use it to install whatever you want.

Examples

In the examples below, /blender refers to your Blender installation directory. This could be something like /home/michel/Downloads/blender-2.93.0-stable+blender-v293-release.84da05a8b806-linux.x86_64-release

First we install the pip module

/blender/2.93/python/bin/python3.9 -m ensurepip
After installing it, we verify whether it works
/blender/2.93/python/bin/python3.9 -m pip --version

pip 20.2.3 from /blender/2.93/python/lib/python3.9/site-packages/pip (python 3.9)
Now we can immediately use it to install packages inside the Blender Python installation, for example the line_profiler package
/blender/2.93/python/bin/python3.9 -m pip install line_profiler

Collecting line_profiler

... [a boatload of dependencies gets downloaded as well] ...
You will most likely get a warning
WARNING: You are using pip version 20.2.3; however, version 21.1.2 is available.
which is ok. You can upgrade the pip module if you like (with /blender/2.93/python/bin/python3.9 -m pip install --upgrade pip) but the version you got with ensurepip works fine, as demonstrated, so there is no immediate need. You can verify that the package is installed
ls /blender/2.93/python/lib/python3.9/site-packages/line_profiler
Now you can use this package in your add-ons without the need to change sys.path
from line_profiler import LineProfiler  
profile = LineProfiler()
 
@profile
def fie():
    ... expensive code happening here ...

And then somewhere else in your code
profile.dump_stats("/tmp/test.prof")
Inspecting the profile can then be done with
/blender/2.93/python/bin/python3.9 -m line_profiler /tmp/test.prof

Notes

You cannot profile Python code that you code inside text blocks in Blender. Or rather you can profile them alright but you can not inspect the stats in any meaningful way because the file name of the code that is logged makes no sense (if your .blend is called MyBlend and you have a text block test.py, the file will be called .../MyBlend.blend/test.py which is a file that does not exist and even if you save your text block this is an issue because MyBlend.blend is not a directory you can save to. I do not have a workaround for this (except for hacking the .prof file) but in practice this is probably not much of an issue.

Do not forget to remove the profiling changes (and the import) if you want to distribute your login, because your customer will probably not have the line_profiler package installed.

(refer to the original article for a bit more detail. Note that there is no longer the need to use my tweaked version, if you use pip as described earlier you have all you need)

IDMapper just got support for face-maps

IDMapper just got support for face-maps

It is now possible to assign vertex colors based on the face-map membership of faces.


If your workflow involves face-maps already, it is now super easy to create a vertex color layer (ID-map) based on those face-maps.

IDMapper simplifies creation and editing of vertex color layers that can be used as ID-maps in texturing software like Substance Painter or Quixel. It aims to reduce the time it takes to create an ID-map significantly, especially for complex hard surface models. It uses powerful heuristics to create an ID-map from scratch and lets you interactively adjust the results. It offers options to use existing information, like uv-seams, but can also intelligently assign the same color to similar mesh parts. 

The new version is available on BlenderMarket.

Updating old Blender add-ons

The last couple of weeks I have been porting almost a hundred add-ons, scripts and snippets to Blender 2.9x and you would think that to be a lot of work. This proved to be not the case though, because the changes between 2.8x and 2.9x are actually fairly minimal and even scripts dating back all the way to 2.78 were fairly straightfoward to port.

This doesn't mean there were no changes but the fundamental concepts in the Blender python API, like data access and the way operators and panels work stayed the same. What changed were mainly additions (new operators, other new classes like all kinds of nodes), minor changes (optional arguments must now be passed using the keyword) and renaming (groups became collections, lamps became lights).

There were some exceptions though that had more impact, for example, the decision to require annotations for properties and force a naming convention onto classes that need to be registered (like Panel and Menu derived classes) force you to check every add-on because although not mandatory yet, it will be in the future.

Another change that required a bit of work was that the helper function to register all classes in a module has been removed. As we will see in the examples, an alternative is provided.

The biggest change has been in the OpenGL bindings, but although this has a lot of impact, only a minority of add-ons deal with OpenGL drawing.

Anyway, in the list of examples below, I have highlighted the changes, sorted roughly based on how many scripts it has affected (I think).

Required version in bl_info

bl_info["blender"] is no longer just a minimum but must list a version that is at least 2, 80, 0. Otherwise your add-on simply won't run, even if it were compatible.

Old:
bl_info = {
    "name": "Some Operator",
    "author": "Me, myself and I",
    "version": (1, 2, 3),
    "blender": (2, 78, 0),
    "location": "View3D > Object > Some Op",
    "description": "An operator doing someting",
    "category": "Special stuff"}
new:
bl_info = {
    "name": "Some Operator",
    "author": "Me, myself and I",
    "version": (1, 2, 4),
    "blender": (2, 92, 0),
    "location": "View3D > Object > Some Op",
    "description": "An operator doing someting",
    "category": "Special stuff"}

register_module() no longer exists

The way classes that need to be registered are dealt with has changed. Instead of registering everything in a module we need to register individual classes.

from bpy.utils import register_module, unregister_module

def register()
    register_module(__name__)

def unregister()
    unregister_module(__name__)

There is now a factory fumction for this(docs)

from bpy.utils import register_classes_factory  

classes = [Myoperator, VIEW3D_PT_mypanel]
register_classes, unregister_classes = register_classes_factory(classes)

def register():
    register_classes()

def unregister():
    unregister_classes()

properties are no longer assigned values but annotated

I never understood the benefits of this change but it affects almost every add–on

Old:

class Myoperator(bpy.types.Operator):
    bl_idname = 'mesh.myoperator'
    bl_label = 'Do something'
    bl_options = {'REGISTER', 'UNDO'}

    someprop = IntProperty(name="Some prop")

New: Note that the only visible difference is that we now use a colon (:)

class Myoperator(bpy.types.Operator):
    bl_idname = 'mesh.myoperator'
    bl_label = 'Do something'
    bl_options = {'REGISTER', 'UNDO'}

    someprop : IntProperty(name="Some prop")

add-on preferences class needs to be registered

This wasn't the case earlier, but it is as simple as adding it to your list of classes

class MyAddonPrefs(bpy.types.AddonPreferences):
    bl_idname = __name__  # give settings the name of the python module

    somepref : IntProperty(name="Some pref")

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "somepref")
        
classes = [Myoperator, MyAddonPrefs]
register_classes, unregister_classes = register_classes_factory(classes)

Python 3.7

The visible bit of moving to a newer python version is mainly the use of the @ operator instead of *

    mat = ob.matrix_world.inverted()
    for vert in ob_verts:
        world_coordinates = mat * vert.co
    mat = ob.matrix_world.inverted()
    for vert in ob_verts:
        world_coordinates = mat @ vert.co

This is also applies to multiplying two vectors: * is now element-wise multiplication, @ gives the dot product (this is in line with Numpy)

Of course you get all the added benefits from Python 3.7 as well although most are not relevant for add-on development per se.

renamed built-in icons

Not only have the icons been redesigned, many new ones are available and quite a few are removed. The list of change sis to big to list here but if you want an overview of the changes since 2.78 I have made a page

Name change for some types of objects

Lamp has become Light and Group has become Collection. Collections offer a lot more functionality too of course but at its simplest level a collection is just a group.

mandatory keyword parameters

Functions that have optional parameters not must use a keyword. Some notable examples

setting text in an area header,
for example when displaying the status of a modal operator (docs)

context.area.header_text_set(text="something")
context.area.header_text_set(text=None)

And the label() function in a layout (docs), which is used often in panels and operator draw functions.

layout.label(text="something")

layout.split()

Talking about layout, a minor change is that the percentage argument renamed to factor (docs)

layout,split(factor=0.40)

A scene has now a separate cursor attribute

See here

x,y,z = context.scene.cursor.location

Vertex colors are now RGBA

But the Color() constructor always expects 3 values (docs)

Which means you cannot assign a Color object to a .color attribute in a vertex color layer.

some methods expect a dependency graph

The ray_cast() function for example

so instead of

scene.ray_cast(origin, direction )
scene.ray_cast(context.window.view_layer.depsgraph, origin, direction )

The mathutils.bvhtree.BVHTree.FromObject function also needs it.

user preferences are now called preferences

Still accessible through the context but now as ,code>context.preferences instead of context.user_preferences

Note that this breaks stored preferences as well since they might contain references to this attribute in the set itself (preferences are stored as executable python)

The active object is accessed differently

scene.objects.active is no longer available, use context.active_object (docs)

To set an object as the active object you need to do something different as well

context.view_layer.objects.active = myobj

Selecting objects using setters/getters

instead of

ob.select = True
ob.select_set(True)

More here

There is a select_get() as well. This is consistent with selection functions in BMeshes for verts, edges, etc. (docs)

Menus have been shuffled

For example the Object and Mesh menus were part of the Infobar but are now part of the 3d view area.

So, for example INFO_MT_mesh_add menu is now called VIEW3D_MT_mesh_add

The info bar is no longer present by default, the part at the very top (with File, Edit, Render, ...) is now called the TOPBAR

attributes have changed on the Subsurface modifier

Notably the way to perform uv subdivision as well see more here

mod.use_subsurf_uv = True

is no longer needed.

OpenGL bindings are completely changed

bgl does no longer support direct mode at all (not even as an option). Which means everything has to be done replaced by shaders. more here

Blender Add-on Cookbook 2.93 Edition

I am pleased to announce that I just released an updated version of my Blender Add-on Cookbook


This new edition has been updated and tested on Blender 2.92 & 2.93 (beta at the time of writing).

It does not contain any drastically new things, but it has been revised to take into account all the small things that have changed in the Blender Python API since version 2.78, most screenshots have been updated to reflect the 2.9x interface, all links have been verified/updated, and the updated code has been placed in its own Github repository.


The Blender Add-on Cookbook - 2.93 Edition is available on Blender Market.

Progress indicator updated

A long time ago I created a progress indicator that blended nicely with the info header. However, as someone pointed out, we nowadays have the view3d header at the top of the screen and the python API has also changed a bit. So, time for an update :-)


Code

In the end, I didn't have to change all that much. The main difference is that we now replace the draw() method of the VIEW3D_HT_tool_header instead of the INFO_HT_header and in the update() function we now make sure we tag all VIEW3D areas for redraw instead of the INFO areas.

Some minor changes were needed too, but this affected mainly the test operators: wm.event_timer_add() now has mandatory keyword for optional arguments and of course the way we register operators has changed.

The updated code is available from GitHub.


Creating add-ons for Blender 2.93 edition

I am pleased to announce that I just released an updated version of Creating add-ons for Blender.


This new edition has been updated and tested on Blender 2.93 (beta at the time of writing) and will work on 2.92 too.

I does not contain any drastically new things compared to the previous edition, but it has been revised to take into account all the small things that have changed in the Blender Python API that would block a beginner.

Creating add-ons for Blender is available on Blendermarket.

The book is provided in .epub, .mobi and .pdf versions.  The .epub will be readable on most devices, and the .mobi on newer Kindles but the .pdf is probably looks best.

Set up Rigify custom bone colors correctly

The rigify add-on that is bundled with Blender is a great way the generate easy-to-use armatures for any kind of mesh, but it took me a while to figure out how to assign custom colors that always turned out correctly.
The important bit is understanding which bone layers in your metarig (that is used to generate the Rigify rig later) correspond to which layers in the generated rig and how you can control that.

Preparing the metarig

Start with a new armature and switch to edit mode. Create your metarig as you would normally do. This is not a complete Rigify tutorial ( check this playlist by CGDive for a great start) but the things you have to think of while building your metarig if you want to use custom bone colors later are highlighted in the steps below.

add bone chains from samples

We use a simple metarig with a head, a spine, and two arms in this example. It is not necessarily a stellar example of a rig, but it serves to illustrate the relevant steps. The result looks like this


move start-of-chain bones to a bone layer

After your metarig is complete and aligned with your mesh, you should make sure that the start bones of the rigify samples are in the correct bone layer. The is no need to move any of the other bones in a chain. Do make sure that each limb, spine, or head chain starts on a layer that is at least 3 layers removed from any other layer. So if say the start of the right arm is in layer 4, the start of the left arm should go to layer 7.
The reason for this is that Rigify will put the IK controller bones in the final Rigify rig in the same layer and will put corresponding tweak and FK bones in the next two layers. If you don allow room for this, different kinds of bones will end up in the same layers, which is not an issue in itself (the armature will function ok), but will mess up all the nice customization!
Don't forget to make the layers visible in the armature as well (in the Skeleton panel of the Armature properties) otherwise your bones might become invisible when you move them to another layer).

set tweak and fk layers for start bones

Preparing the custom bone groups

This is done with the metarig in Pose mode.

Create a color palette


If you don like the bright colors of the standard, you can add individual colors from theme packs. Here we have clicked Add Standard.

Configure bone layer customization

This bit is the essence of creating the correct colorful bones. We have to assign names, button positions, and colors to the bone layers in the metarig. Once we generate the Rigify rig, these will result in colors and a tool panel with meaningful names.
The rows that are mentioned in the Rigify Layer Names panel correspond to the bone layers in our metarig, but they are off by one! So row 1. refers to bone layer zero.
In our example we have the start of the Head chain in bone layer zero, so we label row 1 as 'Head'
We also configured that any tweak bones for the head will be generated in the next layer, so we can put the label 'Head (tweak)' in row one. There will be no FK bones generate for the head so we ignore row 3.
The start bone of the spine chain is in bone layer 3 in our example, and tweak and FK bones will be generated in the next two layers, so we label rows 4,5 and 6 with 'Spine', 'Spine (tweak)' and 'Spine (FK)' respectively.
We also set the Bone Groups for each set of bones: The chain head to Bone Group 2, because those are the IK  bones (and will be shown in red in the final rig if you use the standard colors), and any FK bones to Bone group 5 so they'll turn up in green in the final rig.
Tweak bone layers are mostly assigned to Bone Group 4 except. The Spine is the exception here: we assigned it to Bone Group 4 so it will show up as yellow. This all follows the coloring of the Human Basic rig.
The UI row of a Rigify Layer determines where the hide/unhide buttons go that will be generated for those layers. Those buttons will show up in the Rig Layers panel in the toolbar once you have generated the Rigify rig.

Generate the Rigify rig

It makes sense to hide the original metarig and move the Rigify rig to the same collection as the mesh


Parent the mesh to the rigify rig

You are done now, but just to make that there are no loose ends, we parent the mesh to the Rigify rig (not the metarig!).
In object mode, select the mesh and then the Rigify rig and then parent the mesh to the rig.
Note that we parent with empty groups here, because that often makes more sense when rigging hard surface models than parenting with automated weights.
We get a list of empty vertex groups, one for each deform bone in the Rigify rig.

For this simple puppet mesh I assigned all head vertices to the DEF-head vertex group (i.e. I did not assign any vertices to the neck groups); all body vertices to the DEF-spine group and the arms and hands to the DEF-upper_arm and DEF-hand groups respectively. But all this is up to you.

Bye

PS> And what about custom bone shapes?

All the custom shapes that the Rigify rig uses are part of a generated collection WGTS_rig (where the last part corresponds to the name of your rigify rig).
These are hidden, but you can edit them in place, or, if you have a ready-made collection of widgets you want to use, you can change the shape of each bone in the Viewport Display panel of the Bone properties (in Pose mode!) by assigning it there.
(you may want to move that whole collection as a sub-collection to your mesh collection as well)

WeightLifter updated for Blender 2.93

Old but still going strong!

WeightLifter has been been updated for 2.9x compatibility. It has been tested on Blender 2.92 and 2.93 beta and only minor modifications were needed to make it compatible. If you still encounter a bug , please let know so that I can have a look at it.

WeightLifter is available on BlenderMarket. This update is free for customers who bought previous versions of WeightLifter.

WeightLifter is an add-on that can calculate all sorts of information and store this into vertex groups or vertex color layers. It can for example determine the visibility of vertices for a certain camera or the distance to some light source and much more, information that can for example be used as a density map in particle systems. 

The future

The code in the add-on is very old (7 years, which in Internet terms is ancient) and it does show its age in the way it is structured and also, as I experience myself, it is not very fast, especially on large meshes.

I am tempted to modernize it and improve its speed if possible, but this might also be an opportunity to add features. So if you have an idea or suggestion, please drop me a note in the contact box at the top right of the page and I'll be happy to consider it.