0
$\begingroup$

I am looking to automate a process that takes objects from one collection and bakes diffuse from objects in another collection.

The issue is that everytime I run the code, Blender either crashes or stops after baking after completeting 2 objects.

Below is the python script that I'm trying to run.

import os
import bpy

os.system("cls")


bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 16
bpy.context.scene.render.bake.use_selected_to_active = True
bpy.context.scene.render.bake.cage_extrusion = 0.5
bpy.context.scene.render.bake.use_clear = False
bpy.context.scene.render.bake.margin = 2


for a in bpy.data.collections["source"].all_objects:
    
    a.select_set(True)
    a.hide_render = False
    
    for b in bpy.data.collections["target"].all_objects:
        
        b.select_set(True)
        b.hide_render = False
        bpy.context.view_layer.objects.active = b
        bpy.ops.object.bake(type='DIFFUSE')
        #bpy.ops.image.save_all_modified()
        
a.select_set(False)

The code seems correct to me and I'm unable to find the bug/problem.

Any help would be greatly appreciated.

I am attaching the blend file below- https://drive.google.com/file/d/1G6sH6Kl2chIgAfcv1maMy_fQ4v1ZM9Y0/view?usp=sharing

$\endgroup$
3
  • $\begingroup$ I don't know why it crashes but a few things: 1) why don't you just select all targets and one source and do one bake? 2) you select the target "b" and enable it for render but after baking you don't revert this, so it's still selected. 3) the last line should be in the loop, shouldn't it? 4) the cube and the torus are not baked to the plane because they don't touch it. The cone does. 5) Shouldn't you deselect all objects before you bake? $\endgroup$
    – Blunder
    Commented Nov 2, 2022 at 20:30
  • $\begingroup$ Never mind 2) and 3) I've confused a and b. You should name them "source" and "target" ;-) But still about 1) if you move the 2nd loop out of the first then it works. -- I still have no idea why it crashes. If I separate the loops and add another useless loop around the 2nd loop (the bake loop) it still works. $\endgroup$
    – Blunder
    Commented Nov 2, 2022 at 21:01
  • $\begingroup$ It's the a.hide_render that causes the crash. It causes really weird things. imgur.com/a/6gNTGCX -- (1) If it's executed then in the 3rd iteration of the outer loop the "plane" object is all of the sudden in the target collection and in the source collection. WTF? (2) If the line is commented out then the loops work as expected. $\endgroup$
    – Blunder
    Commented Nov 2, 2022 at 21:36

1 Answer 1

0
$\begingroup$

It's a Bug

I guess you found a bug in Blender. If you use a simple script that loops through two collections as you did, and the script toggles the hide_reder property (=the "camera" icon in the outliner) then the object (Plane) from the "target" collection is suddenly in the "source" collection for the last iteration.

If the bug in the collection occurs, Blender tries to bake the object on itself and crashes. Or it crashes while it tries to access that illegal phantom object. Whatever, the result is:

ExceptionCode         : EXCEPTION_ACCESS_VIOLATION
Exception Address     : 0x00007FF699CAB0AA
Exception Module      : blender.exe
...

Stack trace:
blender.exe         :0x00007FF699CAB080  Collection_all_objects_next
blender.exe         :0x00007FF699DCA0C0  pyrna_prop_collection_iter_next
python310.dll       :0x00007FF98CA288F0  PyEval_EvalFrameDefault
python310.dll       :0x00007FF98CA288F0  PyEval_EvalFrameDefault
python310.dll       :0x00007FF98CA28680  PyEval_EvalCode
blender.exe         :0x00007FF699DBDAB0  python_script_exec

The bug only appears one time, when you open the blend file or if you click a "camera" icon.

screenshot

Test script (does not crash Blender):

import bpy
import os

#os.system("cls")

def crashtest():
    for a in bpy.data.collections["source"].all_objects:

        print("Loop 1 - a = " + a.name)
        a.hide_render = True
    
        for b in bpy.data.collections["target"].all_objects:
            print("Loop 2 - b = " + b.name)     

print("-------- START --------")
print("run 1: ")
crashtest()
 
print("run 2: ")
crashtest()            
print("-------- END --------")
    

As a workaround, you can save the names of the objects in the collections in a Python array and get the object in the loop by its name.

def crashtest():
    source_names = [o.name for o in bpy.data.collections["source"].all_objects]
    target_names = [o.name for o in bpy.data.collections["target"].all_objects]
    
    for a_name in source_names:
        a = bpy.data.objects[a_name]
        print("Loop 1 - a = " + a.name)
        a.hide_render = True
    
        for b_name in target_names:
            b = bpy.data.objects[b_name]
            print("Loop 2 - b = " + b.name)    

Bake Only Once

Besides that, I would change the script a bit so that it selects all source objects and bakes them all together to the target object(s). This way you only need one bake per target object and not one for each source object.

import bpy
import os

os.system("cls")

bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 16
bpy.context.scene.render.bake.use_selected_to_active = True
bpy.context.scene.render.bake.cage_extrusion = 0.5
bpy.context.scene.render.bake.use_clear = False
bpy.context.scene.render.bake.margin = 2
bpy.context.scene.render.bake.use_clear = True

# deselect everything
bpy.ops.object.select_all(action='DESELECT')

# workaround to prevent Blender to crash. 
# Save the names of the objects in the collections in arrays and use them for the loops
source_names = [o.name for o in bpy.data.collections["source"].all_objects]
target_names = [o.name for o in bpy.data.collections["target"].all_objects]

# enable all source objects for render & in the viewport, then select them    
for src_name in source_names:
    src = bpy.data.objects[src_name]
    src.hide_render = False
    #src.hide_viewport = False   # "monitor" icon
    src.hide_set(False)          # "eye" icon in the outliner
    src.select_set(True)
    
# bake the selected source objects to the target objects 
for trg_name in target_names:
    trg = bpy.data.objects[trg_name]

    print("Baking " + trg_name)
    
    trg.hide_render = False
    #trg.hide_viewport = False   
    trg.hide_set(False)     
    trg.select_set(True)
    bpy.context.view_layer.objects.active = trg
    bpy.ops.object.bake(type='DIFFUSE')
    #bpy.ops.image.save_all_modified()
    trg.select_set(False)
    
    print("Baked " + trg_name)

(Without the workaround, the script crashes Blender if you have two target objects in the collection. So it's not the nested loops that cause the trouble.)

$\endgroup$

You must log in to answer this question.

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