- edited description
New Morph Transfer to Nearest Face could use Blender's closest_point_on_mesh()
Blender internally takes advantage of structures that subdivide the 3d space (kd-trees and BVH trees), which make a lot of spatial lookups way faster.
Instead of manually considering all pairs with numpy, why not use Blender’s API for the Nearest Face method?
The relevant closest_point_on_mesh() method is documented here: https://docs.blender.org/api/current/bpy.types.Object.html (notice that you can get the face index). I attached a .blend that contains a simple example script that finds the closest point to two objects.
This could maybe also be applied to the Geograft method with some additional considerations (you need the closest vertex in that case, not a face index). Even better, the Blender’s KDTree API might be more useful in that case: https://docs.blender.org/api/current/mathutils.kdtree.html . Also the BVHTree API: https://docs.blender.org/api/current/mathutils.bvhtree.html
Comments (19)
-
reporter -
reporter - edited description
-
reporter - edited description
-
reporter - edited description
-
reporter - edited description
-
reporter - edited description
-
Also shrinkwrap can be used to avoid transferring jcms at all, see
, personally I find it very effective. I don’t like to get lots of jcms on the outfit unless necessary. Then some jcms may be needed, especially custom jcms for the outfit, but those are loaded not tranfered.#226 -
repo owner Xin, thank you for this. It promises to make the tool much faster, since almost all time is spent figuring out the nearest faces. However, I am currently stuck because the function seems to return face indices larger than the total number of faces. Consider the following program:
import bpy
ob = bpy.context.object
data = [ob.closest_point_on_mesh(v.co) for v in ob.data.vertices]
fnums = [idx for res,loc,nor,idx in data]
print("Max fnum = %d" % max(fnums))
print("Total fnums = %d" % len(ob.data.polygons))
When applied to G8F, the output is
Max fnum = 65468
Total fnums = 16368
So what am I missing? One can note that 4*16368 = 65372.
-
repo owner I asked the question on BA: https://blenderartists.org/t/closest-point-on-mesh-returns-face-number-exceeding-the-total-number-of-faces/1275462
Alessandro, shrinkwrap works ok, but the results are not quite as good as with transferred JCMs, cf the bend-forward poses in https://diffeomorphic.blogspot.com/p/morphs-section-version-15.html.
-
repo owner Xin, the test script only uses one mesh, but the problem arose with two meshes. In the case of a single triangle, the list is [0,0,0], and max([0,0,0]) = 0, which is less than len(ob.data.polygons) = 1. Fine. Things also work find with primitives that I have tested, both pure triangle and pure quad meshes. The problem arises with G8F, where the max face number > total number of faces.
-
reporter Oh sorry for that, I just misread what you wrote first, disregard my previous answer.
The real answer is just the subdiv modifier which is being taken into account by the function.
I forgot to mention this before, but in my attached example, you can see that clearly: one of the cubes was subdivided precisely to demonstrate that the subdiv is taken into account by closest_point_on_mesh(). Try making the subdiv invisible and you will see how my example changes. Same thing happens with g8, which subdivided has way more faces than what ob.data.polygons returns.
That’s why the function has a depsgraph parameter too, to change the default behavior (more suitable for more complex cases with more modifiers than just a single subdiv like in this example): https://docs.blender.org/api/current/bpy.types.Object.html#bpy.types.Object.closest_point_on_mesh .
-
repo owner Thank you, Xin, that was it. Morph transfer is now very much faster, and the actual matching between verts and faces is lightning fast.
-
reporter Good news and thanks again.
Have you tried seeing if building the BVH tree yourself outside the loop (see; https://docs.blender.org/api/current/mathutils.bvhtree.html ) and querying it inside makes it faster, compared to calling closest_point_on_mesh() each time? I haven’t looked into how Blender implements that function, but it is perhaps not as well optimized for a lot of sequential queries as a direct BVH tree that you can create yourself. I really doubt it will make much of a difference (and it might be worse) since I suspect the tree you are querying with the function already internally exists for rendering purposes, but it might be worth testing.
-
reporter Oh and for the Geograft method I think the KDTree might give a performance boost too: https://docs.blender.org/api/current/mathutils.kdtree.html , since you need to match vertex to vertex in that one. But I don’t expect that to be that big of a difference since geografts don’t have high vertex counts.
-
repo owner I don’t think there is anything more to gain here. I tested to transfer from G8F to a mesh which covers it tightly and has almost four times as many verts. Not a geograft, but similar in spirit. The standard JCMs were transferred in 21.9 s, but only 0.7 s was spent on matching verts with faces. So the matching code is not critical anymore.
-
repo owner Then I transferred all Meipex morphs from Golden Palace to the merged mesh. Total time 29.5 s, vertex matching 1.0 s. Not super critical either. In principle you could see artifacts from using nearest-vertex matching here too, but for the intended application the vertices should match exactly. A KDTree may be useful for the body method though, where you match everything but the geografts. Will check that out.
-
Commit 27eac5d seems to work great, thank you Xin and Thomas for the nice speedup. As for shrinkwrap I can’t reproduce the exact issue with the bardot skirt, but of course I agree that transferring jcms is more precise.
-
reporter This is pretty much resolved then. Thanks again Thomas.
-
reporter - changed status to resolved
- Log in to comment