Subdivida faces de objetos por área no script python
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
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()