3
$\begingroup$

I understand how weirdly specific this is, but is there no way to select faces based on the normals they're facing AND the adjacent faces' normals?

What I want is a little complicated, but if I were to have a subdivided cube with perfectly flat faces, would I be able to select every face on the X positive normal but make an exception for any face that's connected to a different face with a Z positive normal? enter image description here

I've tried putting specific selections into different vertex groups, but I'ven't been getting much luck using that work around. Could I not use python to make a custom search and dot in my own parameters?

[EDIT 1]

Alright, thinking a little better this time, I realised that what I was asking was odd because of the examples I used. So, I'm editing this to better clarify, using a model that correlates much better to the issue I'm having.

I suppose there'd be 2 options to what I'm asking: "select adjacent faces of normal based on the faces' normals" or "limit selection of faces based on the connected faces' normals." Though in all fairness, I'd only profit from the 2nd case. Giving examples now.

In the first case, "select adjacent faces of normal based on faces' normals," I've set up an example. In this case, I asked to select all the Z+ normals AND select adjacent faces that have the Y- normal. As you can see, only the faces that are making contact with the Z+ faces are selected. enter image description here

In the second case, "limit selection of faces based on the connected faces' normals," I've another example. Again, I ask to select all Z+ faces, but this time, avoid any faces that are on the Y- normal. enter image description here

Is there any way to do this? Not a big deal if there isn't, I just spent the better part of my day trying to do something like the earlier 2nd example with only commands and couldn't.

$\endgroup$
6
  • 1
    $\begingroup$ Hi, thanks for the post. This question was in the review queue. I think it's a good question and I'm pretty sure I understand what you mean, but can you be more specific? Can you include more examples of success cases for this type of selection? Maybe even failure? For example, what should happen at higher levels of subdivision? If a mesh has an odd number of faces such that one normal could be considered to be both Z- and Z+, what should happen? $\endgroup$ May 7, 2021 at 20:23
  • $\begingroup$ There is Select -> Select Similar -> Normal but that is only part of what you asked. $\endgroup$ May 7, 2021 at 20:25
  • $\begingroup$ @AllenSimpson I already racked my mind immensely trying to convey my issue, I'm not so sure I can do any better, but I can still try. Imagine a staircase, and imagine that I select a tread (google stair anatomy) somewhere in the middle of the staircase. What if I wanted to select the riser that's directly below it? If I do Select -> Select More, I'd get the riser above the tread and the riser below the tread when I only want one. This example makes no sense since, in theory, every riser has the same normals; imagine that it's a SINGLE step instead of a staircase, with only 2 risers and 1 tread $\endgroup$
    – user123645
    May 7, 2021 at 20:41
  • $\begingroup$ Man, if I knew that pressing enter immediately uploaded the comment, I sure as hell wouldn't've pressed it. $\endgroup$
    – user123645
    May 7, 2021 at 20:47
  • 1
    $\begingroup$ Your first example selects Y- adjacent to Z+, but only downwards. That's the tricky bit. If you wanted up and down, that would be easier. $\endgroup$ May 7, 2021 at 22:19

1 Answer 1

3
$\begingroup$

Select +Z faces and neighboring -Y faces if they go down:

import bpy, bmesh
from mathutils import Vector

up = Vector((0, 0, 1))
side = Vector((0, -1, 0))

def main():
    me = bpy.context.active_object.data
    bm = bmesh.from_edit_mesh(me)
    for face in bm.faces:
        if face.normal == up:
            face.select = True
            conditional_select_neighbors(face)
    bmesh.update_edit_mesh(me)


def conditional_select_neighbors(face):
    for loop in face.loops:  # each edge has a 'loop' associated with it for each face
        loop2 = loop.link_loop_radial_next  # radial_next gets the loop of the same edge but another face
        is_side = loop2.face.normal == side  # as R. Betts spotted, this is not enough
        loop3 = loop2.link_loop_next.link_loop_next
        is_lower = loop.vert.co.z > loop3.vert.co.z  
        if is_side and is_lower:
            loop2.face.select = True


if __name__ == "__main__":
    main()

Select +Z faces without -Y neighbors:

import bpy, bmesh
from mathutils import Vector

up = Vector((0, 0, 1))
side = Vector((0, -1, 0))

def main():
    me = bpy.context.active_object.data
    bm = bmesh.from_edit_mesh(me)
    for face in bm.faces:
        if face.normal == up:
            conditional_select(face)
    bmesh.update_edit_mesh(me)


def conditional_select(face):
    face.select = True
    for loop in face.loops:  # each edge has a 'loop' associated with it for each face
        loop2 = loop.link_loop_radial_next  # radial_next gets the loop of the same edge but another face
        if loop2.face.normal == side:  # in this case this is enough
            face.select = False


if __name__ == "__main__":
    main()
$\endgroup$
2
  • $\begingroup$ Thanks Markus, this worked! And thank you for giving both of the solutions, I actually needed a mix of both to do what I wanted. Despite my question being somewhat vague, you still understood it and gave me the answer I wanted. This'll save me at least an hour of time each time I need to make a new blocky map. $\endgroup$
    – user123645
    May 8, 2021 at 10:35
  • $\begingroup$ @user123645 Knowing the question is half of the answer. :) $\endgroup$ May 8, 2021 at 11:47

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.