]> git.tdb.fi Git - libs/gl.git/commitdiff
Implement material maps for exporting objects with multiple materials
authorMikko Rasa <tdb@tdb.fi>
Sun, 19 May 2019 23:03:41 +0000 (02:03 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 19 May 2019 23:19:51 +0000 (02:19 +0300)
This replaces the material texture feature which was removed earlier.  The
principle is similar (store material color values in a texture) but the
maps are global, making it possible for multiple objects to use the same
map.

blender/io_mspgl/__init__.py
blender/io_mspgl/export_material.py
blender/io_mspgl/export_object.py
blender/io_mspgl/export_scene.py
blender/io_mspgl/mesh.py
blender/io_mspgl/properties.py
blender/io_mspgl/util.py

index 133b844c2a7d6152f833c58f320f2b54b3c94779..905bfe73b6f52fbeda10ab1c172b652117a68bbf 100644 (file)
@@ -7,7 +7,7 @@ bl_info = {
 
 if "bpy" in locals():
        import imp
-       for sub in "armature", "datafile", "export_armature", "export_material", "export_mesh", "export_object", "export_scene", "export_texture", "mesh", "properties", "util":
+       for sub in "armature", "datafile", "export_armature", "export_material", "export_mesh", "export_object", "export_scene", "export_texture", "material", "mesh", "properties", "util":
                if sub in locals():
                        imp.reload(locals()[sub])
 
index 46b571a9202e3408662b95f79a11debc8d9c8e8d..0860c28bbc2061a0cf1ed0c31b8689175504d7d9 100644 (file)
@@ -1,18 +1,5 @@
 import os
 
-def linear_to_srgb(l):
-       if l<0.0031308:
-               return 12.92*l
-       else:
-               return 1.055*(l**(1/2.4))-0.055
-
-def get_colormap(srgb):
-       if srgb:
-               return linear_to_srgb
-       else:
-               return lambda x: x
-
-
 class MaterialExporter:
        def __init__(self):
                self.single_file = True
@@ -96,7 +83,9 @@ class MaterialExporter:
                mat_res = Resource(material.name+".mat")
                statements = mat_res.statements
 
+               from .util import get_colormap
                cm = get_colormap(material.srgb_colors)
+
                if 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)
@@ -111,3 +100,60 @@ class MaterialExporter:
                statements.append(Statement("shininess", material.specular_hardness))
 
                return mat_res
+
+
+class MaterialMapExporter:
+       def __init__(self):
+               self.single_file = True
+
+       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'
+
+                       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))
+
+                       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")
+
+               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")
+
+                       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)
+
+               return tech_res
index ad76336b3c09d41197c9c9fa73b6a1e8adad4090..cbd009a150080a9f9ec67369a47b9299a69c184b 100644 (file)
@@ -48,6 +48,12 @@ class ObjectExporter:
                material_export.use_textures = self.use_textures
                return material_export
 
+       def create_material_map_exporter(self):
+               from .export_material import MaterialMapExporter
+               material_map_export = MaterialMapExporter()
+               material_map_export.single_file = self.single_file
+               return material_map_export
+
        def export_to_file(self, context, out_fn):
                obj = context.active_object
 
@@ -82,24 +88,41 @@ class ObjectExporter:
                        for s in obj_res.statements:
                                s.write_to_file(out_file)
 
-       def export_object_resources(self, context, obj, resources, progress):
+       def export_object_resources(self, context, obj, resources, progress, material_maps=None):
+               if material_maps is None:
+                       material_maps = {}
+
                lods = self.collect_object_lods(obj)
 
                from .mesh import create_mesh_from_object
+               from .material import create_material_map
                mesh_export = self.create_mesh_exporter()
                material_export = self.create_material_exporter()
+               material_map_export = self.create_material_map_exporter()
 
                for i, l in enumerate(lods):
                        lod_index = l.lod_index if l.lod_for_parent else 0
                        progress.push_task_slice("LOD {}".format(lod_index), i, len(lods))
 
-                       mesh_name = l.data.name+".mesh"
-                       if mesh_name not in resources:
-                               mesh = create_mesh_from_object(context, l, progress)
-                               mesh_res = mesh_export.export_mesh(context, mesh, progress)
-                               resources[mesh_name] = mesh_res
+                       material_map = None
+                       mapped_count = sum(m.material_map for m in l.data.materials)
+                       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:
+                                       raise Exception("Conflicting settings in object materials")
+
+                               if material_map_tech in material_maps:
+                                       material_map = material_maps[material_map_tech]
+                               else:
+                                       material_map = create_material_map(context, l.data.materials[0])
+                                       material_maps[material_map_tech] = material_map
 
-                       if l.material_slots and l.material_slots[0].material:
+                               tech_name = "material_map_{}.tech".format(os.path.splitext(material_map_tech)[0])
+                               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:
@@ -108,6 +131,12 @@ class ObjectExporter:
                        elif "stub.tech" not in resources:
                                resources["stub.tech"] = self.export_stub_technique()
 
