Разделить грани объекта по площади в скрипте Python

Sep 22 2020

У меня есть следующая сцена с несколькими объектами:

Это в основном CAD-модель, так как вы можете видеть, что некоторые лица на некоторых объектах отличаются больше по сравнению с некоторыми другими. Что я хотел бы сделать, так это разделить, по крайней мере, большие, если не все из них (учитывая, что некоторые маленькие, которые будут соответствовать пороговому значению площади, длинные, их также может потребоваться реструктуризация), чтобы они выполняли ограничение площади. Также я хотел бы, чтобы новые разделенные объекты не теряли своих свойств материала / размера / имени.

Более того, учитывая, что мне нужно было бы сделать это для нескольких сцен (до пары сотен), я не знаю, можно ли будет решить задачу в режиме графического процессора или что-то в этом роде (если честно, поскольку я не пробовал на практике, процессор может быть достаточно быстрым).

Я нашел здесь похожую нить, разделяющую все грани, площадь которых больше 1 мм, пока каждая площадь лица не станет меньше 1 мм, но решение описывается в пользовательском интерфейсе, а не в скрипте python, который я бы предпочел.


Обновить:

Тестирование приведенного ниже решения от @batFINGER с оператором remesh не дало желаемого результата (я тестировал его с настройками «SHARP» и octree_depth=7-10, которые кажутся наиболее подходящими для сохранения исходной геометрии без изменений). Более того, похоже, что это разрушает отображение текстуры UV, чего я не хочу, если это возможно. Я пробовал dyntopo из пользовательского интерфейса, но он также, похоже, создает проблемы с наложением текстуры и сохранением исходной геометрии нетронутой.

Например, оператор Ремеша с 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()

Из того, что я понял, если я хочу сохранить геометрию нетронутой, мне действительно нужно создать множество крошечных граней, которые мне на самом деле не нужны, и все же это не гарантируется. В моем случае размер граней мебели должен быть достаточным и для моих больших плоских объектов, например потолка, пола, стен и т. Д.

Поэтому я попытался следовать некоторым из примеров, приведенных по следующим ссылкам:

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

и я написал следующий код, в котором я пытаюсь подразделить свои объекты по длине ребер:

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)

Но на самом деле это не работает. Если, с другой стороны, разделить все края с помощью фиксированного разреза, то, похоже, это сработает, но снова все края будут разрезаны аналогичным образом, а я не хочу.

Например, это результат с фиксированным вырезом 5 в стенах и потолке:

Результат кажется прекрасным, за исключением граней в границах с длинными краями, которые становятся беспорядочными (это тоже то, что мне нужно найти способ решить, если возможно).

Любая идея, почему моя forпетля для разделения краев разрезом не работает. Также приветствуются любые другие предложения по решению проблемы.

Ответы

2 batFINGER Sep 22 2020 at 16:09

Модификатор Ремеш.

Один из ответов, на который есть ссылка, рекомендует использовать модификатор remesh, не обращая внимания на то, какие настройки будут правильными, то есть это код, чтобы «сделать это», но не исследовал настройки и какие значения установить.

Переберите объекты, добавьте модификатор, получите деформированную сетку, удалите модификаторы, обновите сетку.

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()