1
$\begingroup$

I have a set of objects which initially come as mix of triangles,quads and ngons as you can also see in the image below:

enter image description here

I would like to transform them all to quads. The proposed solution that I've found is to use the bpy.ops.mesh.tris_convert_to_quads operator. However, this doesn't seem to always work, below the code snippet I used:

import bpy

C = bpy.context
scene = C.scene

for ob in C.selected_objects:
    if ob.type == 'MESH':
        scene.objects.active = ob #set active object
        for vert in ob.data.vertices:
            vert.select = True #ensure all vertices are selected
        bpy.ops.object.mode_set(mode='EDIT') #switch to edit mode
        bpy.ops.mesh.remove_doubles() #remove doubles
        bpy.ops.mesh.tris_convert_to_quads() #tris to quads
        bpy.ops.object.mode_set(mode='OBJECT') #switch to object mode

I've tried to play a bit with the angle thresholds as suggested but it didn't work as well. Any idea how I can transform all triangles to quads?

Here are some of the links with the proposing solution (but all end up with the same problem that it doesn't always work):

'Tris to Quads' on narrow faces does nothing

https://blenderartists.org/t/help-converting-tris-to-quads/518437

https://blenderartists.org/t/remove-doubles-and-tris-to-quads-for-all-imported-meshes-at-once/646911

https://blenderartists.org/t/converting-triangles-to-quads-on-multiple-meshes-objects/548173/7

https://blenderartists.org/t/why-doesnt-convert-triangle-to-quads-work/376707/9

How can I decimate a mesh to remove unnecessary triangles?

And here I attach the .blend file:


Update:

Ok, I've tried the quadremesher addon which seems to work quite nicely. I have one issue though, I have the following the script running:

import bpy
import time

# set quadremesher parameters
bpy.context.scene.qremesher.use_materials = True
bpy.context.scene.qremesher.adaptive_size = 100
bpy.context.scene.qremesher.target_count = 5000
bpy.context.scene.qremesher.adapt_quad_count = True
bpy.context.scene.qremesher.autodetect_hard_edges = True

# create list of objects
obs = [o for o in bpy.data.objects
        if o.type == 'MESH']

# loop over objects and apply remesh
for ob in obs:
    bpy.ops.object.select_all(action='DESELECT') # Deselect all objects    
    #Set active object to variable
    bpy.context.view_layer.objects.active = ob
    if ob: ob.select_set(True)

    print(ob.name)

    # remesh object!!!!!    
    ret = bpy.ops.qremesher.remesh()
    print('Ret: {}'.format(ret))

however the operator returns "{RUNNING MODAL}" this has an affect that only the last object is remeshed because the operator is called multiple times before the first call is finished. Thus, I would like to have the loop going to the next iteration only after the operator is finished a similar issues is described here and here. I am not sure though how to exactly apply this wrapper and callback() calls in my case with the for loop. Thus, any insight will be helpful.


Update 1:

Ok, I've managed to modify my script to work with the wrapper linked above and here is the code snippet:

import bpy

from quad_remesher_1_1 import QREMESHER_OT_remesh as op

def remesh_object(obs):
    if obs:
        ob = obs.pop(0)
        bpy.ops.object.select_all(action='DESELECT') # Deselect all objects    
        #Set active object to variable
        bpy.context.view_layer.objects.active = ob
        if ob: ob.select_set(True)

        print("Object: {}".format(ob.name))

        # remesh object!!!!!    
        bpy.ops.qremesher.remesh()

# some callback function - here we put what shall be run after the modal is finished
def callback(ret):
    print('Callback triggered: {} !!'.format(ret))

    remesh_object(obs)
    
def modal_wrap(modal_func, callback):

    def wrap(self, context, event):
        ret, = retset = modal_func(self, context, event)
        if ret in {'FINISHED'}: # my plugin emits the FINISHED event on finish - yours might do FINISH or CANCELED, you might have to look it up in the source code, __init__.py , there look at the modal() function for things like return {'FINISHED'} or function calls that return things alike.
            print(f"{self.bl_idname} returned {ret}")
            callback(ret)
        return retset
    return wrap

op._modal_org = op.modal
op.modal = modal_wrap(op.modal, callback)


# set quadremesher parameters
bpy.context.scene.qremesher.use_materials = True
bpy.context.scene.qremesher.adaptive_size = 100
bpy.context.scene.qremesher.target_count = 5000
bpy.context.scene.qremesher.adapt_quad_count = True
bpy.context.scene.qremesher.autodetect_hard_edges = True


obs = [o for o in bpy.data.objects
        if o.type == 'MESH']
        
remesh_object(obs)

and the output:

enter image description here

Considering that the plugin is not open source, my provided solution might not be the ideal. Thus, if someone can come up with another solution I would be glad if he can share it.

$\endgroup$

Your Answer

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

Browse other questions tagged or ask your own question.