11
$\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$

3 Answers 3

19
$\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$
3
  • 4
    $\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$
    – Eyad Ahmed
    Commented Jan 22, 2021 at 5:34
  • 2
    $\begingroup$ This was the exact solution I needed for my project. evaluated_get() made it easy! Thank you! $\endgroup$
    – Raptormeat
    Commented Jan 29, 2021 at 5:00
  • $\begingroup$ How do you update original object mesh with the evaluated data ? $\endgroup$
    – StackHola
    Commented Jul 22, 2022 at 11:30
11
$\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$
1
  • $\begingroup$ Any reasons @Downvoter? $\endgroup$
    – brockmann
    Commented Jul 9, 2021 at 13:27
2
$\begingroup$

To add to @batFINGER's edited answer, another way to get a finalized copy of an evaluated mesh data block is using bpy.data.meshes.new_from_object()

for example:

import bpy

obj = bpy.context.active_object
dg = bpy.context.evaluated_depsgraph_get()

bpy.data.meshes.new_from_object(obj,depsgraph=dg)

another version with the same result:

bpy.data.meshes.new_from_object(dg.objects[obj.name])

Both @batFINGER's evaluated_obj.data.copy() method and these methods create a new mesh in bpy.data.meshes, so remember to remove them if you are not using them anymore or they will hang around until the file is saved and reloaded.

$\endgroup$

You must log in to answer this question.

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