Skip to content
Snippets Groups Projects
shading_nodes.py 63.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Maurice Raybaud's avatar
    Maurice Raybaud committed
        bl_label = 'Value'
        bl_icon = 'SHADING_SOLID'
    
        def init(self, context):
    
            self.outputs.new('PovraySocketUniversal', "Value")
    
        def draw_label(self):
            return "Value"
    
    
    class PovrayModifierNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayModifierNode'
        bl_label = 'Modifier'
        bl_icon = 'NODE_TEXTURE'
    
        def init(self, context):
    
            turb_x = self.inputs.new('PovraySocketFloat_0_10', "Turb X")
            turb_x.default_value = 0.1
            turb_y = self.inputs.new('PovraySocketFloat_0_10', "Turb Y")
            turb_y.default_value = 0.1
            turb_z = self.inputs.new('PovraySocketFloat_0_10', "Turb Z")
            turb_z.default_value = 0.1
            octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves")
            octaves.default_value = 1
            lambat = self.inputs.new('PovraySocketFloat_0_10', "Lambda")
            lambat.default_value = 2.0
            omega = self.inputs.new('PovraySocketFloat_0_10', "Omega")
            omega.default_value = 0.5
            freq = self.inputs.new('PovraySocketFloat_0_10', "Frequency")
            freq.default_value = 2.0
            self.inputs.new('PovraySocketFloat_0_10', "Phase")
    
            self.outputs.new('NodeSocketVector', "Modifier")
    
        def draw_label(self):
            return "Modifier"
    
    
    class PovrayPigmentNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayPigmentNode'
        bl_label = 'Color'
        bl_icon = 'SHADING_SOLID'
    
        def init(self, context):
    
            color = self.inputs.new('PovraySocketColor', "Color")
            color.default_value = (1, 1, 1)
            pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter")
            transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit")
            self.outputs.new('NodeSocketColor', "Pigment")
    
        def draw_label(self):
            return "Color"
    
    
    class PovrayColorImageNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayColorImageNode'
        bl_label = 'Image map'
    
        map_type: bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=(
                ('uv_mapping', "UV", ""),
                ('0', "Planar", "Default planar mapping"),
                ('1', "Spherical", "Spherical mapping"),
                ('2', "Cylindrical", "Cylindrical mapping"),
                ('5', "Torroidal", "Torus or donut shaped mapping"),
            ),
            default='0',
        )
        image: StringProperty(maxlen=1024)  # , subtype="FILE_PATH"
        interpolate: EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2',
        )
        premultiplied: BoolProperty(default=False)
        once: BoolProperty(description="Not to repeat", default=False)
    
        def init(self, context):
    
            gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
            gamma.default_value = 2.0
            transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit")
            pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter")
            mapping = self.inputs.new('NodeSocketVector', "Mapping")
            mapping.hide_value = True
            transform = self.inputs.new('NodeSocketVector', "Transform")
            transform.hide_value = True
            modifier = self.inputs.new('NodeSocketVector', "Modifier")
            modifier.hide_value = True
    
            self.outputs.new('NodeSocketColor', "Pigment")
    
        def draw_buttons(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            row = column.row()
            row.prop(self, "premultiplied", text="Premul")
            row.prop(self, "once", text="Once")
    
        def draw_buttons_ext(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            row = column.row()
            row.prop(self, "premultiplied", text="Premul")
            row.prop(self, "once", text="Once")
    
        def draw_label(self):
            return "Image map"
    
    
    class PovrayBumpMapNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayBumpMapNode'
        bl_label = 'Bump map'
        bl_icon = 'TEXTURE'
    
        map_type: bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=(
                ('uv_mapping', "UV", ""),
                ('0', "Planar", "Default planar mapping"),
                ('1', "Spherical", "Spherical mapping"),
                ('2', "Cylindrical", "Cylindrical mapping"),
                ('5', "Torroidal", "Torus or donut shaped mapping"),
            ),
            default='0',
        )
        image: StringProperty(maxlen=1024)  # , subtype="FILE_PATH"
        interpolate: EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2',
        )
        once: BoolProperty(description="Not to repeat", default=False)
    
        def init(self, context):
    
            self.inputs.new('PovraySocketFloat_0_10', "Normal")
            mapping = self.inputs.new('NodeSocketVector', "Mapping")
            mapping.hide_value = True
            transform = self.inputs.new('NodeSocketVector', "Transform")
            transform.hide_value = True
            modifier = self.inputs.new('NodeSocketVector', "Modifier")
            modifier.hide_value = True
    
            normal = self.outputs.new('NodeSocketFloat', "Normal")
            normal.hide_value = True
    
        def draw_buttons(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            column.prop(self, "once", text="Once")
    
        def draw_buttons_ext(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            column.prop(self, "once", text="Once")
    
        def draw_label(self):
            return "Bump Map"
    
    
    class PovrayImagePatternNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayImagePatternNode'
        bl_label = 'Image pattern'
        bl_icon = 'NODE_TEXTURE'
    
        map_type: bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=(
                ('uv_mapping', "UV", ""),
                ('0', "Planar", "Default planar mapping"),
                ('1', "Spherical", "Spherical mapping"),
                ('2', "Cylindrical", "Cylindrical mapping"),
                ('5', "Torroidal", "Torus or donut shaped mapping"),
            ),
            default='0',
        )
        image: StringProperty(maxlen=1024)  # , subtype="FILE_PATH"
        interpolate: EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2',
        )
        premultiplied: BoolProperty(default=False)
        once: BoolProperty(description="Not to repeat", default=False)
        use_alpha: BoolProperty(default=True)
    
        def init(self, context):
    
            gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
            gamma.default_value = 2.0
    
            self.outputs.new('PovraySocketPattern', "Pattern")
    
        def draw_buttons(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            row = column.row()
            row.prop(self, "premultiplied", text="Premul")
            row.prop(self, "once", text="Once")
            column.prop(self, "use_alpha", text="Use alpha")
    
        def draw_buttons_ext(self, context, layout):
    
            column = layout.column()
            im = None
            for image in bpy.data.images:
                if image.name == self.image:
                    im = image
            split = column.split(factor=0.8, align=True)
            split.prop_search(self, "image", context.blend_data, "images", text="")
            split.operator("pov.imageopen", text="", icon="FILEBROWSER")
            if im is not None:
                column.prop(im, "source", text="")
            column.prop(self, "map_type", text="")
            column.prop(self, "interpolate", text="")
            row = column.row()
            row.prop(self, "premultiplied", text="Premul")
            row.prop(self, "once", text="Once")
    
        def draw_label(self):
            return "Image pattern"
    
    
    class ShaderPatternNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'ShaderPatternNode'
        bl_label = 'Other patterns'
    
        pattern: EnumProperty(
            name="Pattern",
            description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling",
            items=(
                ('agate', "Agate", ""),
                ('crackle', "Crackle", ""),
                ('gradient', "Gradient", ""),
                ('pavement', "Pavement", ""),
                ('spiral1', "Spiral 1", ""),
                ('spiral2', "Spiral 2", ""),
                ('tiling', "Tiling", ""),
            ),
            default='agate',
        )
    
        agate_turb: FloatProperty(
            name="Agate turb", description="Agate turbulence", min=0.0, max=100.0, default=0.5
        )
    
        crackle_form_x: FloatProperty(
            name="X", description="Form vector X", min=-150.0, max=150.0, default=-1
        )
    
        crackle_form_y: FloatProperty(
            name="Y", description="Form vector Y", min=-150.0, max=150.0, default=1
        )
    
        crackle_form_z: FloatProperty(
            name="Z", description="Form vector Z", min=-150.0, max=150.0, default=0
        )
    
        crackle_metric: FloatProperty(
            name="Metric", description="Crackle metric", min=0.0, max=150.0, default=1
        )
    
        crackle_solid: BoolProperty(name="Solid", description="Crackle solid", default=False)
    
        spiral_arms: FloatProperty(name="Number", description="", min=0.0, max=256.0, default=2.0)
    
        tiling_number: IntProperty(name="Number", description="", min=1, max=27, default=1)
    
        gradient_orient: EnumProperty(
            name="Orient",
            description="",
            items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")),
            default='x',
        )
    
        def init(self, context):
    
            pat = self.outputs.new('PovraySocketPattern', "Pattern")
    
        def draw_buttons(self, context, layout):
    
            layout.prop(self, "pattern", text="")
            if self.pattern == 'agate':
                layout.prop(self, "agate_turb")
            if self.pattern == 'crackle':
                layout.prop(self, "crackle_metric")
                layout.prop(self, "crackle_solid")
                layout.label(text="Form:")
                layout.prop(self, "crackle_form_x")
                layout.prop(self, "crackle_form_y")
                layout.prop(self, "crackle_form_z")
            if self.pattern in {"spiral1", "spiral2"}:
                layout.prop(self, "spiral_arms")
            if self.pattern in {'tiling'}:
                layout.prop(self, "tiling_number")
            if self.pattern in {'gradient'}:
                layout.prop(self, "gradient_orient")
    
        def draw_buttons_ext(self, context, layout):
            pass
    
        def draw_label(self):
            return "Other patterns"
    
    
    class ShaderTextureMapNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'ShaderTextureMapNode'
        bl_label = 'Texture map'
    
        brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
    
        brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
    
        brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
    
        brick_mortar: FloatProperty(
            name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
        )
    
        def init(self, context):
            mat = bpy.context.object.active_material
            self.inputs.new('PovraySocketPattern', "")
            color = self.inputs.new('NodeSocketColor', "Color ramp")
            color.hide_value = True
            for i in range(0, 4):
                transform = self.inputs.new('PovraySocketTransform', "Transform")
                transform.hide_value = True
            number = mat.pov.inputs_number
            for i in range(number):
                self.inputs.new('PovraySocketTexture', "%s" % i)
    
            self.outputs.new('PovraySocketTexture', "Texture")
    
        def draw_buttons(self, context, layout):
    
            if self.inputs[0].default_value == 'brick':
                layout.prop(self, "brick_mortar")
                layout.label(text="Brick size:")
                layout.prop(self, "brick_size_x")
                layout.prop(self, "brick_size_y")
                layout.prop(self, "brick_size_z")
    
        def draw_buttons_ext(self, context, layout):
    
            if self.inputs[0].default_value == 'brick':
                layout.prop(self, "brick_mortar")
                layout.label(text="Brick size:")
                layout.prop(self, "brick_size_x")
                layout.prop(self, "brick_size_y")
                layout.prop(self, "brick_size_z")
    
        def draw_label(self):
            return "Texture map"
    
    
    class ShaderNormalMapNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'ShaderNormalMapNode'
        bl_label = 'Normal map'
    
        brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
    
        brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
    
        brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
    
        brick_mortar: FloatProperty(
            name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
        )
    
        def init(self, context):
            self.inputs.new('PovraySocketPattern', "")
            normal = self.inputs.new('PovraySocketFloat_10', "Normal")
            slope = self.inputs.new('PovraySocketMap', "Slope map")
            for i in range(0, 4):
                transform = self.inputs.new('PovraySocketTransform', "Transform")
                transform.hide_value = True
            self.outputs.new('PovraySocketNormal', "Normal")
    
        def draw_buttons(self, context, layout):
            # for i, inp in enumerate(self.inputs):
    
            if self.inputs[0].default_value == 'brick':
                layout.prop(self, "brick_mortar")
                layout.label(text="Brick size:")
                layout.prop(self, "brick_size_x")
                layout.prop(self, "brick_size_y")
                layout.prop(self, "brick_size_z")
    
        def draw_buttons_ext(self, context, layout):
    
            if self.inputs[0].default_value == 'brick':
                layout.prop(self, "brick_mortar")
                layout.label(text="Brick size:")
                layout.prop(self, "brick_size_x")
                layout.prop(self, "brick_size_y")
                layout.prop(self, "brick_size_z")
    
        def draw_label(self):
            return "Normal map"
    
    
    class ShaderNormalMapEntryNode(Node, ObjectNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'ShaderNormalMapEntryNode'
        bl_label = 'Normal map entry'
    
        def init(self, context):
            self.inputs.new('PovraySocketFloat_0_1', "Stop")
            self.inputs.new('PovraySocketFloat_0_1', "Gray")
    
        def draw_label(self):
            return "Normal map entry"
    
    
    class IsoPropsNode(Node, CompositorNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'IsoPropsNode'
        bl_label = 'Iso'
        node_label: StringProperty(maxlen=1024)
    
        def init(self, context):
            ob = bpy.context.object
            self.node_label = ob.name
            text_name = ob.pov.function_text
            if text_name:
                text = bpy.data.texts[text_name]
                for line in text.lines:
                    split = line.body.split()
                    if split[0] == "#declare":
                        socket = self.inputs.new('NodeSocketFloat', "%s" % split[1])
                        value = split[3].split(";")
                        value = value[0]
                        socket.default_value = float(value)
    
        def draw_label(self):
            return self.node_label
    
    
    class PovrayFogNode(Node, CompositorNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovrayFogNode'
        bl_label = 'Fog'
    
        def init(self, context):
            color = self.inputs.new('NodeSocketColor', "Color")
            color.default_value = (0.7, 0.7, 0.7, 0.25)
            self.inputs.new('PovraySocketFloat_0_1', "Filter")
            distance = self.inputs.new('NodeSocketInt', "Distance")
            distance.default_value = 150
            self.inputs.new('NodeSocketBool', "Ground")
            fog_offset = self.inputs.new('NodeSocketFloat', "Offset")
            fog_alt = self.inputs.new('NodeSocketFloat', "Altitude")
            turb = self.inputs.new('NodeSocketVector', "Turbulence")
            turb_depth = self.inputs.new('PovraySocketFloat_0_10', "Depth")
            turb_depth.default_value = 0.5
            octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves")
            octaves.default_value = 5
            lambdat = self.inputs.new('PovraySocketFloat_0_10', "Lambda")
            lambdat.default_value = 1.25
            omega = self.inputs.new('PovraySocketFloat_0_10', "Omega")
            omega.default_value = 0.35
            translate = self.inputs.new('NodeSocketVector', "Translate")
            rotate = self.inputs.new('NodeSocketVector', "Rotate")
            scale = self.inputs.new('NodeSocketVector', "Scale")
            scale.default_value = (1, 1, 1)
    
        def draw_label(self):
            return "Fog"
    
    
    class PovraySlopeNode(Node, TextureNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'PovraySlopeNode'
        bl_label = 'Slope Map'
    
        def init(self, context):
            self.use_custom_color = True
            self.color = (0, 0.2, 0)
            slope = self.inputs.new('PovraySocketSlope', "0")
            slope = self.inputs.new('PovraySocketSlope', "1")
            slopemap = self.outputs.new('PovraySocketMap', "Slope map")
            output.hide_value = True
    
        def draw_buttons(self, context, layout):
    
            layout.operator("pov.nodeinputadd")
            row = layout.row()
            row.label(text='Value')
            row.label(text='Height')
            row.label(text='Slope')
    
        def draw_buttons_ext(self, context, layout):
    
            layout.operator("pov.nodeinputadd")
            row = layout.row()
            row.label(text='Value')
            row.label(text='Height')
            row.label(text='Slope')
    
        def draw_label(self):
            return "Slope Map"
    
    
    
    # -------- Texture nodes # ---------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    class TextureOutputNode(Node, TextureNodeTree):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = 'TextureOutputNode'
        bl_label = 'Color Map'
    
        def init(self, context):
            tex = bpy.context.object.active_material.active_texture
            num_sockets = int(tex.pov.density_lines / 32)
            for i in range(num_sockets):
                color = self.inputs.new('NodeSocketColor', "%s" % i)
                color.hide_value = True
    
        def draw_buttons(self, context, layout):
    
            layout.label(text="Color Ramps:")
    
        def draw_label(self):
            return "Color Map"
    
    
    
    # ------------------------------------------------------------------------------ #
    # --------------------------------- Operators ---------------------------------- #
    # ------------------------------------------------------------------------------ #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    class NODE_OT_iso_add(Operator):
        bl_idname = "pov.nodeisoadd"
        bl_label = "Create iso props"
    
        def execute(self, context):
            ob = bpy.context.object
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                bpy.context.scene.use_nodes = True
            tree = bpy.context.scene.node_tree
            for node in tree.nodes:
                if node.bl_idname == "IsoPropsNode" and node.label == ob.name:
                    tree.nodes.remove(node)
            isonode = tree.nodes.new('IsoPropsNode')
            isonode.location = (0, 0)
            isonode.label = ob.name
            return {'FINISHED'}
    
    
    class NODE_OT_map_create(Operator):
        bl_idname = "node.map_create"
        bl_label = "Create map"
    
        def execute(self, context):
            x = y = 0
            space = context.space_data
            tree = space.edit_tree
            for node in tree.nodes:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    x, y = node.location
                node.select = False
            tmap = tree.nodes.new('ShaderTextureMapNode')
            tmap.location = (x - 200, y)
            return {'FINISHED'}
    
        def invoke(self, context, event):
            wm = context.window_manager
            return wm.invoke_props_dialog(self)
    
        def draw(self, context):
            layout = self.layout
            mat = context.object.active_material
            layout.prop(mat.pov, "inputs_number")
    
    
    class NODE_OT_povray_node_texture_map_add(Operator):
        bl_idname = "pov.nodetexmapadd"
        bl_label = "Texture map"
    
        def execute(self, context):
            tree = bpy.context.object.active_material.node_tree
            tmap = tree.nodes.active
            bpy.context.object.active_material.node_tree.nodes.active = tmap
            el = tmap.color_ramp.elements.new(0.5)
            for el in tmap.color_ramp.elements:
                el.color = (0, 0, 0, 1)
            for inp in tmap.inputs:
                tmap.inputs.remove(inp)
            for outp in tmap.outputs:
                tmap.outputs.remove(outp)
            pattern = tmap.inputs.new('NodeSocketVector', "Pattern")
            pattern.hide_value = True
            for i in range(0, 3):
                tmap.inputs.new('NodeSocketColor', "Shader")
            tmap.outputs.new('NodeSocketShader', "BSDF")
            tmap.label = "Texture Map"
            return {'FINISHED'}
    
    
    class NODE_OT_povray_node_output_add(Operator):
        bl_idname = "pov.nodeoutputadd"
        bl_label = "Output"
    
        def execute(self, context):
            tree = bpy.context.object.active_material.node_tree
            tmap = tree.nodes.new('ShaderNodeOutputMaterial')
            bpy.context.object.active_material.node_tree.nodes.active = tmap
            for inp in tmap.inputs:
                tmap.inputs.remove(inp)
            tmap.inputs.new('NodeSocketShader', "Surface")
            tmap.label = "Output"
            return {'FINISHED'}
    
    
    class NODE_OT_povray_node_layered_add(Operator):
        bl_idname = "pov.nodelayeredadd"
        bl_label = "Layered material"
    
        def execute(self, context):
            tree = bpy.context.object.active_material.node_tree
            tmap = tree.nodes.new('ShaderNodeAddShader')
            bpy.context.object.active_material.node_tree.nodes.active = tmap
            tmap.label = "Layered material"
            return {'FINISHED'}
    
    
    class NODE_OT_povray_input_add(Operator):
        bl_idname = "pov.nodeinputadd"
        bl_label = "Add entry"
    
        def execute(self, context):
            node = bpy.context.object.active_material.node_tree.nodes.active
            if node.type in {'VALTORGB'}:
                number = 1
                for inp in node.inputs:
                    if inp.type == 'SHADER':
                        number += 1
                node.inputs.new('NodeSocketShader', "%s" % number)
                els = node.color_ramp.elements
                pos1 = els[len(els) - 1].position
                pos2 = els[len(els) - 2].position
                pos = (pos1 - pos2) / 2 + pos2
                el = els.new(pos)
    
            if node.bl_idname == 'PovraySlopeNode':
                number = len(node.inputs)
                node.inputs.new('PovraySocketSlope', "%s" % number)
    
            return {'FINISHED'}
    
    
    class NODE_OT_povray_input_remove(Operator):
        bl_idname = "pov.nodeinputremove"
        bl_label = "Remove input"
    
        def execute(self, context):
            node = bpy.context.object.active_material.node_tree.nodes.active
            if node.type in {'VALTORGB', 'ADD_SHADER'}:
                number = len(node.inputs) - 1
                if number > 5:
                    inp = node.inputs[number]
                    node.inputs.remove(inp)
                    if node.type in {'VALTORGB'}:
                        els = node.color_ramp.elements
                        number = len(els) - 2
                        el = els[number]
                        els.remove(el)
            return {'FINISHED'}
    
    
    class NODE_OT_povray_image_open(Operator):
        bl_idname = "pov.imageopen"
        bl_label = "Open"
    
        filepath: StringProperty(
            name="File Path", description="Open image", maxlen=1024, subtype='FILE_PATH'
        )
    
        def invoke(self, context, event):
            context.window_manager.fileselect_add(self)
            return {'RUNNING_MODAL'}
    
        def execute(self, context):
            im = bpy.data.images.load(self.filepath)
            node = context.object.active_material.node_tree.nodes.active
            node.image = im.name
            return {'FINISHED'}
    
    
    # class TEXTURE_OT_povray_open_image(Operator):
    # bl_idname = "pov.openimage"
    # bl_label = "Open Image"
    
    # filepath = StringProperty(
    # name="File Path",
    # description="Open image",
    # maxlen=1024,
    # subtype='FILE_PATH',
    # )
    
    # def invoke(self, context, event):
    # context.window_manager.fileselect_add(self)
    # return {'RUNNING_MODAL'}
    
    # def execute(self, context):
    # im=bpy.data.images.load(self.filepath)
    # tex = context.texture
    # tex.pov.image = im.name
    # view_layer = context.view_layer
    # view_layer.update()
    # return {'FINISHED'}
    
    
    class PovrayPatternNode(Operator):
        bl_idname = "pov.patternnode"
        bl_label = "Pattern"
    
        add = True
    
        def execute(self, context):
            space = context.space_data
            tree = space.edit_tree
            for node in tree.nodes:
                node.select = False
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                tmap = tree.nodes.new('ShaderNodeValToRGB')
                tmap.label = "Pattern"
                for inp in tmap.inputs:
                    tmap.inputs.remove(inp)
                for outp in tmap.outputs:
                    tmap.outputs.remove(outp)
                pattern = tmap.inputs.new('PovraySocketPattern', "Pattern")
                pattern.hide_value = True
                mapping = tmap.inputs.new('NodeSocketVector', "Mapping")
                mapping.hide_value = True
                transform = tmap.inputs.new('NodeSocketVector', "Transform")
                transform.hide_value = True
                modifier = tmap.inputs.new('NodeSocketVector', "Modifier")
                modifier.hide_value = True
                for i in range(0, 2):
                    tmap.inputs.new('NodeSocketShader', "%s" % (i + 1))
                tmap.outputs.new('NodeSocketShader', "Material")
                tmap.outputs.new('NodeSocketColor', "Color")
                tree.nodes.active = tmap
                self.add = False
            aNode = tree.nodes.active
            aNode.select = True
            v2d = context.region.view2d
            x, y = v2d.region_to_view(self.x, self.y)
            aNode.location = (x, y)
    
        def modal(self, context, event):
            if event.type == 'MOUSEMOVE':
                self.x = event.mouse_region_x
                self.y = event.mouse_region_y
                self.execute(context)
                return {'RUNNING_MODAL'}
            elif event.type == 'LEFTMOUSE':
                return {'FINISHED'}
            elif event.type in ('RIGHTMOUSE', 'ESC'):
                return {'CANCELLED'}
    
            return {'RUNNING_MODAL'}
    
        def invoke(self, context, event):
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
    
    
    class UpdatePreviewMaterial(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = "node.updatepreview"
        bl_label = "Update preview"
    
        def execute(self, context):
            scene = context.view_layer
            ob = context.object
            for obj in scene.objects:
                if obj != ob:
                    scene.objects.active = ob
                    break
            scene.objects.active = ob
    
        def modal(self, context, event):
            if event.type == 'RIGHTMOUSE':
                self.execute(context)
                return {'FINISHED'}
            return {'PASS_THROUGH'}
    
        def invoke(self, context, event):
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
    
    
    class UpdatePreviewKey(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        bl_idname = "wm.updatepreviewkey"
        bl_label = "Activate RMB"
    
        @classmethod
        def poll(cls, context):
            conf = context.window_manager.keyconfigs.active
            mapstr = "Node Editor"
            map = conf.keymaps[mapstr]
            try:
                map.keymap_items["node.updatepreview"]
                return False
            except BaseException as e:
                print(e.__doc__)
                print('An exception occurred: {}'.format(e))
                return True
    
        def execute(self, context):
            conf = context.window_manager.keyconfigs.active
            mapstr = "Node Editor"
            map = conf.keymaps[mapstr]
            map.keymap_items.new("node.updatepreview", type='RIGHTMOUSE', value="PRESS")
            return {'FINISHED'}
    
    
    classes = (
        PovraySocketUniversal,
        PovraySocketFloat_0_1,
        PovraySocketFloat_0_10,
        PovraySocketFloat_10,
        PovraySocketFloatPositive,
        PovraySocketFloat_000001_10,
        PovraySocketFloatUnlimited,
        PovraySocketInt_1_9,
        PovraySocketInt_0_256,
        PovraySocketPattern,
        PovraySocketColor,
        PovraySocketColorRGBFT,
        PovraySocketTexture,
        PovraySocketTransform,
        PovraySocketNormal,
        PovraySocketSlope,
        PovraySocketMap,
        # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
        # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
        # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
        NODE_MT_POV_map_create,
        ObjectNodeTree,
        PovrayOutputNode,
        PovrayTextureNode,
        PovrayFinishNode,
        PovrayDiffuseNode,
        PovrayPhongNode,
        PovraySpecularNode,
        PovrayMirrorNode,
        PovrayAmbientNode,
        PovrayIridescenceNode,
        PovraySubsurfaceNode,
        PovrayMappingNode,
        PovrayMultiplyNode,
        PovrayTransformNode,
        PovrayValueNode,
        PovrayModifierNode,
        PovrayPigmentNode,
        PovrayColorImageNode,
        PovrayBumpMapNode,
        PovrayImagePatternNode,
        ShaderPatternNode,
        ShaderTextureMapNode,
        ShaderNormalMapNode,
        ShaderNormalMapEntryNode,
        IsoPropsNode,
        PovrayFogNode,
        PovraySlopeNode,
        TextureOutputNode,
        NODE_OT_iso_add,
        NODE_OT_map_create,
        NODE_OT_povray_node_texture_map_add,
        NODE_OT_povray_node_output_add,
        NODE_OT_povray_node_layered_add,
        NODE_OT_povray_input_add,
        NODE_OT_povray_input_remove,
        NODE_OT_povray_image_open,
        PovrayPatternNode,
        UpdatePreviewMaterial,
        UpdatePreviewKey,
    )
    
    
    def register():
    
        bpy.types.NODE_HT_header.append(menu_func_nodes)
        nodeitems_utils.register_node_categories("POVRAYNODES", node_categories)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        for cls in classes:
            register_class(cls)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    def unregister():
        for cls in reversed(classes):
            unregister_class(cls)
    
        nodeitems_utils.unregister_node_categories("POVRAYNODES")
        bpy.types.NODE_HT_header.remove(menu_func_nodes)