0

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)

bpy.ops.object.delete()

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]
strand.append(currentVertex.index)

for i in range(maximumNumberOfSegments):
    currentEdgeIndex = theVertex2EdgeMap[currentVertex.index]

    if currentEdgeIndex == -1:
        break

    currentEdge = theMesh.data.edges[currentEdgeIndex]

    currentVertex = theMesh.data.vertices[currentEdge.vertices[1]]
    strand.append(currentVertex.index)

return strand

def BuildStrands(theMesh, theVertex2EdgeMap): strands = [] currentStartVertexIndex = 0 maximumNumberOfStrands = 100000

for i in range(maximumNumberOfStrands):
    if currentStartVertexIndex >= len(theMesh.data.vertices):
        break

    strand = BuildStrand(theMesh, theVertex2EdgeMap, currentStartVertexIndex)
    strands.append(strand)

    currentStartVertexIndex = strand[-1] + 1

return strands

def CreateEmitterMesh(theMesh): mesh = bpy.data.meshes.new(emitterMeshName) mesh.from_pydata([(0, 0, 0)], [], []) mesh.update()

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
scene.objects.link(object)
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:
        strands.append(strand[0::math.ceil(len(strand)/maximumNumberOfSegments)])
    else:
        strands.append(strand)

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): bpy.ops.object.particle_system_add() 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: bpy.ops.particle.particle_edit_toggle()

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

if isInitialSetting:
    bpy.ops.particle.particle_edit_toggle()

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

@persistent 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): bpy.app.handlers.frame_change_pre.clear() bpy.app.handlers.frame_change_pre.append(theUpdateMethod) bpy.app.handlers.frame_change_post.clear() bpy.app.handlers.frame_change_post.append(theUpdateMethod)

if name == "main": # Remove possibly existing old mesh if shouldRemoveOldMesh: RemoveOldMesh(emitterMeshName);

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)
HideMesh(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
SetFrameHandlers(UpdateParticleSystem)

else: # Get existing meshes mesh = bpy.context.scene.objects[meshName] emitterMesh = bpy.context.scene.objects[emitterMeshName]

# Set frame handlers
SetFrameHandlers(UpdateParticleSystem)

```

Joshua C
  • 1
  • 1
  • 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... https://blender.stackexchange.com/a/167899/31447 – brockmann Jun 30 '20 at 21:20
  • @brockmann it looks like i'm still having the same problem – Joshua C Jul 01 '20 at 22:40
  • 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? – brockmann Jul 01 '20 at 23:17

0 Answers0