I'm trying to render a video of two moving fibers. After applying an emitter to the fibers meshes and applying the constraint copy transform to the emitter I am able to see the emitter moving in sync with the fiber mesh, but when I save the file the emitter freezes on the current frame in the UI and the animation output.

For context I'm running version 2.79 of Blender, I am using a render farm, but I don't think that's the issue since I see freezing in the .blend file, and the .blend file is on cycles render.

Any help is appreciated! Thanks (Also put the hair emitter script below)

import bpy
import math
import mathutils
import os
import threading
import time
import logging
from bpy.app.handlers import persistent

# Tested with Blender 2.66

# File configuration
meshName = "hair"
emitterMeshName = "hair-emitter"
shouldRemoveOldMesh = True

# Hair configuration
useBSplineInterpolation = True
numberOfSegmentsToDrawForPreview = 10
numberOfSegmentsToRender = 12
hairShape = -0.4
hairWidth = 0.7

def RemoveOldMesh(theMeshName):
    candidateList = [item.name for item in bpy.data.objects]
    for name in candidateList:
        bpy.data.objects[name].select = (name == theMeshName)

def BuildVertex2EdgeMap(theMesh):
    vertex2EdgeMap = [-1] * len(theMesh.data.vertices)
    for edge in theMesh.data.edges:
        vertex2EdgeMap[edge.vertices[0]] = edge.index
    return vertex2EdgeMap

def BuildStrand(theMesh, theVertex2EdgeMap, theStartVertexIndex):
    strand = []
    maximumNumberOfSegments = 1000

    currentVertex = theMesh.data.vertices[theStartVertexIndex]
    for i in range(maximumNumberOfSegments):
        currentEdgeIndex = theVertex2EdgeMap[currentVertex.index]
        if currentEdgeIndex == -1:
        currentEdge = theMesh.data.edges[currentEdgeIndex]
        currentVertex = theMesh.data.vertices[currentEdge.vertices[1]]
    return strand

def BuildStrands(theMesh, theVertex2EdgeMap):
    strands = []
    currentStartVertexIndex = 0
    maximumNumberOfStrands = 100000
    for i in range(maximumNumberOfStrands):
        if currentStartVertexIndex >= len(theMesh.data.vertices):
        strand = BuildStrand(theMesh, theVertex2EdgeMap, currentStartVertexIndex)
        currentStartVertexIndex = strand[-1] + 1
    return strands

def CreateEmitterMesh(theMesh):
    mesh = bpy.data.meshes.new(emitterMeshName)
    mesh.from_pydata([(0, 0, 0)], [], [])
    object = bpy.data.objects.new(emitterMeshName, mesh)
    object.data = mesh
    object.rotation_euler = mathutils.Euler((0.5 * math.pi, 0, 0))
    scene = bpy.context.scene
    bpy.context.scene.objects.active = object
    bpy.ops.object.constraint_add(type = "COPY_TRANSFORMS")
    object.constraints[0].target = theMesh
    return object

def HideMesh(theMesh):
    theMesh.hide = True
    theMesh.hide_render = True

def SubSampleStrandSegments(theStrands):
    strands = []
    maximumNumberOfSegments = 51
    for strand in theStrands:
        if len(strand) > maximumNumberOfSegments:
    return strands

def SortStrands(theStrands):
    sortedStrands = []
    strandSegments = sorted([len(strand) for strand in theStrands])
    numberOfSegmentsForSystems = sorted(list(set(strandSegments)))
    for numberOfSegments in numberOfSegmentsForSystems:
        sortedStrands.append([strand for strand in theStrands if len(strand) == numberOfSegments])

    return sortedStrands

def CreateParticleSystem(theEmitMesh, theNumberOfParticles, theNumberOfSegments):
    particleSystem = theEmitMesh.particle_systems.active
    particleSystem.name = "hair"
    particleSettings = particleSystem.settings
    particleSettings.name = "hair-settings"
    particleSettings.type = "HAIR"
    particleSettings.count = theNumberOfParticles
    particleSettings.emit_from = "FACE"
    particleSettings.use_hair_bspline = useBSplineInterpolation
    particleSettings.draw_step = numberOfSegmentsToDrawForPreview
    particleSettings.render_step = numberOfSegmentsToRender
    particleSettings.hair_step = theNumberOfSegments - 1
    particleSettings.use_advanced_hair = True
    particleSettings.physics_type = "KEYED"
    particleSettings.cycles.shape = hairShape
    particleSettings.cycles.root_width = hairWidth
    return particleSystem

def CreateParticleSystems(theStrands, theEmitMesh):
    for strandsForParticleSystem in theStrands:
        numberOfParticles = len(strandsForParticleSystem)
        numberOfSegments = len(strandsForParticleSystem[0])
        CreateParticleSystem(theEmitMesh, numberOfParticles, numberOfSegments)

def SetParticleStrand(theStrand, theParticles, theMesh, theFrame):
    numberOfSegments = len(theStrand)
    vertices = mesh.data.shape_keys.key_blocks[theFrame - 1].data
    startCoordinates = vertices[theStrand[0]].co
    theParticles.location = startCoordinates
    theParticles.hair_keys[0].co = startCoordinates
    for i in range(numberOfSegments - 1):
        theParticles.hair_keys[i + 1].co = vertices[theStrand[i + 1]].co

def SetParticleStrands(theStrands, theParticleSystem, theMesh, theFrame, isInitialSetting):
    if isInitialSetting:
    for i in range(len(theStrands)):
        SetParticleStrand(theStrands[i], theParticleSystem.particles[i], theMesh, theFrame)

    if isInitialSetting:

def SetStrandsForParticleSystems(theMesh, theEmitMesh, theFrame, isInitialSetting):
    theStrands = theEmitMesh["strands"]
    for i in range(len(theStrands)):
        SetParticleStrands(theStrands[i], theEmitMesh.particle_systems[i], theMesh, theFrame, isInitialSetting)

def SaveParticleStrands(theStrands, theEmitMesh):
    theEmitMesh["strands"] = theStrands

def UpdateParticleSystem(scene):
    global mesh, emitterMesh
    oldMesh = bpy.context.scene.objects.active
    bpy.context.scene.objects.active = emitterMesh
    start = scene.frame_start
    end = scene.frame_end
    current = max(start, min(scene.frame_current, end))
    SetStrandsForParticleSystems(mesh, emitterMesh, current, False)
    bpy.context.scene.objects.active = oldMesh

def SetFrameHandlers(theUpdateMethod):

if __name__ == "__main__":
    # Remove possibly existing old mesh
    if shouldRemoveOldMesh:
    mesh = bpy.context.scene.objects[meshName]
    # Calculate strand segments
    vertex2EdgeMap = BuildVertex2EdgeMap(mesh)
    strands = BuildStrands(mesh, vertex2EdgeMap)
    # Subsample strand segments
    strands = SubSampleStrandSegments(strands)
    strands = SortStrands(strands)
    # Create dummy emitter and particle system
    emitterMesh = CreateEmitterMesh(mesh)
    # Create particle systems
    CreateParticleSystems(strands, emitterMesh)
    # Save strands into emitter mesh
    SaveParticleStrands(strands, emitterMesh)
    # Set current strands
    start = bpy.context.scene.frame_start
    end = bpy.context.scene.frame_end
    current = max(start, min(bpy.context.scene.frame_current, end))
    SetStrandsForParticleSystems(mesh, emitterMesh, current, True)
    # Set frame handlers
    # Get existing meshes
    mesh = bpy.context.scene.objects[meshName]
    emitterMesh = bpy.context.scene.objects[emitterMeshName]
    # Set frame handlers
  • $\begingroup$ I don't have 2.79 anymore. However, sounds like there is something wrong with the handlers. Try to remove bpy.app.handlers.* in SetFrameHandlers() one by one and see if the error persists. Also have a look into the console, as of Blender 2.79b or something they changed the arguments for handlers... blender.stackexchange.com/a/167899/31447 $\endgroup$ – brockmann Jun 30 '20 at 21:20
  • $\begingroup$ @brockmann it looks like i'm still having the same problem $\endgroup$ – Joshua C Jul 1 '20 at 22:40
  • $\begingroup$ Downloaded blender 2.79, renamed the cube to 'hair', added a particle system called 'hair-emitter' and I still get quite a bunch of errors. What else is required to set that up? $\endgroup$ – brockmann Jul 1 '20 at 23:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.