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])
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
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)
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
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
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:
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):
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))
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()
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
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")
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)
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"
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")
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: