1
$\begingroup$

I try to export a collection of meshes such that

  1. The original meshes is not touched by the exporter
  2. All modifiers affect the exported mesh
  3. The exported mesh is triangulated
  4. It should not look like the file has been changed by the exporter

It is permissable to not fullfil the fourth requirement though.

Here is my preparation functions, used to copy the meshes and joining them into one:

def collect_meshes(collection):
    meshes = []
    for object in collection:
        if object.type == 'MESH':
            tmp = object.copy()
            tmp.data = object.data.copy()
            meshes.append(tmp)
    return meshes

def apply_modifiers(objects):
    context = bpy.context.copy()
    for object in objects:
        # Cannot select this object because it is not in a view layer
        # object.select_set(True)
        for modifier in object.modifiers:
            # How to apply a modifier on a different object? 
            # bpy.ops.object.modifier_apply(context, modifier =  modifier.name)
            pass

def join_meshes(mesh_array):
    context = bpy.context.copy()
    context['active_object'] = mesh_array[0]
    context['selected_editable_objects'] = mesh_array
    bpy.ops.object.join(context)
    return context['active_object']

def triangulate(mesh):
    bm = bmesh.new()
    bm.from_mesh(mesh)
    bmesh.ops.triangulate(bm, faces = bm.faces[:])
    bm.to_mesh(mesh)

In case something went wrong, I also have the following cleanup:

class MeshCopy:
    def __init__(self, mesh):
        self.__mesh = mesh
    
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.__mesh.user_clear()
        bpy.data.objects.remove(self.__mesh)
    
    def get(self):
        return self.__mesh

So I can use the with statement for RAII. Is only way to get this working to create a copy of the original file, and export the content of that file and then delete the copy?

$\endgroup$
0

1 Answer 1

2
$\begingroup$

Bmesh from object.

See How do I get a mesh data-block with modifiers and shape keys applied in Blender 2.8? Can use the evaluated mesh and look at loop_triangles or with a bmesh and call the bmesh triangulate operator. In both cases no new data need be added to file.

The two answers show how to get the evaluated mesh as either a bmesh or an evauated mesh. In either case there would appear little need to bother with a context manager to use the with statement.

enter image description here Result of running on two selected subsurfed default cubes, creating one joined and triangulated to export

Here is an example that creates a joined and triangulated bmesh from all the selected mesh objects.

Just as with multiple calls to bm.from_mesh(...) can be used to read multiple meshes into one bmesh, can do same for bm.from_object.

On each "read" the new vertices are transformed by the objects matrix world.

To demonstrate result, a new mesh object is created and added to scene, whereas the exporter can simply re format the bmesh data.

import bpy
import bmesh
context = bpy.context

meshobs = [o for o in context.selected_objects if o.type == 'MESH']

dg = context.evaluated_depsgraph_get()

bm = bmesh.new()

for o in meshobs:
    n = len(bm.verts)
    bm.from_object(o, dg)
    bmesh.ops.transform(
            bm,
            verts=bm.verts[n:],
            matrix=o.matrix_world,
            )
    
bmesh.ops.triangulate(
        bm,
        faces=bm.faces,
        )
        
# new object to test
me = bpy.data.meshes.new("TriExportTest")
bm.to_mesh(me)
ob = bpy.data.objects.new("TriExportTest", me)
context.collection.objects.link(ob)

World or Object Coordinates?

Note the created bmesh above will have its vertices use the global coordinates of the input meshes. And an identity matrix world (no transform) To more closely emulate the join operator, by using object space of the active object

Save and calculate inverse of matrix world of active object.

mw = context.active_object.matrix_world
mwi = mw.inverted()

then in the bmesh transform operator

    matrix = mwi @ o.matrix_world

and finally assign the matrix world to the joined object

ob = bpy.data.objects.new("TriExportTest", me)
ob.matrix_world = mw
$\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.