objects = context.context.selected_objects
resources = {}
- material_atlases = {}
task = ctx.task("Exporting resources", 1.0)
- dummy_res = self.export_resources(task, objects, resources, material_atlases)
+ dummy_res = self.export_resources(task, objects, resources)
path, base = os.path.split(out_fn)
base, ext = os.path.splitext(base)
for r in refs:
r.write_to_file(os.path.join(path, r.name))
- def export_resources(self, ctx, objects, resources, material_atlases):
- if material_atlases is None:
- material_atlases = {}
-
+ def export_resources(self, ctx, objects, resources):
object_exporter = None
camera_exporter = None
armature_exporter = None
if not object_exporter:
from .export_object import ObjectExporter
object_exporter = ObjectExporter()
- object_exporter.export_object_resources(task, obj, resources, material_atlases)
+ object_exporter.export_object_resources(task, obj, resources)
res = object_exporter.export_object(obj, resources)
elif obj.type=='CAMERA':
res_name = obj.name+".camera"
task = ctx.task("Exporting resources", 1.0)
resources = {}
- dummy_res = data_exporter.export_resources(task, all_objects, resources, None)
+ dummy_res = data_exporter.export_resources(task, all_objects, resources)
task = ctx.task("Exporting scenes", 1.0)
for s in ordered_scenes:
shader_res.statements.append(st)
return shader_res
-
-
-class MaterialAtlasExporter:
- def __init__(self):
- pass
-
- def export_technique_resources(self, material_atlas, resources):
- from .datafile import Resource, Statement, Token
- base_color_name = material_atlas.name+"_base_color.tex"
- base_color_res = resources.get(base_color_name)
- if not base_color_res:
- base_color_res = Resource(base_color_name, "texture")
-
- base_color_res.statements.append(Statement("type", Token("\\2d")))
- base_color_res.statements.append(Statement("storage", Token('SRGB_ALPHA'), *material_atlas.size))
- base_color_res.statements.append(Statement("raw_data", material_atlas.base_color_data))
-
- resources[base_color_name] = base_color_res
-
- sampler_name = "nearest.samp"
- sampler_res = resources.get(sampler_name)
- if not sampler_res:
- sampler_res = Resource(sampler_name, "sampler")
-
- sampler_res.statements.append(Statement("filter", Token('NEAREST')))
-
- resources[sampler_name] = sampler_res
-
- mat_name = material_atlas.name+".mat"
- if mat_name not in resources:
- mat_res = Resource(mat_name, "material")
- mat_res.statements.append(Statement("type", Token('pbr')))
- mat_res.statements.append(mat_res.create_reference_statement("base_color_map", base_color_res))
- mat_res.statements.append(mat_res.create_reference_statement("sampler", sampler_res))
-
- resources[mat_name] = mat_res
-
- def export_technique(self, material_atlas, resources):
- return create_technique_resource(material_atlas, resources)
return lods
- def export_object_resources(self, ctx, obj, resources, material_atlases):
- if material_atlases is None:
- material_atlases = {}
-
+ def export_object_resources(self, ctx, obj, resources):
lods = self.collect_object_lods(obj)
from .export_mesh import MeshExporter
- from .export_material import MaterialAtlasExporter, MaterialExporter
+ from .export_material import MaterialExporter
from .mesh import create_mesh_from_object
- from .material import Material, create_material_atlas
+ from .material import Material
mesh_export = MeshExporter()
material_export = MaterialExporter()
- material_atlas_export = MaterialAtlasExporter()
ctx.set_slices(len(lods))
for l in lods:
lod_index = l.lod_index if l.lod_for_parent else 0
task = ctx.next_slice("LOD {}".format(lod_index))
- material_atlas = None
- atlas_flags = [m.render_mode!='EXTERNAL' and m.material_atlas for m in l.data.materials if m]
- if any(atlas_flags):
- mmk = lambda m: m.shader if m.render_mode=='CUSTOM' else ""
- material_atlas_key = mmk(l.data.materials[0])
- key_mismatch = any(mmk(m)!=material_atlas_key for m in l.data.materials)
- if not all(atlas_flags) or key_mismatch:
- raise Exception("Invalid configuration on object {}: Mixed material atlas state")
-
- if material_atlas_key in material_atlases:
- material_atlas = material_atlases[material_atlas_key]
- else:
- material_atlas = create_material_atlas(task.context, l.data.materials[0])
- material_atlases[material_atlas_key] = material_atlas
-
- tech_name = "{}.tech".format(material_atlas.name)
- if tech_name not in resources:
- material_atlas_export.export_technique_resources(material_atlas, resources)
- resources[tech_name] = material_atlas_export.export_technique(material_atlas, resources)
- elif l.material_slots and l.material_slots[0].material:
+ if l.material_slots and l.material_slots[0].material:
material = l.material_slots[0].material
subtask = task.task(material, 0.1)
if material.render_mode!='EXTERNAL':
mesh_name = l.data.name+".mesh"
if mesh_name not in resources:
subtask = task.task(l.data, 1.0)
- mesh = create_mesh_from_object(subtask, l, material_atlas)
+ mesh = create_mesh_from_object(subtask, l)
mesh_res = mesh_export.export_mesh(subtask, mesh)
resources[mesh_name] = mesh_res
if material:
if material.render_mode=='EXTERNAL':
tech_name = material.technique
- elif material.material_atlas:
- tech_name = "material_atlas_{}.tech".format(os.path.splitext(material.technique)[0])
else:
tech_name = material.name+".tech"
else:
from .export import DataExporter
data_exporter = DataExporter()
- data_exporter.export_resources(ctx, scene.prototypes, resources, None)
+ data_exporter.export_resources(ctx, scene.prototypes, resources)
def export_scene(self, scene, resources):
from .datafile import Resource, Statement, Token
prop = MaterialProperty(*args)
self.properties.append(prop)
return prop
-
-
-class MaterialAtlas:
- def __init__(self, materials):
- self.render_mode = materials[0].render_mode
- if self.render_mode=='EXTERNAL':
- raise Exception("Material atlas with external render mode does not make sense")
-
- if self.render_mode=='CUSTOM':
- self.render_methods = materials[0].render_methods
- else:
- self.render_methods = None
- if self.render_methods:
- self.name = "material_atlas_"+os.path.splitext(self.render_methods[0].shader)[0]
- else:
- self.name = "material_atlas"
- self.receive_shadows = materials[0].receive_shadows
- self.cast_shadows = (materials[0].shadow_method!='NONE')
- self.materials = materials
- self.material_names = [m.name for m in self.materials]
- self.uniforms = None
- method_hash = compute_render_method_hash(self)
- for m in self.materials:
- if m.render_mode!=self.render_mode:
- raise Exception("Conflicting render modes in MaterialAtlas constructor")
- if self.render_mode=='CUSTOM' and compute_render_method_hash(m)!=method_hash:
- raise Exception("Conflicting shaders in MaterialAtlas constructor")
- if m.receive_shadows!=self.receive_shadows or m.shadow_method!=materials[0].shadow_method:
- raise Exception("Conflicting shadow settings in MaterialAtlas constructor")
-
- count = len(self.materials)
- size = 1
- while size*size*2<count:
- size *= 2
- if size*size>=count:
- self.size = (size, size)
- else:
- self.size = (size*2, size)
-
- from .util import get_colormap
-
- 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 atlas")
- 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)
- x = index%self.size[0]
- y = index//self.size[0]
- return ((x+0.5)/self.size[0], (y+0.5)/self.size[1])
-
-def create_material_atlas(context, material):
- if not material.material_atlas:
- raise Exception("Material is not part of a material atlas")
-
- method_hash = compute_render_method_hash(material)
- materials = []
- for m in context.blend_data.materials:
- if m.material_atlas and compute_render_method_hash(m)==method_hash:
- materials.append(m)
-
- return MaterialAtlas(materials)
# Merge materials and form a lookup from source material indices to the
# merged material list
- material_atlas = []
+ material_lookup = []
for m in other.materials:
if m in self.materials:
- material_atlas.append(self.materials.index(m))
+ material_lookup.append(self.materials.index(m))
else:
- material_atlas.append(len(self.materials))
+ material_lookup.append(len(self.materials))
self.materials.append(m)
# Append data and adjust indices where necessary. Since the data is
f.index += offset
f.loop_indices = range(f.loop_indices.start+offset, f.loop_indices.stop+offset)
if other.materials:
- f.material_index = material_atlas[f.material_index]
+ f.material_index = material_lookup[f.material_index]
offset = len(self.edges)
self.edges += other.edges
for g in v.groups:
g.group = group_index_map[g.group]
- def apply_material_atlas(self, material_atlas):
- for m in self.materials:
- if m.name not in material_atlas.material_names:
- raise Exception("Material atlas {} is not compatible with Mesh {}".format(material_atlas.name, self.name))
-
- if self.use_uv=='NONE':
- return
-
- layer = UvLayer("material_atlas")
- 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_atlas.get_material_uv(self.materials[f.material_index])
- for i in f.loop_indices:
- layer.uvs[i] = uv
-
def prepare_uv(self, task):
# Form a list of UV layers referenced by materials with the array atlas
# property set
e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
-def create_mesh_from_object(ctx, obj, material_atlas):
+def create_mesh_from_object(ctx, obj):
if obj.type!="MESH":
raise Exception("Object {} is not a mesh".format(obj.name))
mesh.name = obj.data.name
- if material_atlas:
- mesh.apply_material_atlas(material_atlas)
-
task = ctx.task("Triangulating", 0.3)
mesh.prepare_triangles(task)
task = ctx.task("Smoothing", 0.5)
self.layout.prop(mat, "array_atlas")
if mat.array_atlas:
self.layout.prop(mat, "array_layer")
- if mat.render_mode!='EXTERNAL':
- self.layout.prop(mat, "material_atlas")
if mat.render_mode=='CUSTOM':
self.layout.separator()
self.layout.label(text="Uniform values")
bpy.types.Material.image_based_lighting = bpy.props.BoolProperty(name="Image based lighting", description="Use an environment map for ambient lighting", default=False)
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_atlas = bpy.props.BoolProperty(name="Material atlas", description="Make this material part of a material atlas")
bpy.types.Material.uniforms = bpy.props.CollectionProperty(type=MspGLUniform, name="Uniforms", description="Uniform variables to add to the technique")
bpy.types.Material.active_uniform_index = bpy.props.IntProperty("Active uniform index")