1
$\begingroup$

I'm writing a procedural tree generator that creates a mesh consisting of a network of nodes joined by edges, then adds a Skin Modifier. Here is a simple example with just one trunk and one branch:

enter image description here

The Skin Modifier does this:

enter image description here

I get the desired effect if afterwards, via the UI, I select all nodes that represent "joints" between branches and click "Mark Root" on the Skin Modifier.

enter image description here

It also works if I just select the whole mesh and hit "Mark Root", but obviously this is not really correct. But at this point I'll take that.

The Info panel suggests bpy.ops.object.skin_root_mark() and trial and error suggests this works on the selected vertices in Edit Mode. My effort at scripting this produces this error:

    ret = op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.object.skin_root_mark.poll() failed, context is incorrect
Error: Python script failed, check the message in the system console

This error is raised by the attempt to switch to Edit mode or, if that is commented out, by the call to skin_root_mark (the latter is the version pasted above). I can't tell from this error message what the underlying issue is, especially as the same error occurs on either of the lines, which seem

The following is a MWE that exemplifies the crash without all the tree-building logic:

import bpy

# Clean up
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete()

verts = [(0, 0, 0), (1, 0, 0)]
edges = [(0, 1)]

# Create the mesh
mesh = bpy.data.meshes.new("TreeMesh") 
mesh.from_pydata(verts, edges, []) 
mesh.update()
mesh.validate()

# Create Object to hold the mesh and add it to the scene
obj = bpy.data.objects.new('Tree', mesh)
scene = bpy.context.scene
scene.collection.objects.link(obj)

# Add the skin modifier
obj.modifiers.new(name="SK", type="SKIN")

# Make sure to deselect everything in the mesh
# (I guess this is overkill but just in case it makes a difference...)
for g in mesh.vertices[:] + mesh.edges[:] + mesh.polygons[:]:
    g.select = False

# Select all vertices
for v in mesh.vertices:
    v.select=True

# Either of these lines raises the "context is incorrect" exception:
bpy.ops.object.mode_set(mode="EDIT", toggle=False)
bpy.ops.object.skin_root_mark()

As an aside, it's not at all clear to me how skin_root_mark works. What's the intended behaviour if you have nodes selected that belong to multiple objects and / or are affected by multiple Skin Modifiers? It seems like an odd choice to make it a global function rather than, say, a list of vertices attached to each modifier. If anyone can point me to info on the rationale behind this, that would also be very helpful.

$\endgroup$
  • $\begingroup$ The object is not active so the context (for the last two lines) cannot find on what to operate. Add bpy.context.view_layer.objects.active = obj $\endgroup$ – lemon Mar 14 at 13:33
  • $\begingroup$ and by the way: only one root which corresponds the red circle in the UI. $\endgroup$ – lemon Mar 14 at 13:33
  • $\begingroup$ @lemon That fixed it, thank you! If you make your comment an answer I'll accept. Looks like until now I'd failed to appreciate the distinction between "selected" and "active". $\endgroup$ – helveticat Mar 14 at 16:00
2
$\begingroup$

As said in comment, the object is not active and that's why the ops failed.

You may use:

bpy.context.view_layer.objects.active = obj 

before the ops.

Though you can also without ops:

# Add the skin modifier
obj.modifiers.new(name="SK", type="SKIN")

#Get the skin vertices layers
skin_vertices = obj.data.skin_vertices
#Get the layer
skin_layer = skin_vertices[0]

#Assigns radius to some vertices
skin_layer.data[0].radius = (0.20,0.20) #Indices 0 and 1 are the vertex indices
skin_layer.data[1].radius = (0.10,0.10)

#Assigns root
skin_layer.data[1].use_root = True
#Assigns loose
skin_layer.data[1].use_loose = True

Also note that, this way you can assign multiple roots which is not allowed from the UI. I don't know if this can lead to issues or not.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thank you, with the code example you also solved my next problem (how to set vertex radii individually). Much appreciated! $\endgroup$ – helveticat Mar 14 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.