1
$\begingroup$

I am trying to render a series of *.vtp files (output of a cfd simulation) in Blender (v2.79). I have successfully installed pip into Blender's python, then using pip, installed vtk (i.e. pip install vtk).

The *.vtp files contain arrays of points that describe the location of particles. So, I create a new object and mesh, read the vtp file, transform the particle coordinates to mesh vertices. I also registered a callback so that when the frame changes, the mesh vertices are replaced with the particle positions in the correct vtp file.

I don't think I am updating the mesh vertices correctly.

I have pieced together the following code by scavenging random posts:

import vtk
from vtk.util import numpy_support
import os
import numpy as np
import bpy
import glob


def new_mesh(objname="MyObject", meshname="mesh"):
    """Create a mew mesh object"""
    mesh = bpy.data.meshes.new(meshname)  # add a new mesh
    obj = bpy.data.objects.new(objname, mesh)  # add a new object using the mesh

    scene = bpy.context.scene
    scene.objects.link(obj)  # put the object into the scene (link)
    scene.objects.active = obj  # set as the active object in the scene
    obj.select = True  # select object

    return mesh


def vtp_to_blender(fname, mesh=None):
    """given a vtp (particle) file, convert the particle positions to a blender
    mesh"""

    # check for file name
    if not os.path.exists(fname):
        print('Warning: file does not exist: {}'.format(fname))
        return

    # get the file name
    objname = os.path.basename(fname)

    # create reader
    reader = vtk.vtkXMLPolyDataReader()
    reader.SetFileName(fname)
    reader.Update()
    poly_data = reader.GetOutput()
    array = numpy_support.vtk_to_numpy(poly_data.GetPoints().GetData())

    if mesh is None:
        mesh = new_mesh(objname, 'points')

    verts = mesh.vertices
    if len(verts) != len(array):
        verts.add(len(array) - len(verts))
    verts.foreach_set('co', array.flatten())
    mesh.update()

def handle_frame_change(scene, mesh, flist):
    """callback to change the vtp file"""
    frame = scene.frame_current
    vtp_to_blender(flist[max(frame-1, 0)], mesh)

def test():
    mesh = new_mesh()
    flist = sorted(glob.glob('path/to/PART_*.vtp'))
    bpy.app.handlers.frame_change_pre.append(lambda scene: handle_frame_change(scene, mesh, flist))

if __name__ == "__main__":
    test()

This all works nicely and the vertices are move when I hit "play". However, I have two problems:

1. If attached a sphere to each vertex by adding a sphere to the scene, setting the parent to the vtp file(s), and selecting duplication -> verts on the parent vtp object, when I hit "play" I get this Traceback:

Traceback (most recent call last):
  File "/Text", line 59, in <lambda>
  File "/Text", line 52, in handle_frame_change
  File "/Text", line 45, in vtp_to_blender
RuntimeError: Error: Cannot add vertices in edit mode

2. If I apply any transforms to the vtp file object and hit "play", the transform persists when the mesh is updated however I occasionally receive segfaults with the following backtrace:

# backtrace
blender-2.79b/blender(BLI_system_backtrace+0x20) [0x1a6c700]
blender-2.79b/blender() [0x1078395]
/lib64/libc.so.6(+0x35270) [0x2b6f8a753270]
blender-2.79b/blender(CustomData_get_active_layer+0x1c) [0x1794e2c]
blender-2.79b/blender(CustomData_bmesh_update_active_layers+0xd5) [0x1796155]
blender-2.79b/blender(BKE_mesh_update_customdata_pointers+0x58) [0x18012a8]
blender-2.79b/blender(ED_mesh_vertices_add+0x16e) [0x12ba99e]
blender-2.79b/blender(RNA_function_call+0x12) [0x190e5d2]
blender-2.79b/blender() [0x1471ef3]
blender-2.79b/blender(PyObject_Call+0x5c) [0x2e4f06c]
blender-2.79b/blender(PyEval_EvalFrameEx+0x3812) [0x2f123a2]
blender-2.79b/blender() [0x2f1869e]
blender-2.79b/blender(PyEval_EvalFrameEx+0x6c8a) [0x2f1581a]
blender-2.79b/blender(PyEval_EvalFrameEx+0x74eb) [0x2f1607b]
blender-2.79b/blender() [0x2f1869e]
blender-2.79b/blender(PyEval_EvalCodeEx+0x23) [0x2f18773]
blender-2.79b/blender() [0x2e79674]
blender-2.79b/blender(PyObject_Call+0x5c) [0x2e4f06c]
blender-2.79b/blender(bpy_app_generic_callback+0x9c) [0x147a01c]
blender-2.79b/blender(BLI_callback_exec+0x2d) [0x1a30f6d]
blender-2.79b/blender(BKE_scene_update_for_newframe_ex+0x52) [0x18765e2]
blender-2.79b/blender(ED_update_for_newframe+0xa6) [0x1361056]
blender-2.79b/blender(wm_event_do_notifiers+0x188) [0x107eb48]
blender-2.79b/blender(WM_main+0x20) [0x1078f80]
blender-2.79b/blender(main+0x3f8) [0x1016a38]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x2b6f8a73fc05]
blender-2.79b/blender() [0x10752ee]
$\endgroup$

1 Answer 1

0
$\begingroup$

I figured it out! just in case anyone stumbles on this, this trick is to use a temporary bmesh and then to over-write the mesh of the object. So the vtp_to_blender function should be:

def vtp_to_blender(fname, mesh=None):
    """given a vtp (particle) file, convert the particle positions to a blender
    mesh"""

    # make sure we are in object mode
    try:
        bpy.ops.object.mode_set(mode="OBJECT")
    except:
        return

    # check for file name
    if not os.path.exists(fname):
        print('Warning: file does not exist: {}'.format(fname))
        return

    # get the file name
    objname = os.path.basename(fname)

    # create a new mesh if needed
    if mesh is None:
        mesh = new_mesh(objname, 'points')

    # create reader
    reader = vtk.vtkXMLPolyDataReader()
    reader.SetFileName(fname)
    reader.Update()
    poly_data = reader.GetOutput()
    array = numpy_support.vtk_to_numpy(poly_data.GetPoints().GetData())

    # use a temporary mesh to build the vertices
    temp_mesh = bpy.data.meshes.new('temp')  # add a new mesh
    verts = temp_mesh.vertices
    # add the vertices from the vtk reader without looping!
    verts.add(len(array))
    verts.foreach_set('co', array.flatten())

    # create a new bmesh, fill with temp mesh
    bm = bmesh.new()
    bm.from_mesh(temp_mesh)

    # write the bmesh to the object mesh
    bm.to_mesh(mesh)

    # free and update
    bm.free()
    mesh.update()
    bpy.context.scene.update()
$\endgroup$

You must log in to answer this question.

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