6
$\begingroup$

I'm trying to update an exporter to work with Blender 2.8 but can't seem to get a mesh data-block modified by modifiers or shape keys. It always gives me the base mesh.

I'm currently using Object.to_mesh(). But reading Object.data or bmesh.from_mesh(obj) all returns only the base mesh. So what is the new function to call for getting a mesh data-block with the modifiers and shape keys applied?

$\endgroup$
9
$\begingroup$

Just tested batFINGER's solutions. It's pretty convoluted and you have to call bm.clear() every time the mesh updates before calling the last two functions. It works but really doesn't feel like the intended way to do things. Digging a bit further I think I found the intended solution:

#We only have to do this once:
ob = bpy.context.active_object #getting the object we want.
dg = bpy.context.evaluated_depsgraph_get() #getting the dependency graph

#This has to be done every time the object updates:
ob = ob.evaluated_get(dg) #this gives us the evaluated version of the object. Aka with all modifiers and deformations applied.
me = ob.to_mesh() #turn it into the mesh data block we want.

Still annoying that we can't pick between preview and render for which version of the modifiers it applies (it seems to always pick preview). If there is a way to do that I would love to know.

$\endgroup$
2
  • 1
    $\begingroup$ This answer is amazing and saved so much time, a related note is that object must be linked in a collection first for this to actually work, otherwise it won't give error, but modifiers won't be applied (working on object that are not linked) $\endgroup$ – Iyad Ahmed Jan 22 at 5:34
  • $\begingroup$ This was the exact solution I needed for my project. evaluated_get() made it easy! Thank you! $\endgroup$ – Raptormeat Jan 29 at 5:00
7
$\begingroup$

BMesh.from_object(...)

Using the bmesh from object, can be written to another mesh as the deformed modified mesh.

Test script, writes context object deformed / modified mesh to new mesh "ObjBmesh"

import bpy
import bmesh

context = bpy.context
ob = context.object
dg = context.evaluated_depsgraph_get()

bm = bmesh.new()
bm.from_object(ob, dg)
bm.to_mesh(bpy.data.meshes.new("ObjBmesh"))

Using the evaluated object.

Further to the answer of @Somebody285 can get the evaluated objects from the depsgraph's objects collection.

Python console example, default cube has a subsurf modifier with default settings.

>>> dg = C.evaluated_depsgraph_get()
>>> cube = dg.objects.get("Cube")
>>> me = cube.to_mesh()
>>> len(me.vertices)
26

>>> len(cube.data.vertices)
26

me is the evaluated (modifiers / shapekeys) applied mesh.

>>> me
bpy.data.meshes['Cube']

>>> D.meshes["Cube"]
bpy.data.meshes['Cube']

even tho it represents itself the same, it is not the base mesh object

>>> len(D.meshes["Cube"].vertices)
8

>>> me is D.meshes['Cube']
False

The original property of an object points to its unevaluated original.

>>> D.objects['Cube'].data is D.meshes['Cube']
True

>>> cube.original
bpy.data.objects['Cube']

>>> cube.original.data is D.meshes['Cube']
True

The object is evaluated, shown by its is_evaluated property. The "evaluated mesh" does not have this set.

>>> cube.is_evaluated
True

>>> me.is_evaluated
False

However it is, as displayed in error message when trying to use it as a mesh data block.

>>> newcube = D.objects.new("Cube2", me)
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
RuntimeError: Error: Can not create object in main database with an evaluated data data-block

>>> newcube = D.objects.new("Cube2", cube.data)
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
RuntimeError: Error: Can not create object in main database with an evaluated data data-block

Copying gives us a mesh data block.

>>> newcube = D.objects.new("Cube2", cube.data.copy())
$\endgroup$

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.