Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion skills/bake-high-to-low/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ This skill is the bake step. It composes `ai-mesh-cleanup` (identity scale, appl

## The core misunderstanding

Baking is not a render. EEVEE has no bake path. The operator writes into the **active Image Texture node** of the **active** object's material, not into a file and not into "the selected image datablock." A missing UV layer, the wrong object active, or a node that is not `nodes.active` finishes without error and leaves a blank or unchanged image.
Baking is not a render. EEVEE has no bake path. The operator writes into an **Image Texture node** in the **active** object's material (the active one, `nodes.active`, when the material has several), not into a file and not into "the selected image datablock." The failure modes are not equally loud. Measured on 4.5.11 LTS and 5.2.1 LTS:

| Setup mistake | What `bpy.ops.object.bake` does |
| --- | --- |
| Target mesh has no UV layer | Raises `RuntimeError: No active UV layer found in the object "Low"` |
| Target material has no Image Texture node | Returns `{'CANCELLED'}`, **raises nothing**, writes nothing |
| The only Image Texture node is not `nodes.active` | Bakes into it anyway (`{'FINISHED'}`) |

So a headless job must check the returned set: `{'CANCELLED'}` is the silent one. With several Image Texture nodes, set `nodes.active` to the one you mean.

The pass type RNA is `type`, not `bake_type`. `bake_type` is not on `bpy.ops.object.bake`. Passing it is a TypeError.

Expand Down Expand Up @@ -104,6 +112,9 @@ result = bpy.ops.object.bake(
use_clear=True,
target="IMAGE_TEXTURES",
)
if result != {'FINISHED'}:
# {'CANCELLED'} raises nothing: e.g. no Image Texture node in the material.
raise RuntimeError(f"bake did not finish: {result}")
```

`cage_extrusion` inflates the active object along its normals so rays hit the high mesh. It must clear the high-frequency amplitude (ribs at 0.10 need extrusion > 0.10; 0.20 is the worked value). If extrusion alone skims past concavities, set `use_cage=True` and `cage_object="CageName"` (the object's `.name`). Do not pass the Object.
Expand Down
7 changes: 6 additions & 1 deletion snippets/bake_normal_high_to_low.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# normal map. Cycles CPU only. High must be selected; low must be active.
# Operator RNA is `type`, not `bake_type`. cage_object is a string name.
# Identifiers match on 4.5 LTS, 5.1, and 5.2 LTS — no version shim.
# A missing UV layer raises, but a material with no Image Texture node
# returns {'CANCELLED'} without raising, so check the result.
#
# Reference:
# https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.bake
Expand All @@ -18,7 +20,7 @@ def bake_normal_high_to_low(high, low, cage_extrusion=0.20, margin=16):
high.select_set(True)
low.select_set(True)
bpy.context.view_layer.objects.active = low
return bpy.ops.object.bake(
result = bpy.ops.object.bake(
type="NORMAL",
use_selected_to_active=True,
cage_extrusion=cage_extrusion,
Expand All @@ -29,3 +31,6 @@ def bake_normal_high_to_low(high, low, cage_extrusion=0.20, margin=16):
use_clear=True,
target="IMAGE_TEXTURES",
)
if result != {"FINISHED"}:
raise RuntimeError(f"bake did not finish: {result}")
return result
Loading