when you developed a script that builds geometry each frame which takes some time - how do you organize everything to render an animation out of it?
When I hit render animation, blender often crashes or weird things are going on. I think this is because building geometry takes longer than blender renderer reserves time between rendering each frame. I convert mesh data into bezier via ops and assign a material permanently by a frame change pre handler.
One solution was not to use frame pre handler but rendering each frame manually. I wait a second via time.wait(1) before I start rendering one frame manually. That worked well but I wondered if there is common workflow to force blender to wait until all the script is executed, geometry is built, materials are assigned etc:
I just tried out, but I have the feeling that blender only makes sure that the script is executed but not that any of my converts via ops has finished too, what do you think?
Works at my machine as realtime preview, but when I hit render or use render pre handler, it mostly crashes or my material menues dissapear and blender doesn’t react properly anymore - especially when I render animation
I have done a lot of frame_change work and have not encountered a time limit. Your posted BLEND file keeps referencing a drive letter that I do not have on my system so Blender, annoyingly, keeps popping up a Try Again message box. However, I could see some of the code. The problem is your code is referencing the context in a render event. This is not allowed and produces an error when rendering. That is why your script does not work. Re-write your code to not use the context at all and it should work fine with rendering.
Sorry, it didn’t fix it really. The problems came back even not using context.
For my tests I can pause the script one second before rendering but it will be hard to do with V-Ray Exporter later because I can’t use the regular render animation button then…
Maybe someone has any quick solution that works for my existing script and can reupload the blend?
It’s not normal that whole menues disappear within the interface anyway? Do you experience this too with my file when you hit render animation and have a look at the material tab as an example?
Traceback (most recent call last):
File “C:\Users\Dennis\Downloads\blender_releases\blender-2.63a-release-windows64\2.63\scripts\addons\cycles\ui.py”, line 624, in poll
return context.material and CyclesButtonsPanel.poll(context)
AttributeError: ‘Context’ object has no attribute ‘material’
location:<unknown location>:-1
I think I could be right - there is too much happening in one frame for blender to handle it without waiting a moment before rendering, because I get no error messages if i do exactly this…
I see what you mean, Blender crashes. bpy.ops does not work in a Render event either (some can work but best to avoid and write your own versions). Also you don’t need to use bpy.data.scenes[0], you are provided with a scene by the event handler.
My guess is you are operating upon stale data. Because we don’t really know what the bpy.ops do (unless you read the Blender source code), we can never be sure of what state variables are in after you execute those kinds of statements. bpy.ops should be used in an Operator context and thus are useful for modeling tools or one-shot generators.
Generally speaking, deleting and removing objects from memory should be avoided unless you are absolutely sure there are no other users of that datablock. Are you really setting the active object to the plane you just deleted?
I have commented some code out and this prevents the crash, but it may no longer function as you intended.
import bpy
import time
amountElements = 400
def my_handler(scene):
modMesh = bpy.data.objects['Grid'].to_mesh(bpy.data.scenes[0],True,'RENDER')
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects['plasticplane.001'].select = True
print("Skip delete for now.")
#bpy.ops.object.delete()
for i in range(0,amountElements):
a = i+1
print("Working on " + str(a))
myTarget = bpy.data.objects['eq'+str(a)+'stange']
vertexValue = modMesh.vertices[i].co.z*10+10
eqTargetAdd = 0
myTarget.scale.z = 5
targetValue = (vertexValue/2)*(eqTargetAdd/2)+(vertexValue/2)+3
myTarget.location.z = bpy.data.meshes['plasticplane'].vertices[i].co.z = targetValue
print("Done with loop.")
bpy.ops.object.select_all(action='DESELECT')
print(".")
bpy.data.objects['plasticplane'].select = True
print(".")
#bpy.ops.object.duplicate_move()
print(".")
bpy.data.scenes[0].objects.active = bpy.data.objects['plasticplane.001']
print(".")
bpy.ops.object.convert(target='CURVE')
print(".")
currentCurve = bpy.data.objects['plasticplane.001'].data
print(".")
currentCurve.bevel_object = bpy.data.objects['BezierCircle']
print("Skip remove for now.")
#bpy.data.meshes.remove(modMesh)
print(".")
bpy.data.objects['Grid'].location.z+=.008
print(".")
bpy.data.objects['plasticplane.001'].data.materials.append(bpy.data.materials['black'])
print("Deselect")
bpy.ops.object.select_all(action='DESELECT')
bpy.app.handlers.frame_change_pre.append(my_handler)
Okay - not to get misunderstood: What I already have is exactly what I want to have but it does cause some issues, because I use ops in a frame loop, so that isn’t ideal.
Here is what it does:
take the faceless (only edges in it) base mesh and convert it into a curve
take the new curve and apply a bevel object curve to it so the edges get some thickness
What I animate via python is the faceless mesh and each frame it should be converted to a solid wire to output something that has thickness. Okay I also could try to animate the already solid wire, but then it could get really complicated because the vertices don’t follow a simple grid pattern anymore.
So the only thing that would be useful is finding another method to get a solid wireframe autmatically. Solidify modifer doesn’t work at a faceless mesh as far as I know because it doesn’t create a wireframe.
But it’s not too important - I can work out a workaround I think
What about something like this…
After looking at the code I am not really sure why you need this to happen in a frame change? I am just guessing but all you really need is to create a bunch of slightly altered curves from a mesh.
This code does that.
It appears as a button in the Tools panel of the 3D window. Select the plasticplane and click the button and you will get a bunch of new curve objects that are like the mesh object. I may have borked the math a bit, but perhaps this can get you what you need.
import bpy
bl_info = {
"name": "Convert",
"author": "Atom",
"version": (1, 0, 0),
"blender": (2, 6, 3),
"location": "View3D > Tool Shelf",
"description": "Convert plasticplane into curves.",
'warning': '',
"category": "Mesh"}
def removeMeshFromMemory (passedMeshName):
# Extra test because this can crash Blender.
mesh = bpy.data.meshes[passedMeshName]
try:
mesh.user_clear()
can_continue = True
except:
can_continue = False
if can_continue == True:
try:
bpy.data.meshes.remove(mesh)
result = True
except:
result = False
else:
result = False
return result
class MEshToCurves(bpy.types.Operator):
bl_idname = 'object.mesh_to_curves'
bl_label = 'MeshToCurves'
bl_description = 'Mesh To Curves'
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
ob = context.object
if ob != None:
source_name = "plasticplane"
ob_source = bpy.data.objects.get(source_name)
if ob_source != None:
me_source = ob_source.data
# Make a mesh for every element requested, derived from the source mesh.
amountElements = 10
# Loop and create.
for i in range(0,amountElements):
a = i+1
new_name = 'new_'+str(a)+'_grid'
me_temp = me_source.copy() # Make a unique mesh from the original (memory gobble, gobble..)
me_temp.name = 'me_' + new_name
ob_temp = bpy.data.objects.new(new_name,me_temp)
context.scene.objects.link(ob_temp)
vertexValue = me_temp.vertices[i].co.z*10+10
eqTargetAdd = 0
ob_temp.scale.z = 5
targetValue = (vertexValue/2)*(eqTargetAdd/2)+(vertexValue/2)+3
ob_temp.location.z = me_temp.vertices[i].co.z = targetValue
# Loop and convert, apply bevel.
for i in range(0,amountElements):
a = i+1
new_name = 'new_'+str(a)+'_grid'
# Now that we have a bunch of objects, let's convert them to curves.
bpy.ops.object.select_all(action='DESELECT')
# No need to duplicate because we have already created everything in the previous loop.
#bpy.data.objects[new_name].select = True
#bpy.ops.object.duplicate_move()
bpy.data.objects[new_name].select = True # Select the object by it's name.
context.scene.objects.active = bpy.data.objects[new_name]
print("Converting [" + new_name + "] to a curve.")
bpy.ops.object.convert(target='CURVE')
currentCurve = context.active_object.data #bpy.data.objects[new_name].data
print("New active object [" + context.active_object.name + "].")
print("New object type [" + context.active_object.type + "].")
ob_temp = context.active_object
currentCurve.name = 'cu_' + new_name
currentCurve.bevel_object = bpy.data.objects['BezierCircle']
# Loop and memory cleanup.
for i in range(0,amountElements):
a = i+1
ob_name = 'new_'+str(a)+'_grid'
ob_temp = bpy.data.objects.get(ob_name)
if ob_temp != None:
#context.scene.objects.unlink(ob_temp) # NOTE: Unlinked objects are still hanging around in memory.
me_name = 'me_new_'+str(a)+'_grid'
# Attempt to remove mesh from memory, this can fail so review the result.
r = removeMeshFromMemory(me_name)
if r == True:
print ("Removed [" + me_name + "] from memory.")
else:
print ("Failed to remove [" + me_name + "] from memory.")
else:
print("Could not locate plasticplane.")
return {'FINISHED'}
class ButtonMeshToCurves(bpy.types.Panel):
bl_label = 'Mesh To Curves'
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
def draw(self, context):
layout = self.layout
layout.operator('object.mesh_to_curves')
bpy.utils.register_class(MEshToCurves)
bpy.utils.register_class(ButtonMeshToCurves)