1
$\begingroup$

I want to create a script that opens a blender file and splits the edges of all objects in it, perhaps ignoring those that are specified as smooth.

This is for a custom renderer that takes in blend files but interpolates ALL mesh normals from its vertices, which makes it difficult to have walls when they have a volume (i.e. their vertex normals don't match any of their faces).

It should be pretty straightforward, but blender's API comes off as strange to me, and I don't quite get the best way of doing things.

This is what I've got

import bpy
import sys

scene_file = sys.argv[sys.argv.index('--')+1]

bpy.ops.wm.open_mainfile(filepath=scene_file)
bpy.ops.object.select_all(action='DESELECT')

for i in bpy.data.objects:
    i.select = True
    bpy.ops.object.editmode_toggle()
    bpy.ops.mesh.edge_split()
    bpy.ops.object.editmode_toggle()

bpy.ops.wm.save_mainfile(filepath=scene_file[:-5]+'modded.blend')

I'm kind of trying to emulate the steps I'd take on the GUI, go through every object, get into edit mode, select all vertices/faces/edges, split edges, exit edit mode and continue. But it doesn't seem to do what I want (or anything in fact).

I've also seen the bmesh.ops.split_edges(bm, edges, verts, use_verts) operator, but it requires mesh, edges and vertices, which I don't know how to retrieve from the object.

Also, I may want to make all normals consistent before splitting them just in case.

$\endgroup$
1
$\begingroup$

Using bmesh.ops.split_edges(..)

Test script, object mode, for all unique meshes of all mesh objects in the scene, load a bmesh, split all edges, rewrite to mesh. Need only pass edges of the bmesh to the operator.

import bpy
import bmesh
context = bpy.context
scene = context.scene

bm = bmesh.new()
meshes = set(o.data for o in scene.objects if o.type == 'MESH')
for me in meshes:
    bm.from_mesh(me)
    bmesh.ops.split_edges(bm, edges=bm.edges)
    bm.to_mesh(me)
    bm.clear() 

bm.free()    

On smooth shading AFAIK it is set on a per face basis. Using the methods outlined here Using python to set object shading to 'smooth' could make a flag to check if any, or in this case all the faces have smooth shading.

# check if all faces in bmesh are smooth
is_smooth = all(f.smooth for f in bm.faces)

Some notes on question code:

when using operators its far better to iterate over context.scene.objects or if pre selected context.selected_objects. The collection bpy.data.objects is all objects in the blend whether linked to context scene any scene or no scene. As well as being selected you also want to set the object to active, and select all geom before calling operator

mesh_objects = [o for o in context.scene.objects if o.type == 'MESH']
meshes = set(o.data for o in mesh_objects)
for ob in mesh_objects:
    if not meshes:
        break
    elif ob.data not in meshes:  
        continue
    context.scene.objects.active = ob
    ob.select = True
    bpy.ops.object.editmode_toggle()
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.mesh.edge_split()
    bpy.ops.object.editmode_toggle()
    meshes.remove(ob.data)

Note that 100 objects in a scene could share one mesh, hence the logic above to avoid calling the operator on the same mesh 100 times.

No biggy, but as a generality, i is used for integer iterators.

$\endgroup$
  • $\begingroup$ The first script doesn't work. I'm running this from the terminal (blender -b -P thescript.py -- thefile.blend) and saving it to another blender file and it just doesn't modify anything. The second one is also wrong (me is not defined). $\endgroup$ – mjgalindo Oct 19 '18 at 8:27
  • $\begingroup$ Yep me should be ob.data no biggy (edited). IMO FIrst script is the way to go. If running in batch mode need to save the file after running. First and last question I answer for you. $\endgroup$ – batFINGER Oct 19 '18 at 12:20
  • $\begingroup$ Ok, yeah, I didn't run it correctly the first time... Still, I didn't mean the comment in a bad way, no need to get jumpy $\endgroup$ – mjgalindo Oct 19 '18 at 12:31
  • $\begingroup$ Thankyou. Apologies for being the Soup Nazi from Sienfeld Bmesh is def the way to go, when possible, for multiple mesh operations. No operators, no edit mode toggle. One way to select all edges that are not part of a smooth face edges = set(e for f for f in bm.faces for e in f.edges) - set(e for f in bm.faces if f.smooth for e in f.edges) $\endgroup$ – batFINGER Oct 19 '18 at 13:13

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.