1
$\begingroup$

First: I have a very detailed mesh (16M tris) but there are many regions where lots of faces have the same height and angle.

An optimization approach (real time rendering is the goal) is to select a region's face, go to select linked flat faces, and unsubdivide a bunch.

That's all fine but there are hundreds of regions like this and I'd really love to lose about 5M++ tris WITHOUT doing it by hand (mind you this won't be the only mesh). Pic for context

So... I really not new to programming (but to Python, which shouldn't be a problem.........) and I came up with a VERY simple, brute force algorithm to do it for me, which's pseudo-code looks like this(sorry if I use C-like syntax):

for(int i = 0; i < mesh.faces.count; i++)
{
    List<face> linkedFaces = mesh.faces[i].SelectLinkedFaces(0.02f);
    linkedFaces.UnSubdivide(10);
}

Well... OBVIOUSLY I don't know anything about the blender-python API. I just want to select every single face, execute the "Select linked faces" option with an angle of 0.02 degrees and then un-subdivide all faces selected that way ten times. Can anyone help me out, even if it's just a guide? Thank you!

$\endgroup$
  • $\begingroup$ Maybe try some Merge by Distance first, it may caught thousands vertices already. Then try Decimate modifier, with the Unsubdivide mode, then adjust the iteration. Anyway, I dunno much about Blender API, but why not pick them up from the manual? docs.blender.org/api/current and maybe bpy.ops.mesh is the good start docs.blender.org/api/current/… $\endgroup$ – lintang_biru Mar 30 at 5:37
  • $\begingroup$ I have tried many other options. There is usually loss of detail involved. As you can tell, I need HIGH detail at LOW cost (RAM-wise). Unsubdividing is the same as not subdividing before applying a displace modifier, so I don't get the point. And yes I could've gone ahead and "learned" Python and the entire blender API just to execute 2 lines of code within a loop. I'd rather spend the time with selecting and unsubdividing by hand :D $\endgroup$ – 47Mortuus Mar 30 at 14:09
2
$\begingroup$

Edit Mode bmesh script.

enter image description here Tested on subdivided and poked cube.

  • Tags all edges that have a face angle > EPS
  • Gets all triangular faces made up of only edges not tagged and joins them. (To Quad)
  • Feeds all verts that have no linked edges tagged into bmesh unsubdivide operator.

Can edit properties of both to quads, eg max face angle, and iterations for unsubdivide

Test script, run in edit mode. Needs no selection.

import bmesh
import bpy
from math import radians

EPS = 1e-5 # edge angle
ITER = 10 # unsubdiv iters
AST = radians(40)
AFT = radians(40)        

me = bpy.context.object.data
bm = bmesh.from_edit_mesh(me)

for e in bm.edges:
    if len(e.link_faces) < 2:
        e.tag = True
    else:
        e.tag = e.calc_face_angle() > EPS


ret = bmesh.ops.join_triangles(
        bm,
        faces=[f for f in bm.faces if all(not e.tag for e in f.edges)],
        angle_face_threshold=AFT, 
        angle_shape_threshold=AST,
        )

bmesh.ops.unsubdivide(bm,
        verts=[v for v in bm.verts if not any(e.tag for e in v.link_edges)],
        iterations=ITER,
        )

bmesh.update_edit_mesh(me) 

Alternatively Dissolve edges with 0 face angle.

In edge select mode select an edge with no face angle, then select similar by face angle

enter image description here

Hit X

and dissolve edges.

A script

enter image description here

A quick edit mode bmesh script to do this.

import bmesh
import bpy

EPS = 1e-5


me = bpy.context.object.data
bm = bmesh.from_edit_mesh(me)


bmesh.ops.dissolve_edges(bm, 
        edges = [e for e in bm.edges if e.calc_face_angle() < EPS],
        )

bmesh.update_edit_mesh(me)        

Not sure how it will go on 5M ++ faces. Could change slightly to work on selection.

        edges = [e for e in bm.edges if e.calc_face_angle() < EPS and e.select]

This will ofcourse create a number of ngons, that can be grid filled, and fed into the unsubdivided.

| improve this answer | |
$\endgroup$
  • $\begingroup$ People like you slowly restore my faith in humanity. Thank you for your effort. I copy pasted both sripts and ran them. This error pops up: "BMEdge.calc_face_angle(): edge doesn't use 2 faces" - line 15 in the first function. Maybe you assumed that my model is "closed", whereas it's actually just a plane with a displacement modifier applied to it. $\endgroup$ – 47Mortuus Mar 30 at 14:26
  • $\begingroup$ Fixed for the first. Never got around to finishing second (which was first till I went unsub rather than ngon) as grid fill is tricky in bmesh. $\endgroup$ – batFINGER Mar 30 at 14:48
1
$\begingroup$

Thanks to a hint from @batFINGER, turns out the best solution is simply to select an edge (Edge Select mode) with no face angles, go to Select > Select Similar > Face Angles. Then go to Face Select mode and un-subdivide.

No scripting needed. For simple shapes, Dissolve Edges will work way better. But beware of face-groups that are selected and connected to other face-groups at an angle with more complex models.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Cheers UV'd .. lol, I was all round it. Never used unsub before. Appears bmesh needs quads for it to do anything, and couldn't find it in UI. $\endgroup$ – batFINGER Mar 30 at 15:05

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.