# How to get the mapping of face(vertex) index after applying geometry modifier?

Here's my nodes setup that converts each quad face to 16 vertices. For example, face 19 is converted to vertex 81 233 234 77…13. How can I find the mapping between generated vertices(or faces) and original face id in geometry nodes or python?

• What kind of format do you want them in? For what purpose? Is just knowing the numbers enough, or do you need them as csv list or something? Also, in your example, there are extra faces in between the 9-square groupings you've highlighted, are those important? Dec 9, 2022 at 1:52
• @Kuboå A csv list would be the best. I'm trying to create a bezier surface for each new vertex generated from the original quad face. Something like: for face in original_mesh_faces: get generated_vertices; create a bezier surface from the generated_vertices. The extra faces in between the groupings are not important. Dec 9, 2022 at 8:53

As a starting point, capture the original face index: Put your Doo Sabin algorithm after that (lower on the modifier stack). Python can access evaluated data without the need to apply the modifiers, like so:

import bpy
from bpy import context as C
from collections import defaultdict

face_to_eval_faces = defaultdict(set)
dg = C.evaluated_depsgraph_get()
ev_ob = C.object.evaluated_get(dg)
for i, wrapper in enumerate(ev_ob.data.attributes['orig face'].data):

print(list(face_to_eval_faces))


This lists all faces (their indices) in the evaluated geometry that "come from" the original face with index $$15$$. However, that's more than 9 faces, why? Let's see step by step:

1. Subdivide - face #15 is subdivided into 4 faces, each inheriting its attributes without a change. So far so good.
2. Dual Mesh - the 4 faces are converted to 4 vertices, each inheriting the attribute of the face that spawned it, so for those vertices orig face $$= 15$$. Still exactly what's intended.
3. Subdivide - again each face spawns more faces, but this time the attribute of interest is held by vertices! This is a problem, it causes an interpolation. Integer interpolation means that a spawned face will take a random¹ integer based on its neighbors. So the 9 "internal" faces (marked red on the images below) you're interested in have all neighbors with the same attribute, and therefore the randomness doesn't matter, but some of the "external" faces will still randomly get the attribute $$15$$, polluting your results. What you can do then is use my answer from this thread:

geometry nodes - doo sabin

In which I color the "internal" faces red, so you can just check which face is red: So replace your 4 node setup with 2 geonodes modifiers from that thread, then add yet another geonode modifier to convert the attribute to Face domain: And finally run a script that checks if the face is red (the shader code from the linked answer isn't very clear about it, but the red area has the doo sabin equal zero):

import bpy
from bpy import context as C, data as D
from collections import defaultdict

face_to_eval_faces = defaultdict(set)

dg = C.evaluated_depsgraph_get()
ev_ob = C.object.evaluated_get(dg)
for i, wrapper in enumerate(ev_ob.data.attributes['orig face'].data):
if ev_ob.data.attributes['doo sabin'].data[i].value != 0:
continue  # you don't care about the shared geo

print(list(face_to_eval_faces))

[196, 287, 175, 180, 212, 60, 61, 62, 63]


This lists 9 correct indices for the original face: From there all you have to do is the verts instead:

import bpy
from bpy import context as C, data as D
from collections import defaultdict

face_to_eval_verts = defaultdict(set)

dg = C.evaluated_depsgraph_get()
ev_ob = C.object.evaluated_get(dg)
for i, wrapper in enumerate(ev_ob.data.attributes['orig face'].data):
if ev_ob.data.attributes['doo sabin'].data[i].value != 0:
continue  # you don't care about the shared geo
face_to_eval_verts[wrapper.value].update(ev_ob.data.polygons[i].vertices)

print(list(face_to_eval_verts))


I'm too cool to check if they all match, but probably 😎

[65, 132, 70, 135, 17, 210, 177, 276, 146, 277, 209, 278, 145, 178, 279, 63] ¹ – it's not actually random, but since the behavior is not documented, not obvious, and probably hard to argue about once you read the source (if it's e.g. based on a hash you could argue it actually is random), IMHO it makes it unreasonable to treat it differently than as random.

• I wasn't too cool, however, to find out an easier approach which probably was just storing another boolean attribute on vertices added by the subdivision (check domain size before subdivision and set boolean based on index being equal or greater) which would be a much simpler setup. Dec 10, 2022 at 21:38
• Yes! I read your answer when I searched for the algorithm, didn't think I could use it here. Thanks! Dec 16, 2022 at 14:05