Naxela / The_Lightmapper

Fast and easy baked GI Lightmaps for Blender and Cycles
GNU General Public License v3.0
686 stars 52 forks source link

GLTF Utilities - error - Cannot add socket to built-in node #185

Open sweco-sekrsv opened 1 month ago

sweco-sekrsv commented 1 month ago

I'm using Blender 3.6.9

I want to export the lightmaps to gltf. I get an error when clicking the "Add GLTF node" button:

Python: Traceback (most recent call last):
  File "C:\Users\sekrsv\AppData\Roaming\Blender Foundation\Blender\3.6\scripts\addons\thelightmapper\addon\operators\tlm.py", line 2056, in execute
    gltf_settings_node.inputs.new('NodeSocketFloat','Occlusion')
RuntimeError: Error: Cannot add socket to built-in node

Any ideas on how to fix that?

sweco-sekrsv commented 1 month ago

For anyone else struggling with this. I dug into the code and did some readup on the topic. This is how I changed the TLM_AddGLTFNode in tlm.py to get it working

class TLM_AddGLTFNode(bpy.types.Operator):
    bl_idname = "tlm.add_gltf_node"
    bl_label = "Add GLTF Node"
    bl_description = "Add to GLTF node to active material and connect lightmap if present"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene
        cycles = scene.cycles

        for obj in bpy.context.scene.objects:

            print("Iterating: " + obj.name)

            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                    for slot in obj.material_slots:

                        material = slot.material

                        nodes = material.node_tree.nodes
                        # create group data
                        gltf_settings = bpy.data.node_groups.get('glTF Material Output')
                        if gltf_settings is None:
                            bpy.data.node_groups.new('glTF Material Output', 'ShaderNodeTree')

                        # add group to node tree
                        gltf_settings_node = nodes.get('glTF Material Output')
                        if gltf_settings_node is None:
                            gltf_settings_node = nodes.new('ShaderNodeGroup')
                            gltf_settings_node.name = 'glTF Material Output'
                            gltf_settings_node.node_tree = bpy.data.node_groups['glTF Material Output']

                        # create group inputs
                        if 'Occlusion' not in gltf_settings_node.node_tree.inputs:
                            gltf_settings_node.node_tree.inputs.new('NodeSocketFloat','Occlusion')                            

                        gltf_settings_node.location.y = 400

                        lightmapNode = nodes.get("TLM_Lightmap")

                        material.node_tree.links.remove(lightmapNode.outputs[0].links[0])
                        material.node_tree.links.new(lightmapNode.outputs[0], gltf_settings_node.inputs[0])

                        #If the material have a node called "Lightmap_Multiplication" and it does have a input into Color2 called "Lightmap_BasecolorNode_A" remove both "Lightmap_BasecolorNode_A" and ""Lightmap_Multiplication". This needs to be done for materials that dont have a base texture but instead using a just a color
                        # Check if the "Lightmap_Multiplication" node exists
                        lightmap_multiplication_node = nodes.get("Lightmap_Multiplication")
                        if lightmap_multiplication_node is not None:
                            # Check if the "Lightmap_BasecolorNode_A" node is connected to the "Color2" input of the "Lightmap_Multiplication" node
                            color2_input = lightmap_multiplication_node.inputs.get("Color2")
                            if color2_input is not None and color2_input.is_linked:
                                lightmap_basecolor_node_a = color2_input.links[0].from_node
                                if lightmap_basecolor_node_a.name == "Lightmap_BasecolorNode_A":
                                    # Remove the "Lightmap_BasecolorNode_A" node
                                    nodes.remove(lightmap_basecolor_node_a)
                                    # Remove the "Lightmap_Multiplication" node
                                    nodes.remove(lightmap_multiplication_node)
                            else:
                                    # Remove the "Lightmap_Multiplication" node
                                    nodes.remove(lightmap_multiplication_node)

        return {'FINISHED'}
jywarren commented 2 weeks ago

Hi, thank you! Here is the diff:

https://gist.github.com/jywarren/b021a934995eeb690352fe9004ae4aa1/revisions

Applied to this file:

https://github.com/Naxela/The_Lightmapper/blob/6e9e3a81d6ced3908b209205e1015860fe999d94/addon/operators/tlm.py#L2019C1-L2069C44

jywarren commented 2 weeks ago

I'm now seeing a different error -

File "/Users/warren/Library/Application Support/Blender/4.1/scripts/addons/thelightmapper/addon/operators/tlm.py", line 1710, in execute if 'Occlusion' not in gltf_settings_node.node_tree.inputs:
^^^^^^^^^^^^^^^^^^^^
AttributeError: 'ShaderNode Tree' object has no attribute 'inputs'
jywarren commented 2 weeks ago

Ah - noting I'm in Blender 4.1. I'm seeing some slightly different-looking naming conventions which could be related:

https://github.com/Naxela/The_Lightmapper/blob/6e9e3a81d6ced3908b209205e1015860fe999d94/addon/utility/utility.py#L270-L300

sweco-sekrsv commented 2 weeks ago

Most likely due to Blender 4.1. I was using the latest Blender 3.6.x release

jywarren commented 2 weeks ago

Just noting via this post:

in Blender 4.0 its inputs and outputs cannot be accessed anymore like this:

input_socket = groupTree.inputs.new('NodeSocketFloat', 'Input') 

instead,

the new python API:

tree.interface.new_socket(name="My Input", in_out='INPUT')
jywarren commented 2 weeks ago

https://wiki.blender.org/wiki/Reference/Release_Notes/4.0/Python_API

I think it would need to be:

if 'Occlusion' not in gltf_settings_node.items_tree:
  gltf_settings_node.node_tree.interface.new_socket(name="Occlusion", in_out='INPUT')

This worked!!