Python: Create default node tree for new materials and worlds

The motivation is to keep backward compatibility after deprecating
 `material.use_nodes()` and `world.use_nodes`. For example the
following script would behave the same way in 4.5 and 5.0:
```python
mat = bpy.data.materials.new("My new mat")
mat.use_nodes = True
```

Pull Request: https://projects.blender.org/blender/blender/pulls/147052
This commit is contained in:
Habib Gahbiche
2025-10-01 15:05:16 +02:00
parent 3f6ef965b9
commit fb7818e55e
15 changed files with 22 additions and 19 deletions

View File

@@ -207,6 +207,7 @@ def SVGGetMaterial(color, context):
return materials[color] return materials[color]
mat = bpy.data.materials.new(name='SVGMat') mat = bpy.data.materials.new(name='SVGMat')
mat.node_tree.nodes.clear()
node_tree = mat.node_tree node_tree = mat.node_tree
bsdf = node_tree.nodes.new("ShaderNodeBsdfDiffuse") bsdf = node_tree.nodes.new("ShaderNodeBsdfDiffuse")
output = node_tree.nodes.new("ShaderNodeOutputMaterial") output = node_tree.nodes.new("ShaderNodeOutputMaterial")

View File

@@ -424,6 +424,7 @@ def create_cycles_material(self, context, img_spec, name):
material = bpy.data.materials.get((name, None)) material = bpy.data.materials.get((name, None))
if material is None: if material is None:
material = bpy.data.materials.new(name=name) material = bpy.data.materials.new(name=name)
material.node_tree.nodes.clear()
material.surface_render_method = self.render_method material.surface_render_method = self.render_method
material.use_backface_culling = self.use_backface_culling material.use_backface_culling = self.use_backface_culling

View File

@@ -27,6 +27,7 @@ def object_ensure_material(obj, mat_name):
break break
if mat is None: if mat is None:
mat = bpy.data.materials.new(mat_name) mat = bpy.data.materials.new(mat_name)
mat.node_tree.nodes.clear()
if mat_slot: if mat_slot:
mat_slot.material = mat mat_slot.material = mat
else: else:

View File

@@ -54,6 +54,7 @@ class WORLD_OT_convert_volume_to_mesh(bpy.types.Operator):
material = bpy.data.materials.new(name) material = bpy.data.materials.new(name)
mesh.materials.append(material) mesh.materials.append(material)
volume_tree = material.node_tree volume_tree = material.node_tree
volume_tree.nodes.clear()
volume_tree.nodes.new("ShaderNodeOutputMaterial") volume_tree.nodes.new("ShaderNodeOutputMaterial")
volume_output = volume_tree.get_output_node('EEVEE') volume_output = volume_tree.get_output_node('EEVEE')

View File

@@ -127,7 +127,7 @@ class IDSubDataTestData : public WholeIDTestData {
/* Add a material that contains an embedded nodetree and assign a custom property to one of /* Add a material that contains an embedded nodetree and assign a custom property to one of
* its nodes. */ * its nodes. */
this->material = BKE_material_add(this->bmain, "Material"); this->material = BKE_material_add(this->bmain, "Material");
ED_node_shader_default(this->C, &this->material->id); ED_node_shader_default(this->C, this->bmain, &this->material->id);
BKE_object_material_assign( BKE_object_material_assign(
this->bmain, this->object, this->material, this->object->actcol, BKE_MAT_ASSIGN_OBJECT); this->bmain, this->object, this->material, this->object->actcol, BKE_MAT_ASSIGN_OBJECT);

View File

@@ -100,7 +100,7 @@ class MaterialTestData : public TestData {
MaterialTestData() MaterialTestData()
{ {
material = BKE_material_add(this->bmain, "Material"); material = BKE_material_add(this->bmain, "Material");
ED_node_shader_default(this->C, &this->material->id); ED_node_shader_default(this->C, this->bmain, &this->material->id);
this->material_nodetree = this->material->nodetree; this->material_nodetree = this->material->nodetree;
} }
}; };

View File

@@ -78,7 +78,7 @@ bool ED_node_supports_preview(SpaceNode *snode);
* Assumes nothing being done in ntree yet, sets the default in/out node. * Assumes nothing being done in ntree yet, sets the default in/out node.
* Called from shading buttons or header. * Called from shading buttons or header.
*/ */
void ED_node_shader_default(const bContext *C, ID *id); void ED_node_shader_default(const bContext *C, Main *bmain, ID *id);
/** /**
* Initializes an empty compositing node tree with default nodes. * Initializes an empty compositing node tree with default nodes.

View File

@@ -853,7 +853,7 @@ static wmOperatorStatus new_material_exec(bContext *C, wmOperator * /*op*/)
else { else {
ma = BKE_gpencil_material_add(bmain, name); ma = BKE_gpencil_material_add(bmain, name);
} }
ED_node_shader_default(C, &ma->id); ED_node_shader_default(C, bmain, &ma->id);
} }
if (prop) { if (prop) {
@@ -982,7 +982,7 @@ static wmOperatorStatus new_world_exec(bContext *C, wmOperator * /*op*/)
} }
else { else {
wo = BKE_world_add(bmain, CTX_DATA_(BLT_I18NCONTEXT_ID_WORLD, "World")); wo = BKE_world_add(bmain, CTX_DATA_(BLT_I18NCONTEXT_ID_WORLD, "World"));
ED_node_shader_default(C, &wo->id); ED_node_shader_default(C, bmain, &wo->id);
} }
/* hook into UI */ /* hook into UI */

View File

@@ -6747,7 +6747,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
bNodeTree *ntree = ma->nodetree; bNodeTree *ntree = ma->nodetree;
if (!ntree) { if (!ntree) {
ED_node_shader_default(C, &ma->id); ED_node_shader_default(C, bmain, &ma->id);
ntree = ma->nodetree; ntree = ma->nodetree;
} }

View File

@@ -510,10 +510,8 @@ bool ED_node_supports_preview(SpaceNode *snode)
(USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) && ED_node_is_shader(snode)); (USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) && ED_node_is_shader(snode));
} }
void ED_node_shader_default(const bContext *C, ID *id) void ED_node_shader_default(const bContext *C, Main *bmain, ID *id)
{ {
Main *bmain = CTX_data_main(C);
if (GS(id->name) == ID_MA) { if (GS(id->name) == ID_MA) {
/* Materials */ /* Materials */
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);

View File

@@ -76,9 +76,10 @@ static void rna_Light_draw_update(Main * /*bmain*/, Scene * /*scene*/, PointerRN
static void rna_Light_use_nodes_update(bContext *C, PointerRNA *ptr) static void rna_Light_use_nodes_update(bContext *C, PointerRNA *ptr)
{ {
Light *la = (Light *)ptr->data; Light *la = (Light *)ptr->data;
Main *bmain = CTX_data_main(C);
if (la->use_nodes && la->nodetree == nullptr) { if (la->use_nodes && la->nodetree == nullptr) {
ED_node_shader_default(C, &la->id); ED_node_shader_default(C, bmain, &la->id);
} }
rna_Light_update(CTX_data_main(C), CTX_data_scene(C), ptr); rna_Light_update(CTX_data_main(C), CTX_data_scene(C), ptr);

View File

@@ -288,8 +288,7 @@ static Material *rna_Main_materials_new(Main *bmain, const char *name)
Material *material = BKE_material_add(bmain, safe_name); Material *material = BKE_material_add(bmain, safe_name);
id_us_min(&material->id); id_us_min(&material->id);
material->nodetree = blender::bke::node_tree_add_tree_embedded( ED_node_shader_default(nullptr, bmain, &material->id);
bmain, &material->id, "Material Node Tree", "ShaderNodeTree");
WM_main_add_notifier(NC_ID | NA_ADDED, nullptr); WM_main_add_notifier(NC_ID | NA_ADDED, nullptr);
@@ -561,8 +560,7 @@ static World *rna_Main_worlds_new(Main *bmain, const char *name)
World *world = BKE_world_add(bmain, safe_name); World *world = BKE_world_add(bmain, safe_name);
id_us_min(&world->id); id_us_min(&world->id);
world->nodetree = blender::bke::node_tree_add_tree_embedded( ED_node_shader_default(nullptr, bmain, &world->id);
bmain, &world->id, "World Node Tree", "ShaderNodeTree");
WM_main_add_notifier(NC_ID | NA_ADDED, nullptr); WM_main_add_notifier(NC_ID | NA_ADDED, nullptr);

View File

@@ -118,7 +118,7 @@ TEST_F(NodeTest, tree_iterator_1_mat)
TestData context; TestData context;
Material *material = BKE_material_add(context.bmain, "Material"); Material *material = BKE_material_add(context.bmain, "Material");
ED_node_shader_default(context.C, &material->id); ED_node_shader_default(context.C, context.bmain, &material->id);
IteratorResult iter_result = this->get_node_trees(context.bmain); IteratorResult iter_result = this->get_node_trees(context.bmain);
@@ -133,7 +133,7 @@ TEST_F(NodeTest, tree_iterator_scene_no_tree)
TestData context; TestData context;
Material *material = BKE_material_add(context.bmain, "Material"); Material *material = BKE_material_add(context.bmain, "Material");
ED_node_shader_default(context.C, &material->id); ED_node_shader_default(context.C, context.bmain, &material->id);
BKE_scene_add(context.bmain, "Scene"); BKE_scene_add(context.bmain, "Scene");
@@ -151,7 +151,7 @@ TEST_F(NodeTest, tree_iterator_1mat_1scene)
const char SCENE_NAME[MAX_ID_NAME] = "Scene for testing"; const char SCENE_NAME[MAX_ID_NAME] = "Scene for testing";
Material *material = BKE_material_add(context.bmain, "Material"); Material *material = BKE_material_add(context.bmain, "Material");
ED_node_shader_default(context.C, &material->id); ED_node_shader_default(context.C, context.bmain, &material->id);
Scene *scene = BKE_scene_add(context.bmain, SCENE_NAME); Scene *scene = BKE_scene_add(context.bmain, SCENE_NAME);
/* Embedded compositing trees are deprecated, but still relevant for versioning/backward /* Embedded compositing trees are deprecated, but still relevant for versioning/backward
@@ -186,7 +186,7 @@ TEST_F(NodeTest, tree_iterator_1mat_3scenes)
const char MATERIAL_NTREE_NAME[MAX_NAME] = "Shader Nodetree"; const char MATERIAL_NTREE_NAME[MAX_NAME] = "Shader Nodetree";
Material *material = BKE_material_add(context.bmain, "Material"); Material *material = BKE_material_add(context.bmain, "Material");
ED_node_shader_default(context.C, &material->id); ED_node_shader_default(context.C, context.bmain, &material->id);
BKE_scene_add(context.bmain, SCENE_NAME_1); BKE_scene_add(context.bmain, SCENE_NAME_1);
/* Note: no node tree for scene 1. */ /* Note: no node tree for scene 1. */
@@ -227,7 +227,7 @@ TEST_F(NodeTest, tree_iterator_1mat_1scene_2compositing_trees)
const char MATERIAL_NTREE_NAME[MAX_NAME] = "Shader Nodetree"; const char MATERIAL_NTREE_NAME[MAX_NAME] = "Shader Nodetree";
Material *material = BKE_material_add(context.bmain, "Material"); Material *material = BKE_material_add(context.bmain, "Material");
ED_node_shader_default(context.C, &material->id); ED_node_shader_default(context.C, context.bmain, &material->id);
BKE_scene_add(context.bmain, SCENE_NAME_1); BKE_scene_add(context.bmain, SCENE_NAME_1);

View File

@@ -1258,6 +1258,7 @@ class USDExportTest(AbstractUSDTest):
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
material = bpy.data.materials.new(name="test_material") material = bpy.data.materials.new(name="test_material")
node_tree = material.node_tree node_tree = material.node_tree
node_tree.nodes.clear()
bsdf = node_tree.nodes.new("ShaderNodeBsdfPrincipled") bsdf = node_tree.nodes.new("ShaderNodeBsdfPrincipled")
output = node_tree.nodes.new("ShaderNodeOutputMaterial") output = node_tree.nodes.new("ShaderNodeOutputMaterial")
node_tree.links.new(bsdf.outputs["BSDF"], output.inputs["Surface"]) node_tree.links.new(bsdf.outputs["BSDF"], output.inputs["Surface"])

View File

@@ -934,6 +934,7 @@ def render_output(scene, bounds, filepath):
scene.render.filepath = filepath scene.render.filepath = filepath
world = bpy.data.worlds.new(name_gen) world = bpy.data.worlds.new(name_gen)
world.node_tree.nodes.clear()
output = world.node_tree.nodes.new("ShaderNodeOutputWorld") output = world.node_tree.nodes.new("ShaderNodeOutputWorld")
background = world.node_tree.nodes.new("ShaderNodeBackground") background = world.node_tree.nodes.new("ShaderNodeBackground")
world.node_tree.links.new(output.outputs["Surface"], background.outputs["Surface"]) world.node_tree.links.new(output.outputs["Surface"], background.outputs["Surface"])