Subdivida faces de objetos por área no script python

Sep 22 2020

Tenho a seguinte cena com vários objetos:

É principalmente um modelo cad, então você pode ver que alguns dos rostos em alguns dos objetos são maiores em comparação com alguns outros. O que eu gostaria de fazer é subdividir pelo menos os grandes senão todos eles (considerando que alguns pequenos que atenderiam ao limite da área são longos, eles também podem precisar ser reestruturados) para que cumpram uma restrição de área. Também gostaria que os novos objetos subdivididos não percam suas propriedades de material / tamanho / nome.

Além disso, considerando que eu precisaria fazer isso por um monte de cenas (até algumas centenas), não sei se seria possível resolver a tarefa em um modo de GPU ou algo assim (para ser honesto, já que eu não tentei na prática, a CPU pode ser rápida o suficiente).

Eu encontrei um segmento semelhante aqui subdividindo todas as faces cuja área é maior do que 1 mm até que cada área da face seja menor do que 1 mm, mas a solução é descrita na IU em vez de um script python que eu preferiria.


Atualizar:

Testar a solução abaixo de @batFINGER com o operador remesh não deu o resultado desejado (eu testei com as configurações 'SHARP' e octree_depth=7-10, que pareciam ser as mais relevantes para manter a geometria inicial intacta). Além disso, parece destruir o mapeamento de textura UV que eu não quero, se possível. Eu tentei o dyntopo da IU, mas também parece criar problemas com o mapeamento de textura e manter a geometria inicial intacta.

Por exemplo, operador Remesh com octree_depth=7:

bm = bmesh.new()
dg = context.evaluated_depsgraph_get()        
for o in obs:
    rm = o.modifiers.new("Remesh", type='REMESH')
    rm.mode = 'SHARP'
#    rm.sharpness = 100
    rm.octree_depth = 7
    #rm.scale = 0.2
    #rm.threshold = 0.5
    dg.update()
    bm.from_object(o,dg)
    o.modifiers.clear()
    bm.to_mesh(o.data)
    bm.clear()
bm.free()

Pelo que entendi, se eu quiser manter a geometria intacta, eu realmente preciso criar um monte de rostos minúsculos que eu realmente não quero e ainda assim não é garantido. Para o meu caso, o tamanho das faces dos móveis deve ser suficiente também para os meus grandes objetos planos, ou seja, teto, piso, paredes, etc.

Portanto, tentei seguir alguns dos exemplos fornecidos nos seguintes links:

https://blender.stackexchange.com/a/93597/106722

https://blender.stackexchange.com/a/168193/106722

https://blender.stackexchange.com/a/120244/106722

https://blender.stackexchange.com/a/163936/106722

e escrevi o código a seguir, no qual estou tentando subdividir meus objetos com base no comprimento das bordas:

import bpy
from mathutils import Vector
from bpy import context
import numpy as np
import bmesh

def subdivide_edge_cuts(obj, cuts=1):
    
    me = obj.data
    # New bmesh
    bm = bmesh.new()
    # load the mesh
    bm.from_mesh(me)
    
    ### collapse short edges
    edges_len_average = 0
    edges_count = 0
    shortest_edge = 10000
    for edge in bm.edges:
        if True:#edge.is_boundary:
            edges_count += 1
            length = edge.calc_length()
            edges_len_average += length
            if length < shortest_edge:
                shortest_edge = length
                
    edges_len_average = edges_len_average/edges_count
    
    print("Total edges: {}".format(len(bm.edges)))
    print("Shortest edge: {}".format(shortest_edge))
    print("Average edge: {}".format(edges_len_average))
 
    subdivide_edges = []
    for edge in bm.edges:
        cut_count = int(edge.calc_length()/shortest_edge)*cuts
        if cut_count <= 0:
            cut_count = 0

        subdivide_edges.append([edge,cut_count])

    print("Edges to subdivide: {}\n".format(len(subdivide_edges)))  

    # group edges by found cut
    grouped_edges = {}            
    for edge, cut in subdivide_edges:
        grouped_edges.setdefault(cut, []).append(edge)
        
    # This works
#    bmesh.ops.subdivide_edges(bm, edges=list(np.array(subdivide_edges)[:,0]), cuts=5, use_grid_fill=True)

    # This doesn't work
    for cut in grouped_edges:
        bmesh.ops.subdivide_edges(bm, edges=grouped_edges[cut], cuts=cut, use_grid_fill=True)

    # Write back to the mesh
    bm.to_mesh(me)
    me.update()

bpy.ops.object.select_all(action='SELECT')
obs = [o for o in context.selected_objects
        if o.type == 'MESH']

for ob in obs:
    subdivide_edge_cuts(ob)

Mas realmente não parece funcionar. Se, por outro lado, subdividir todas as bordas com um corte fixo, então parece funcionar, mas novamente todas as bordas são cortadas de maneira semelhante, o que não é o que eu quero.

Por exemplo, esta é a saída com um corte fixo de 5 nas paredes e no teto:

A saída parece boa, exceto os rostos nos limites com as bordas longas que estão ficando confusos (isso também é algo que preciso encontrar uma maneira de resolver, se possível).

Alguma ideia de por que meu forloop para subdividir as arestas por corte não funciona. Qualquer outra sugestão para resolver o problema também é bem-vinda.

Respostas

2 batFINGER Sep 22 2020 at 16:09

Modificador de remesh.

Uma das respostas vinculadas a recomenda usar o modificador remesh, sem verificar quais seriam as configurações corretas, ou seja , este é o código para "fazer", mas não investigou as configurações e quais valores definir.

Faça um loop sobre os objetos, adicione um modificador, obtenha a malha deformada, remova os modificadores e atualize a malha.

import bpy
import bmesh
from bpy import context

obs = [o for o in context.selected_objects
        if o.type == 'MESH']
bm = bmesh.new()
dg = context.evaluated_depsgraph_get()        
for o in obs:
    rm = o.modifiers.new("Remesh", type='REMESH')
    rm.mode = 'BLOCKS'
    #rm.scale = 0.2
    #rm.threshold = 0.5
    dg.update()
    bm.from_object(
            o,
            dg)
    o.modifiers.clear()
    bm.to_mesh(o.data)
    bm.clear()
bm.free()