3
$\begingroup$

enter image description hereLet's say I have a cube in Blender and there are two materials on it. In this example red and blue.

I am looking for the face with the index 10, which is a face with blue material. If I select all blue faces and separate them from the rest of the mesh, the face gets a new index value as well as the rest of the blue faces. I would like to get this updated face index AND the updated Vertex Points indices of this face through a python script.

Is it possible to get the "re-indexed" value of the face without really going through the seperation and rejoin phase?

I am very grateful for any help!

$\endgroup$
6
  • $\begingroup$ my guess is the order of faces remain the same, and so all you would need to check is how many faces with a lower index would be separated together with the face of interest. $\endgroup$ Sep 13, 2023 at 20:15
  • $\begingroup$ @markus-von-broady That could be the case. However, I also need the updated vertex point indices that belongs to this face. I forgot to mention that in my post and just added it. Do you have any ideas on how best to implement this in python? $\endgroup$
    – Erdorano
    Sep 13, 2023 at 20:33
  • $\begingroup$ Probably same story with vertices… But I didn't test it. $\endgroup$ Sep 13, 2023 at 20:40
  • $\begingroup$ Is it acceptable to separate a duplicated mesh with python, fetch the updated vertex indices (given the re-indexed face) and throw that away afterwards? That would make the scripting easier (and potentionally faster). $\endgroup$
    – taiyo
    Sep 13, 2023 at 22:56
  • $\begingroup$ @taiyo I just checked it and this should be acceptable as long as the duplicated object gets entirely purged afterwards (can't be found in orphan data tab later on) $\endgroup$
    – Erdorano
    Sep 14, 2023 at 6:33

2 Answers 2

1
$\begingroup$

Here is another script which relies more on Blender's built-in methods to retrieve the values. This version has the advantage to still run very fast with bigger meshes. For an object with 100000 faces this script takes around 0.5 seconds while @Erdorano's version needs around 25 seconds on my machine.

Usage: select one face of an object and run the script. Doesn't matter if the object is in Edit or Object mode, only needs to be selected.

import bpy

# get the selected object in edit mode
bpy.ops.object.mode_set(mode = 'OBJECT')
bpy.ops.object.editmode_toggle()
obj = bpy.context.active_object

# get face index
old_face_index = -1
for poly in obj.data.polygons:
    if poly.select:
        old_face_index = poly.index
        break

# compute new face index
material_index = obj.data.polygons[old_face_index].material_index
new_face_index = -1
for poly in obj.data.polygons:
    if poly.material_index == material_index:
        new_face_index += 1
        if poly.select:
            break

# create a duplicate
bpy.ops.object.editmode_toggle()
bpy.ops.object.duplicate()
temporary_obj = bpy.context.object
temporary_obj.name = "temporary" # choose a name which does not exist in the scene

# separate all faces with face material
bpy.ops.object.editmode_toggle()
temporary_obj.data.polygons[old_face_index].select = True
bpy.ops.mesh.select_similar(type='MATERIAL')
bpy.ops.mesh.separate(type='SELECTED')

# select the separated mesh
bpy.ops.object.editmode_toggle()
bpy.ops.object.select_all(action='DESELECT')
temporary_obj001 = bpy.data.objects['temporary.001']
temporary_obj001.select_set(True)

# select the same face in the separated mesh and read the vertex data
temporary_obj001.data.polygons[new_face_index].select = True
bpy.ops.object.editmode_toggle()
new_vertex_indices = [ v for v in temporary_obj001.data.polygons[new_face_index].vertices ]

# we got all we want
print(
    '\nselected face index:', old_face_index,
    '\nnew face index:',  new_face_index,
    '\nnew vertex indices:', new_vertex_indices
)

# delete temporary objects
bpy.ops.object.editmode_toggle()
tmp_mesh = temporary_obj.data
tmp_mesh001 = temporary_obj001.data
temporary_obj.select_set(True)
bpy.ops.object.delete(confirm=False) # delete from outliner
bpy.data.meshes.remove(tmp_mesh)     # delete from orphaned data
bpy.data.meshes.remove(tmp_mesh001)  # delete from orphaned data
$\endgroup$
1
  • 1
    $\begingroup$ Thanks for this approach. I think both have their pros and cons. However I'll choose yours as the answer as its much more performant. $\endgroup$
    – Erdorano
    Sep 17, 2023 at 10:09
3
$\begingroup$

I checked with @markus-von-broady idea how the indexes are organized and it looks like he was right. My script attempt gets a face index as input. The vertices of this face are put into a list and it gets checked which material is on this face. Then all the faces in the object with the same material are gone through and the vertices are compared with those of the original face. If the ids are smaller, they are placed in another list. Then the original vertex ids are calculated down using the amount of smaller ids. In my tests so far, this approach works

import bpy

obj = bpy.context.active_object # Find the object

target_face_index = 567 # Example
material_idx = obj.data.polygons[target_face_index].material_index

face = obj.data.polygons[target_face_index]

target_face_vertex_ids = [vertex_index for vertex_index in face.vertices] # List of vertex IDs that make up the face

smaller_vertex_ids = [] # Empty list in which smaller vertex ids are later stored

# Go through all the faces in the object
for face in obj.data.polygons: 
    # Consider only faces with the same material
    if face.material_index == material_idx: 
        # Read Vertex Ids from found Face
        for vertex_index in face.vertices: 
            # Ensure that Ids are not part of the output Faces
            if vertex_index not in target_face_vertex_ids: 
                smaller = any(vertex_index < i for i in target_face_vertex_ids)
                # If Vertex Id is smaller than that of the output face
                if smaller: 
                    # Make sure the Id is not already in the list
                    if vertex_index not in smaller_vertex_ids:    
                        smaller_vertex_ids.append(vertex_index)

for element in target_face_vertex_ids:
    count = sum(1 for vertex_id in target_face_vertex_ids if vertex_id < element)
    rest = sum(1 for vertex_id in smaller_vertex_ids if vertex_id < element)
    print(count + rest)
$\endgroup$

This site is temporarily in read-only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .