Subdividir caras de objetos por área en el script de Python
Tengo la siguiente escena con varios objetos:
Es principalmente un modelo CAD, por lo que puede ver que algunas de las caras en algunos de los objetos son diferentes más grandes en comparación con otros. Lo que me gustaría hacer es subdividir al menos los grandes, si no todos (teniendo en cuenta que algunos pequeños que cumplirían con el umbral del área son largos, también podrían necesitar ser reestructurados) para que cumplan con una restricción de área. También me gustaría que los nuevos objetos subdivididos no perdieran sus propiedades de material / tamaño / nombre.
Además, considerando que necesitaría hacer eso para un montón de escenas (hasta un par de cientos), no sé si sería posible abordar la tarea en un modo GPU o algo así (para ser honesto, ya que no lo he probado en la práctica, la CPU podría ser lo suficientemente rápida).
Encontré un hilo similar aquí subdividir todas las caras cuyo área es más grande que 1 mm hasta que cada área de la cara sea más pequeña que 1 mm, pero la solución se describe en la interfaz de usuario en lugar de un script de Python que preferiría.
Actualizar:
Probar la solución a continuación de @batFINGER con el operador remesh no dio el resultado deseado (lo probé con la configuración 'SHARP' y octree_depth=7-10, que parecían ser las más relevantes para mantener intacta la geometría inicial). Además, parece destruir el mapeo de textura UV que no quiero si es posible. Probé dyntopo desde la interfaz de usuario, pero también parece crear problemas con el mapeo de texturas y mantener intacta la geometría inicial.
Por ejemplo, operador Remesh con 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()
Por lo que entendí, si quiero mantener la geometría intacta, realmente necesito crear muchas caras pequeñas que realmente no quiero y aún así esto no está garantizado. Para mi caso el tamaño de las caras que tienen los muebles debería ser suficiente también para mis grandes objetos planos, es decir, techo, suelo, paredes, etc.
Por tanto, he intentado seguir algunos de los ejemplos que se dan en los siguientes enlaces:
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
y he escrito el siguiente código donde intento subdividir mis objetos según la longitud de los bordes:
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)
Pero realmente no parece funcionar. Si, por otro lado, subdivido todos los bordes con un corte fijo, entonces parece funcionar, pero nuevamente todos los bordes se cortan de manera similar, que no es lo que quiero.
Por ejemplo, esta es la salida con un corte fijo de 5 en las paredes y el techo:
La salida parece estar bien, excepto las caras en los límites con los bordes largos que se están volviendo desordenados (esto también es algo que necesito encontrar una manera de abordarlo si es posible).
Cualquier idea de por qué mi forbucle para subdividir los bordes por corte no funciona. Cualquier otra sugerencia para abordar el problema también es bienvenida.
Respuestas
Modificador de remallado.
Una de las respuestas vinculadas a recomienda usar el modificador remesh, sin mirar cuál sería la configuración correcta, es decir , este es el código para "hacerlo" pero no he investigado la configuración y qué valores establecer.
Haga un bucle sobre los objetos, agregue un modificador, obtenga la malla deformada, elimine los modificadores actualice la malla.
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()