]> git.tdb.fi Git - libs/gl.git/commitdiff
Rework Blender material and texture export
authorMikko Rasa <tdb@tdb.fi>
Tue, 23 Jun 2020 15:14:59 +0000 (18:14 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 23 Jun 2020 22:24:33 +0000 (01:24 +0300)
blender/io_mspgl/export_material.py
blender/io_mspgl/export_object.py
blender/io_mspgl/export_texture.py
blender/io_mspgl/material.py
blender/io_mspgl/properties.py

index 356186df17c5b2341e525322cc69f3607ce4513f..f51a4a49a88e47c7fbf96055c381be576c7fbb5c 100644 (file)
@@ -1,5 +1,24 @@
 import os
 
+def create_technique_resource(material, resources, single_file):
+       from .datafile import Resource, Statement
+       tech_res = Resource(material.name+".tech")
+
+       mat_res = resources[material.name+".mat"]
+
+       st = Statement("pass", "")
+       if single_file:
+               st.sub.append(tech_res.create_embed_statement("material", mat_res))
+       else:
+               st.sub.append(tech_res.create_reference_statement("material", mat_res))
+
+       if material.render_mode=='CUSTOM':
+               st.sub.append(Statement("shader", material.shader))
+
+       tech_res.statements.append(st)
+
+       return tech_res
+
 class MaterialExporter:
        def __init__(self):
                self.single_file = True
@@ -14,97 +33,53 @@ class MaterialExporter:
        def export_technique_resources(self, material, resources):
                texture_export = self.create_texture_exporter()
 
-               mat_name = material.name+".mat"
-               if mat_name not in resources:
-                       resources[mat_name] = self.export_material(material)
+               from .material import Material
+               material = Material(material)
 
-               if False and self.use_textures:
-                       for s in material.texture_slots:
-                               if s and s.texture.type=='IMAGE' and s.texture.image:
-                                       tex_name = s.texture.name+".tex2d"
+               if self.use_textures:
+                       for p in material.properties:
+                               if p.texture:
+                                       tex_name = p.texture.image.name+".tex2d"
                                        if tex_name not in resources:
-                                               resources[tex_name] = texture_export.export_texture(s.texture)
+                                               resources[tex_name] = texture_export.export_texture(p.texture, p.tex_usage)
+
+               mat_name = material.name+".mat"
+               if mat_name not in resources:
+                       resources[mat_name] = self.export_material(material, resources=resources)
 
        def export_technique(self, material, *, resources):
-               from .datafile import Resource, Statement
-               tech_res = Resource(material.name+".tech")
-
-               mat_res = resources[material.name+".mat"]
-               textures = {}
-               if False and self.use_textures:
-                       image_texture_slots = [s for s in material.texture_slots if s and s.texture.type=='IMAGE' and s.texture.image]
-                       for s in image_texture_slots:
-                               if s.use_map_color_diffuse:
-                                       textures["diffuse_map"] = s.texture
-                               elif s.use_map_normal:
-                                       if s.texture.use_normal_map:
-                                               textures["normal_map"] = s.texture
-                                       else:
-                                               textures["displace_map"] = s.texture
-
-               if material.technique:
-                       if not material.inherit_tech:
-                               return tech_res
+               return create_technique_resource(material, resources, self.single_file)
 
-                       if self.single_file:
-                               raise Exception("Can't export inherited technique to a single file")
-
-                       st = Statement("inherit", material.technique)
-                       for s, t in textures.items():
-                               fn = os.path.basename(t.image.filepath)
-                               if t.default_filter and fn:
-                                       st.sub.append(Statement("texture", s, fn))
-                               else:
-                                       st.sub.append(tech_res.create_reference_statement("texture", s, resources[t.name+".tex2d"]))
-                       if material.override_material:
-                               st.sub.append(tech_res.create_reference_statement("material", "surface", mat_res))
-                       tech_res.statements.append(st)
-               else:
-                       st = Statement("pass", "")
-                       if self.single_file:
-                               st.sub.append(tech_res.create_embed_statement("material", mat_res))
-                       else:
-                               st.sub.append(tech_res.create_reference_statement("material", mat_res))
-
-                       if "diffuse_map" in textures:
-                               diffuse_tex = textures["diffuse_map"]
-                               tex_res = resources[diffuse_tex.name+".tex2d"]
-                               ss = Statement("texunit", 0)
-                               fn = os.path.basename(diffuse_tex.image.filepath)
-                               if self.single_file:
-                                       ss.sub.append(tech_res.create_embed_statement("texture2d", tex_res))
-                               elif diffuse_tex.default_filter and fn:
-                                       ss.sub.append(Statement("texture", fn))
-                               else:
-                                       ss.sub.append(tech_res.create_reference_statement("texture", tex_res))
-                               st.sub.append(ss)
-
-                       tech_res.statements.append(st)
-
-               return tech_res
-
-       def export_material(self, material):
+       def export_material(self, material, *, resources):
                from .datafile import Resource, Statement
                mat_res = Resource(material.name+".mat")
-               statements = mat_res.statements
 
-               from .util import get_colormap
-               cm = get_colormap(material.srgb_colors)
-
-               if False and any(s.use_map_color_diffuse for s in material.texture_slots if s):
-                       statements.append(Statement("diffuse", 1.0, 1.0, 1.0, 1.0))
-                       amb = cm(material.ambient)
-                       statements.append(Statement("ambient", amb, amb, amb, 1.0))
-               else:
-                       diff = material.diffuse_color
-                       statements.append(Statement("diffuse", cm(diff[0]), cm(diff[1]), cm(diff[2]), 1.0))
-                       statements.append(Statement("ambient", cm(diff[0]), cm(diff[1]), cm(diff[2]), 1.0))
-               spec = material.specular_color*material.specular_intensity
-               statements.append(Statement("specular", cm(spec.r), cm(spec.g), cm(spec.g), 1.0))
-               statements.append(Statement("shininess", min(2/material.roughness**2-2, 250)))
+               st = Statement("pbr")
+               st.sub.append(self.create_property_statement(mat_res, material.base_color, "base_color", resources))
+               st.sub.append(self.create_property_statement(mat_res, material.metalness, "metalness", resources))
+               st.sub.append(self.create_property_statement(mat_res, material.roughness, "roughness", resources))
+               st.sub.append(self.create_property_statement(mat_res, material.normal, "normal", resources, tex_only=True))
+               st.sub.append(self.create_property_statement(mat_res, material.emission, "emission", resources))
+               mat_res.statements.append(st)
 
                return mat_res
 
+       def create_property_statement(self, mat_res, prop, keyword, resources, *, tex_only=False):
+               from .datafile import Statement
+               if self.use_textures and prop.texture:
+                       tex_res = resources[prop.texture.image.name+".tex2d"]
+                       fn = os.path.basename(prop.texture.image.filepath)
+                       if self.single_file:
+                               raise Exception("Can't export textures to a single file")
+                       elif prop.texture.default_filter and fn:
+                               return Statement(keyword+"_map", fn)
+                       else:
+                               return mat_res.create_reference_statement(keyword+"_map", tex_res)
+               elif type(prop.value)==tuple:
+                       return Statement(keyword, *prop.value)
+               else:
+                       return Statement(keyword, prop.value)
+
 
 class MaterialMapExporter:
        def __init__(self):
@@ -112,52 +87,25 @@ class MaterialMapExporter:
 
        def export_technique_resources(self, material_map, resources):
                from .datafile import Resource, Statement, Token
-               diffuse_name = material_map.name+"_diffuse.tex2d"
-               if diffuse_name not in resources:
-                       diffuse_res = Resource(diffuse_name)
-
-                       fmt = 'SRGB_ALPHA' if material_map.srgb_colors else 'RGBA'
+               base_color_name = material_map.name+"_base_color.tex2d"
+               if base_color_name not in resources:
+                       base_color_res = Resource(base_color_name)
 
-                       diffuse_res.statements.append(Statement("min_filter", Token('NEAREST')))
-                       diffuse_res.statements.append(Statement("mag_filter", Token('NEAREST')))
-                       diffuse_res.statements.append(Statement("storage", Token(fmt), *material_map.size))
-                       diffuse_res.statements.append(Statement("raw_data", material_map.diffuse_data))
+                       base_color_res.statements.append(Statement("min_filter", Token('NEAREST')))
+                       base_color_res.statements.append(Statement("mag_filter", Token('NEAREST')))
+                       base_color_res.statements.append(Statement("storage", Token('SRGB_ALPHA'), *material_map.size))
+                       base_color_res.statements.append(Statement("raw_data", material_map.base_color_data))
 
-                       resources[diffuse_name] = diffuse_res
-
-               if "basic_white.mat" not in resources:
-                       mat_res = Resource("basic_white.mat")
-                       mat_res.statements.append(Statement("diffuse", 1.0, 1.0, 1.0, 1.0))
-
-                       resources["basic_white.mat"] = mat_res
-
-       def export_technique(self, material_map, *, resources=None):
-               from .datafile import Resource, Statement
-               tech_res = Resource(material_map.name+".tech")
+                       resources[base_color_name] = base_color_res
 
-               mat_res = resources["basic_white.mat"]
-               diffuse_res = resources[material_map.name+"_diffuse.tex2d"]
-
-               if material_map.technique:
-                       if self.single_file:
-                               raise Exception("Can't export inherited technique to a single file")
+               mat_name = material_map.name+".mat"
+               if mat_name not in resources:
+                       mat_res = Resource(mat_name)
+                       st = Statement("pbr")
+                       st.sub.append(mat_res.create_reference_statement("base_color_map", base_color_res))
+                       mat_res.statements.append(st)
 
-                       st = Statement("inherit", material_map.technique)
-                       st.sub.append(tech_res.create_reference_statement("texture", "diffuse_map", diffuse_res))
-                       st.sub.append(tech_res.create_reference_statement("material", "surface", mat_res))
-                       tech_res.statements.append(st)
-               else:
-                       st = Statement("pass", "")
-                       if self.single_file:
-                               st.sub.append(tech_res.create_embed_statement("material", mat_res))
-                       else:
-                               st.sub.append(tech_res.create_reference_statement("material", mat_res))
-                       ss = Statement("texunit", 0)
-                       if self.single_file:
-                               ss.sub.append(tech_res.create_embed_statement("texture2d", diffuse_res))
-                       else:
-                               ss.sub.append(tech_res.create_reference_statement("texture", diffuse_res))
-                       st.sub.append(ss)
-                       tech_res.statements.append(st)
+                       resources[mat_name] = mat_res
 
-               return tech_res
+       def export_technique(self, material_map, *, resources):
+               return create_technique_resource(material_map, resources, self.single_file)
index 6e399b0199f9f77b10175fe5a4a86027d29a88cc..5b81ace45e6cce1a01416e3acb49f4e51d331a78 100644 (file)
@@ -105,29 +105,31 @@ class ObjectExporter:
                        progress.push_task_slice("LOD {}".format(lod_index), i, len(lods))
 
                        material_map = None
-                       mapped_count = sum(m.material_map for m in l.data.materials if m)
+                       mapped_count = sum(m.render_mode!='EXTERNAL' and m.material_map for m in l.data.materials if m)
                        if mapped_count:
-                               material_map_tech = l.data.materials[0].technique
-                               tech_mismatch = any(m.technique!=material_map_tech for m in l.data.materials)
-                               if mapped_count!=len(l.data.materials) or tech_mismatch:
+                               mmk = lambda m: m.shader if m.render_mode=='CUSTOM' else ""
+                               material_map_key = mmk(l.data.materials[0])
+                               key_mismatch = any(mmk(m)!=material_map_key for m in l.data.materials)
+                               if mapped_count!=len(l.data.materials) or key_mismatch:
                                        raise Exception("Conflicting settings in object materials")
 
-                               if material_map_tech in material_maps:
-                                       material_map = material_maps[material_map_tech]
+                               if material_map_key in material_maps:
+                                       material_map = material_maps[material_map_key]
                                else:
                                        material_map = create_material_map(context, l.data.materials[0])
-                                       material_maps[material_map_tech] = material_map
+                                       material_maps[material_map_key] = material_map
 
-                               tech_name = "material_map_{}.tech".format(os.path.splitext(material_map_tech)[0])
+                               tech_name = "{}.tech".format(material_map.name)
                                if tech_name not in resources:
                                        material_map_export.export_technique_resources(material_map, resources)
                                        resources[tech_name] = material_map_export.export_technique(material_map, resources=resources)
                        elif l.material_slots and l.material_slots[0].material:
                                material = l.material_slots[0].material
-                               tech_name = material.name+".tech"
-                               if tech_name not in resources:
-                                       material_export.export_technique_resources(material, resources)
-                                       resources[tech_name] = material_export.export_technique(material, resources=resources)
+                               if material.render_mode!='EXTERNAL':
+                                       tech_name = material.name+".tech"
+                                       if tech_name not in resources:
+                                               material_export.export_technique_resources(material, resources)
+                                               resources[tech_name] = material_export.export_technique(material, resources=resources)
                        elif "stub.tech" not in resources:
                                resources["stub.tech"] = self.export_stub_technique()
 
@@ -179,7 +181,7 @@ class ObjectExporter:
                                tech_res = resources["stub.tech"]
 
                        if tech_res.name!=prev_tech:
-                               if material and not material.material_map and material.technique and not material.inherit_tech:
+                               if material and material.render_mode=='EXTERNAL':
                                        lod_st.append(Statement("technique", material.technique))
                                elif not self.single_file:
                                        lod_st.append(obj_res.create_reference_statement("technique", tech_res))
@@ -201,5 +203,9 @@ class ObjectExporter:
        def export_stub_technique(self):
                from .datafile import Resource, Statement
                tech_res = Resource("stub.tech")
-               tech_res.statements.append(Statement("pass", ""))
+               pass_st = Statement("pass", "")
+               tech_res.statements.append(pass_st)
+               mat_st = Statement("material")
+               pass_st.sub.append(mat_st)
+               mat_st.sub.append(Statement("basic"))
                return tech_res
index dd8191ce9f108652b2924ee1a77fbd0197d0f94c..3b2201302ee1aaa0854e2a77468f496b171e260d 100644 (file)
@@ -4,40 +4,53 @@ class TextureExporter:
        def __init__(self):
                self.inline_data = True
 
-       def export_texture(self, texture):
+       def export_texture(self, tex_node, usage='RGB'):
+               image = tex_node.image
                from .datafile import Resource, Statement, Token
-               tex_res = Resource(texture.name+".tex2d")
+               tex_res = Resource(image.name+".tex2d")
 
-               if texture.use_interpolation:
-                       if texture.use_mipmap:
+               use_interpolation = tex_node.interpolation!='Closest'
+               if use_interpolation:
+                       if tex_node.use_mipmap:
                                tex_res.statements.append(Statement("filter", Token('LINEAR_MIPMAP_LINEAR')))
                                tex_res.statements.append(Statement("generate_mipmap", True))
                        else:
                                tex_res.statements.append(Statement("filter", Token('LINEAR')))
-                       tex_res.statements.append(Statement("max_anisotropy", texture.filter_eccentricity))
+                       tex_res.statements.append(Statement("max_anisotropy", tex_node.max_anisotropy))
                else:
-                       if texture.use_mipmap:
+                       if tex_node.use_mipmap:
                                tex_res.statements.append(Statement("filter", Token('NEAREST_MIPMAP_NEAREST')))
                                tex_res.statements.append(Statement("generate_mipmap", True))
                        else:
                                tex_res.statements.append(Statement("filter", Token('NEAREST')))
 
-               fn = os.path.basename(texture.image.filepath)
+               colorspace = image.colorspace_settings.name
+               if usage=='RGBA':
+                       fmt = 'SRGB_ALPHA' if colorspace=='sRGB' else 'RGBA'
+               elif usage=='GRAY':
+                       if colorspace=='sRGB':
+                               raise Exception("Grayscale textures with sRGB colorspace are not supported")
+                       fmt = 'LUMINANCE'
+               else:
+                       fmt = 'SRGB' if colorspace=='sRGB' else 'RGB'
+
+               tex_res.statements.append(Statement("storage", Token(fmt), image.size[0], image.size[1]))
+
+               fn = os.path.basename(image.filepath)
                if not self.inline_data and fn:
                        tex_res.statements.append(Statement("external_image", fn))
                else:
                        texdata = ""
-                       colorspace = texture.image.colorspace_settings.name
-                       if texture.use_alpha:
-                               fmt = 'SRGB_ALPHA' if colorspace=='sRGB' else 'RGBA'
-                               for p in texture.image.pixels:
+                       if usage=='RGBA':
+                               for p in image.pixels:
                                        texdata += "\\x{:02X}".format(int(p*255))
+                       elif usage=='GRAY':
+                               for i in range(0, len(image.pixels), 4):
+                                       texdata += "\\x{:02X}".format(image.pixels[i])
                        else:
-                               fmt = 'SRGB' if colorspace=='sRGB' else 'RGB'
-                               for i in range(0, len(texture.image.pixels), 4):
+                               for i in range(0, len(image.pixels), 4):
                                        for j in range(3):
-                                               texdata += "\\x{:02X}".format(int(texture.image.pixels[i+j]*255))
-                       tex_res.statements.append(Statement("storage", Token(fmt), texture.image.size[0], texture.image.size[1]))
+                                               texdata += "\\x{:02X}".format(int(image.pixels[i+j]*255))
                        tex_res.statements.append(Statement("raw_data", texdata))
 
                return tex_res
index 470ffc5784e1d4fd0b28d006dfe720863a172257..b275caa37f54a92a2a459df8ca9d66d7d12d90d0 100644 (file)
 import os
 
+def get_linked_node_and_socket(node_tree, socket):
+       for l in node_tree.links:
+               if socket==l.to_socket:
+                       return (l.from_node, l.from_socket)
+               elif socket==l.from_socket:
+                       return (l.to_node, l.to_socket)
+       return (None, None)
+
+class MaterialProperty:
+       def __init__(self, value):
+               self.value = value
+               self.texture = None
+               self.tex_usage = None
+
+       def set_from_input(self, node_tree, input_socket, alpha_socket=None):
+               if type(self.value)==tuple:
+                       if alpha_socket:
+                               self.value = input_socket.default_value[:len(self.value)-1]+(alpha_socket.default_value,)
+                       else:
+                               self.value = input_socket.default_value[:len(self.value)]
+               else:
+                       self.value = input_socket.default_value
+
+               from_node, _ = get_linked_node_and_socket(node_tree, input_socket)
+               alpha_from = None
+               if from_node:
+                       if from_node.type=='NORMAL_MAP':
+                               from_node, _ = get_linked_node_and_socket(node_tree, from_node.inputs["Color"])
+
+                       if alpha_socket:
+                               alpha_from, _ = get_linked_node_and_socket(node_tree, alpha_socket)
+                               if alpha_from and alpha_from!=from_node:
+                                       raise Exception("Separate textures for color and alpha are not supported")
+
+                       if from_node.type=='TEX_IMAGE':
+                               self.texture = from_node
+                               if alpha_from:
+                                       self.tex_usage = 'RGBA'
+                               elif type(self.value)==tuple:
+                                       self.tex_usage = 'RGB'
+                               else:
+                                       self.tex_usage = 'GRAY'
+                       else:
+                               raise Exception("Unsupported property input node type "+from_node.type)
+
+class Material:
+       def __init__(self, material):
+               self.name = material.name
+
+               self.base_color = MaterialProperty((0.8, 0.8, 0.8, 1.0))
+               self.metalness = MaterialProperty(0.0)
+               self.roughness = MaterialProperty(0.5)
+               self.normal = MaterialProperty((0.0, 0.0, 0.1))
+               self.emission = MaterialProperty((0.0, 0.0, 0.0))
+
+               self.render_mode = material.render_mode
+               self.technique = material.technique
+               self.shader = material.shader
+
+               if self.render_mode=='EXTERNAL' and not self.technique:
+                       raise Exception("Missing technique with external rendering mode")
+               elif self.render_mode=='CUSTOM' and not self.shader:
+                       raise Exception("Missing shader with custom rendering mode")
+
+               out_node = None
+               for n in material.node_tree.nodes:
+                       if n.type=='OUTPUT_MATERIAL':
+                               out_node = n
+                               break
+
+               if not out_node:
+                       raise Exception("No material output node found")
+
+               surface_node, _ = get_linked_node_and_socket(material.node_tree, out_node.inputs["Surface"])
+               if not surface_node:
+                       raise Exception("Material has no surface node")
+               elif surface_node.type!='BSDF_PRINCIPLED':
+                       raise Exception("Unsupported surface node type "+surface_node.type)
+
+               self.base_color.set_from_input(material.node_tree, surface_node.inputs["Base Color"], surface_node.inputs["Alpha"])
+               self.metalness.set_from_input(material.node_tree, surface_node.inputs["Metallic"])
+               self.roughness.set_from_input(material.node_tree, surface_node.inputs["Roughness"])
+               self.normal.set_from_input(material.node_tree, surface_node.inputs["Normal"])
+               self.emission.set_from_input(material.node_tree, surface_node.inputs["Emission"])
+
+               self.properties = (self.base_color, self.metalness, self.roughness, self.normal, self.emission)
+
+
 class MaterialMap:
        def __init__(self, materials):
-               self.technique = materials[0].technique
-               if self.technique:
-                       self.name = "material_map_"+os.path.splitext(self.technique)[0]
+               self.render_mode = materials[0].render_mode
+               if self.render_mode=='EXTERNAL':
+                       raise Exception("Material map with external render mode does not make sense")
+
+               self.shader = materials[0].shader
+               if self.shader:
+                       self.name = "material_map_"+os.path.splitext(self.shader)[0]
                else:
                        self.name = "material_map"
                self.materials = materials
                self.material_names = [m.name for m in self.materials]
-               self.srgb_colors = materials[0].srgb_colors
                for m in self.materials:
-                       if m.technique!=self.technique:
-                               raise Exception("Conflicting techniques in MaterialMap constructor")
-                       if m.srgb_colors!=self.srgb_colors:
-                               raise Exception("Conflicting colorspace settings in MaterialMap constructor")
+                       if m.render_mode!=self.render_mode:
+                               raise Exception("Conflicting render modes in MaterialMap constructor")
+                       if self.render_mode=='CUSTOM' and m.shader!=self.shader:
+                               raise Exception("Conflicting shaders in MaterialMap constructor")
 
                count = len(self.materials)
                size = 1
@@ -27,12 +118,14 @@ class MaterialMap:
 
                from .util import get_colormap
 
-               cm = get_colormap(self.srgb_colors)
-               self.diffuse_data = ""
-               for m in self.materials:
-                       diff = [int(cm(c)*255) for c in m.diffuse_color]
-                       self.diffuse_data += "\\x{:02X}\\x{:02X}\\x{:02X}\\xFF".format(*diff)
-               self.diffuse_data += "\\x00\\x00\\x00\\x00"*(self.size[0]*self.size[1]-count)
+               cm = get_colormap(True)
+               self.base_color_data = ""
+               for m in map(Material, self.materials):
+                       if any(p.texture for p in m.properties):
+                               raise Exception("Texturing is incompatible with material map")
+                       base_color = [int(cm(c)*255) for c in m.base_color.value]
+                       self.base_color_data += "\\x{:02X}\\x{:02X}\\x{:02X}\\xFF".format(*base_color)
+               self.base_color_data += "\\x00\\x00\\x00\\x00"*(self.size[0]*self.size[1]-count)
 
        def get_material_uv(self, material):
                index = self.material_names.index(material.name)
index ce4e692f189883d9e137dfcc576bba268eb584d3..0605ba0c2b0fcab5e46dd6b2673275ce2243d7a1 100644 (file)
@@ -64,36 +64,40 @@ class MspGLMaterialProperties(bpy.types.Panel):
                if not mat:
                        return
 
-               self.layout.prop(mat, "technique")
-               self.layout.prop(mat, "inherit_tech")
-               if mat.inherit_tech:
-                       self.layout.prop(mat, "override_material")
-               self.layout.prop(mat, "srgb_colors")
+               self.layout_prop(mat, "render_mode")
+               if mat.render_mode=='CUSTOM':
+                       self.layout_prop(mat, "shader")
+               elif mat.render_mode=='EXTERNAL':
+                       self.layout_prop(mat, "technique")
                self.layout.prop(mat, "array_atlas")
                if mat.array_atlas:
                        self.layout.prop(mat, "array_layer")
-               self.layout.prop(mat, "material_map")
+               if mat.render_mode!='EXTERNAL':
+                       self.layout.prop(mat, "material_map")
 
-class MspGLTextureProperties(bpy.types.Panel):
-       bl_idname = "TEXTURE_PT_mspgl_properties"
+class MspGLTextureNodeProperties(bpy.types.Panel):
+       bl_idname = "NODE_PT_mspgl_properties"
        bl_label = "MspGL properties"
-       bl_space_type = "PROPERTIES"
-       bl_region_type = "WINDOW"
-       bl_context = "texture"
+       bl_space_type = "NODE_EDITOR"
+       bl_region_type = "UI"
+       bl_category = "Item"
 
        @classmethod
        def poll(cls, context):
-               mat = context.active_object.active_material
-               return mat is not None and mat.active_texture is not None
+               node = context.active_node
+               return node and node.type=='TEX_IMAGE'
 
        def draw(self, context):
-               tex = context.active_object.active_material.active_texture
-               if not tex:
+               node = context.active_node
+               if not node:
                        return
 
-               self.layout.prop(tex, "default_filter")
+               self.layout.prop(node, "default_filter")
+               if not node.default_filter:
+                       self.layout.prop(node, "use_mipmap")
+                       self.layout.prop(node, "max_anisotropy")
 
-classes = [MspGLMeshProperties, MspGLObjectProperties, MspGLMaterialProperties, MspGLTextureProperties]
+classes = [MspGLMeshProperties, MspGLObjectProperties, MspGLMaterialProperties, MspGLTextureNodeProperties]
 
 def register_properties():
        bpy.types.Mesh.winding_test = bpy.props.BoolProperty(name="Winding test", description="Perform winding test to skip back faces")
@@ -115,15 +119,19 @@ def register_properties():
        bpy.types.Object.lod_for_parent = bpy.props.BoolProperty(name="LoD for parent", description="This object is a level of detail for its parent")
        bpy.types.Object.lod_index = bpy.props.IntProperty(name="LoD index", description="Index of the level of detail", min=1, max=16, default=1)
 
-       bpy.types.Material.technique = bpy.props.StringProperty(name="Technique", description="Name of an external technique to use for rendering")
-       bpy.types.Material.inherit_tech = bpy.props.BoolProperty(name="Inherit technique", description="Inherit from the technique to customize textures")
-       bpy.types.Material.override_material = bpy.props.BoolProperty(name="Override material", description="Override material in the inherited technique as well", default=True)
-       bpy.types.Material.srgb_colors = bpy.props.BoolProperty(name="sRGB colors", description="Export material colors as sRGB instead of linear", default=True)
+       bpy.types.Material.render_mode = bpy.props.EnumProperty(name="Render mode", description="How this material should be rendered", default="BUILTIN",
+               items=(("BUILTIN", "Built-in", "Use built-in shaders"),
+                       ("CUSTOM", "Custom shader", "Use a custom shader"),
+                       ("EXTERNAL", "External technique", "Use an externally defined technique")))
+       bpy.types.Material.technique = bpy.props.StringProperty(name="Custom technique", description="Name of an external technique to use for rendering")
+       bpy.types.Material.shader = bpy.props.StringProperty(name="Custom shader", description="Name of an external technique to use for rendering")
        bpy.types.Material.array_atlas = bpy.props.BoolProperty(name="Texture array atlas", description="The material is stored in a texture array")
        bpy.types.Material.array_layer = bpy.props.IntProperty("Texture array layer", description="Layer of the texture array atlas to use")
        bpy.types.Material.material_map = bpy.props.BoolProperty(name="Material map", description="Make this material part of a material map")
 
-       bpy.types.Texture.default_filter = bpy.props.BoolProperty(name="Default filter", description="Let the loading program determine filtering options")
+       bpy.types.ShaderNodeTexImage.default_filter = bpy.props.BoolProperty(name="Default filter", description="Let the loading program determine filtering options")
+       bpy.types.ShaderNodeTexImage.use_mipmap = bpy.props.BoolProperty(name="Use mipmaps", description="Use mipmaps (automatically generated) for the texture", default=True)
+       bpy.types.ShaderNodeTexImage.max_anisotropy = bpy.props.FloatProperty(name="Maximum anisotropy", description="Maximum anisotropy to use in texture filtering", min=1, max=16, default=1)
 
        for c in classes:
                bpy.utils.register_class(c)