0
$\begingroup$

I'm attempting to write a background script to process an obj file. It's a single mesh with a hole at both ends; I want to fill in those holes with triangles for rendering purposes.

The shape is along the Y axis, with holes at X=0 and X=[largest X]. I appear to be able to find and select those edges, but bpy.ops.mesh.fill() continues to give a "No edges selected" error. I've tried using bmesh, and also bpy.ops.mesh.loop_multi_select(ring=False) (in every combination!) but nothing seems to make any difference. I'm about to give up and write the fill manually. My code so far is as follows:

import sys
import bpy
import bmesh

argv = sys.argv
argv = argv[argv.index("--") + 1:]  # get all args after "--"
filename = argv[0]
filenameOut = argv[1]

#Delete the cube mesh
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.delete(type='VERT')

#Import object:
status = bpy.ops.import_scene.obj(filepath=filename)

#Deselect everything
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.object.mode_set(mode='OBJECT')

mesh=bpy.data.objects[0].data
EPSILON = 1.0e-5

#Find the highest X coordinate
maxX=0
for vert in mesh.vertices:
    if vert.co[0]>maxX:
        maxX=vert.co[0]

print(maxX)

i=0
for edge in mesh.edges:
    v1=mesh.vertices[edge.vertices[0]]
    v2=mesh.vertices[edge.vertices[1]]
    if -EPSILON <= v1.co[0] <= EPSILON and -EPSILON <= v2.co[0] <= EPSILON:
        i=i+1
        edge.select = True
        v1.select = True
        v2.select = True
print(i)

i=0;
for edge in mesh.edges:
    v1=mesh.vertices[edge.vertices[0]]
    v2=mesh.vertices[edge.vertices[1]]
    if maxX-EPSILON <= v1.co[0] <= maxX+EPSILON and maxX-EPSILON <= v2.co[0] <= maxX+EPSILON:
        i=i+1
        edge.select = True
        v1.select = True
        v2.select = True
print(i)

bpy.ops.object.mode_set(mode='EDIT')

#Just checking...
i=0
for edge in mesh.edges:
    if edge.select:
        i=i+1
print(i)

print("a")
bpy.ops.mesh.fill()
print("b")

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

bpy.ops.export_scene.obj(filepath=filenameOut)

Output is as follows:

[importing file OK]
1.587500810623169
164
164
328
a
Warning: No edges selected
Warning: No edges selected
b
[exporting file]

Uh. What? I could give the bmesh code as well, but it too seems to successfully select edges... only for the fill() to give the same error.

$\endgroup$
  • $\begingroup$ OK, maybe it just ... doesn't work. I have managed to get it done manually, using bm.verts.new and bm.faces.new, though making a number of assumptions about the topology of the input shape. I can give the code if anyone's interested, but I'm now working on code to mark seams and unwrap it. I just hope that the unwrap command works... $\endgroup$ – Tim Owens Dec 14 '17 at 5:06
0
$\begingroup$

A bmesh approach.

Used a purely object mode bmesh approach. Selects all boundary edges, then uses the bmesh "F" tool, the operator result = bmesh.ops.contextual_create(...) to fill the holes, and poked the resultant faces, bmesh.ops.poke(bm, faces=result\["faces"\]) to give a triangular fan from the edges. Tested script below on selected endless cylinder(s).

import bpy
import bmesh
context = bpy.context
scene = context.scene
# completely remove cube if in scene
cube = scene.objects.get("Cube")
if cube:
    bpy.data.objects.remove(cube, do_unlink=True)
bm = bmesh.new()
# import from obj file here, only imported objects will be 
# in context.selected_objects after import.    
for o in context.selected_objects:
    # test here to continue if not mesh, can obj import other???
    # load bmesh
    bm.from_mesh(o.data)
    # find boundaries
    bound_edges = set(e for e in bm.edges if e.is_boundary)
    # used sets, may not be required.
    zero_edges = set(e for e in bound_edges 
        if all(abs(v.co.x) < 0.001 for v in e.verts))
    right_edges = bound_edges - zero_edges
    # use bmesh "F" tool
    result = bmesh.ops.contextual_create(bm, geom=list(bound_edges))
    # poke the resulting faces to make triangular fan.
    bmesh.ops.poke(bm, faces=result["faces"])
    bm.to_mesh(o.data)
    o.data.update() # mainly only need for UI really
    bm.clear()
bm.free()
$\endgroup$
  • $\begingroup$ Thank you for that. I've got it all working now, but I'll bear that in mind if I need to revisit it. And I'll give you a tick for bothering to answer! I initially used a method which basically did the same as a poke, but because the holes were very elongated (OK, these are surfboards!) the UV map ended up quite distorted. I then went for a sawtooth fill instead, which has come out perfectly. I'm guessing that there's no means to specify which part of the object appears first in the UV map. I'm guessing that's also another question! $\endgroup$ – Tim Owens Feb 7 '18 at 21:13
0
$\begingroup$

I believe it's because I hadn't marked the object as active. I added

scn = bpy.context.scene
scn.objects.active = bpy.data.objects[0]

before doing the fill, and that seemed to fix the error. Of course, the fill was completely ... not what I'd expected so I'm persevering with a manual one for now!

$\endgroup$

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.