bl_info = { "name": "Import Spectrum from CSV", "blender": (2, 93, 0), "category": "Node", "location": "Shader Editor > Add > Spectral > Import Spectrum from CSV" } import bpy, csv from bpy_extras.io_utils import ImportHelper from bpy.props import BoolProperty, IntProperty, StringProperty from bpy.types import Operator def read_spectrum_csv(context, filepath, ignore_top_rows, wavelength_column, spectrum_column, label_node): if context.area.type == 'NODE_EDITOR': nodes = context.space_data.node_tree.nodes else: nodes = context.active_object.active_material.node_tree.nodes node = nodes.new("ShaderNodeSpectrumCurve") mapping = node.mapping points = mapping.curves[0].points mapping.use_clip = False if label_node: node.label = bpy.path.display_name_from_filepath(filepath) mapping.extend = 'HORIZONTAL' with open(filepath) as csvfile: rdr = csv.reader(csvfile) for i, row in enumerate(rdr): if i < ignore_top_rows: continue wavelength = float(row[wavelength_column].replace(',', '.')) value = float(row[spectrum_column].replace(',', '.')) point_number = i - ignore_top_rows if point_number < len(points): print(f"Set point {point_number} ({wavelength}, {value})") point = points[point_number] point.location[0] = wavelength point.location[1] = value else: print(f"Add point {point_number} ({wavelength}, {value})") point = points.new(wavelength, value) point.handle_type = 'VECTOR' mapping.update() return {'FINISHED'} class ImportSpectrumCSV(Operator, ImportHelper): """Import spectrum from a CSV file as Spectrum Curves node""" bl_idname = "node.import_spectrum_csv" bl_label = "Import Spectrum from CSV" # ImportHelper mixin class uses this filename_ext = ".csv" filter_glob: StringProperty( default="*.csv", options={'HIDDEN'}, maxlen=255, # Max internal buffer length, longer would be clamped. ) ignore_top_rows: IntProperty( name="Ignore Top Rows", description="Number of rows from the top to ignore", default=1, min=0 ) wavelength_column: IntProperty( name="Wavelength Column", description="Number of the column where the wavelength data is", default=1, min=1 ) spectrum_column: IntProperty( name="Spectrum Column", description="Number of the column where the spectrum data is", default=2, min=1 ) label_node: BoolProperty( name="Label Node", description="Change node label to filename", default=True, ) def execute(self, context): return read_spectrum_csv(context, self.filepath, self.ignore_top_rows, self.wavelength_column - 1, self.spectrum_column - 1, self.label_node) def draw_menu(self, context): layout = self.layout layout.separator() layout.operator("node.import_spectrum_csv") def register(): bpy.utils.register_class(ImportSpectrumCSV) bpy.types.NODE_MT_category_SH_NEW_SPECTRAL.append(draw_menu) def unregister(): bpy.utils.unregister_class(ImportSpectrumCSV) bpy.types.NODE_MT_category_SH_NEW_SPECTRAL.remove(draw_menu)