+                       mesh_name = l.data.name+".mesh"
+                       if mesh_name not in resources:
+                               mesh = create_mesh_from_object(context, l, progress, material_map=material_map)
+                               mesh_res = mesh_export.export_mesh(context, mesh, progress)
+                               resources[mesh_name] = mesh_res
+
                        progress.pop_task()
 
        def export_object(self, context, obj, progress, *, resources=None):
@@ -142,12 +171,15 @@ class ObjectExporter:
                        if l.material_slots:
                                material = l.material_slots[0].material
                        if material:
-                               tech_res = resources[material.name+".tech"]
+                               if material.material_map:
+                                       tech_res = resources["material_map_{}.tech".format(material.technique)]
+                               else:
+                                       tech_res = resources[material.name+".tech"]
                        else:
                                tech_res = resources["stub.tech"]
 
                        if tech_res.name!=prev_tech:
-                               if material and material.technique and not material.inherit_tech:
+                               if material and not material.material_map and material.technique and not material.inherit_tech:
                                        lod_st.append(Statement("technique", material.technique))
                                elif not self.single_file:
                                        lod_st.append(obj_res.create_reference_statement("technique", tech_res))
index b32683ccd5780f265c2ec18a124452ed72c38aac..a0e4118118967e41a60ef50654c248beab6fb0a8 100644 (file)
@@ -79,9 +79,11 @@ class SceneExporter:
                object_export = ObjectExporter()
                object_export.single_file = False
 
+               material_maps = {}
+
                for i, o in enumerate(objs):
                        progress.push_task_slice(o.name, i, len(objs))
-                       object_export.export_object_resources(context, o, resources, progress)
+                       object_export.export_object_resources(context, o, resources, progress, material_maps=material_maps)
                        obj_name = o.name+".object"
                        resources[obj_name] = object_export.export_object(context, o, progress, resources=resources)
                        progress.pop_task()
index 95749f46d4eb67aede93b53a87bdb4be1f789487..125bc5bbbaf01a938af18ec21eefb76239f1534b 100644 (file)
@@ -393,6 +393,30 @@ class Mesh:
                                for g in v.groups:
                                        g.group = group_index_map[g.group]
 
+       def apply_material_map(self, material_map):
+               for m in self.materials:
+                       if m not in material_map.materials:
+                               raise Exception("Material map is not compatible with Mesh")
+
+               if self.use_uv=='NONE':
+                       return
+
+               layer = UvLayer("material_map")
+               if self.use_uv=='UNIT0':
+                       self.uv_layers = [layer]
+                       layer.unit = 0
+               else:
+                       self.uv_layers.append(layer)
+                       used_units = [u.unit for u in self.uv_layers]
+                       layer.unit = next(i for i in itertools.count() if i not in used_units)
+                       self.uv_layers.sort(key=lambda u: u.unit)
+
+               layer.uvs = [(0.0, 0.0)]*len(self.loops)
+               for f in self.faces:
+                       uv = material_map.get_material_uv(self.materials[f.material_index])
+                       for i in f.loop_indices:
+                               layer.uvs[i] = uv
+
        def prepare_uv(self, progress):
                # Form a list of UV layers referenced by materials with the array atlas
                # property set
@@ -740,7 +764,7 @@ class Mesh:
                self._mesh = None
 
 
-def create_mesh_from_object(context, obj, progress):
+def create_mesh_from_object(context, obj, progress, *, material_map=None):
        if obj.type!="MESH":
                raise Exception("Object is not a mesh")
 
@@ -781,6 +805,9 @@ def create_mesh_from_object(context, obj, progress):
 
        mesh.name = obj.data.name
 
+       if material_map:
+               mesh.apply_material_map(material_map)
+
        progress.set_task("Triangulating", 0.2, 0.3)
        mesh.prepare_triangles(progress)
        progress.set_task("Smoothing", 0.3, 0.5)
index 91f3c56bb2f6725116e54f220037f2916cf9633b..032bf8ed5dbced582ecda1f4262da88080f94008 100644 (file)
@@ -72,6 +72,7 @@ class MspGLMaterialProperties(bpy.types.Panel):
                self.layout.prop(mat, "array_atlas")
                if mat.array_atlas:
                        self.layout.prop(mat, "array_layer")
+               self.layout.prop(mat, "material_map")
 
 class MspGLTextureProperties(bpy.types.Panel):
        bl_idname = "TEXTURE_PT_mspgl_properties"
@@ -118,5 +119,6 @@ def register_properties():
        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.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")
index 7e932df382791007e3151beff6d4cc41bf5301ad..70b005f6df60e84db38ea8c35c03fc0bbcc79123 100644 (file)
@@ -52,6 +52,18 @@ class Progress:
                        self.last = value
 
 
+def linear_to_srgb(l):
+       if l<0.0031308:
+               return 12.92*l
+       else:
+               return 1.055*(l**(1/2.4))-0.055
+
+def get_colormap(srgb):
+       if srgb:
+               return linear_to_srgb
+       else:
+               return lambda x: x
+
 def image_name(img):
        fp = img.filepath
        if fp: