From 829344215d23ae8f1a1488cf00c6f5e41b3b85b5 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 7 Jul 2024 23:47:21 +0300 Subject: [PATCH] Add capability to export submeshes for different materials --- blender/io_mspgl/export_mesh.py | 25 +++++-- blender/io_mspgl/export_object.py | 109 ++++++++++++++++++------------ blender/io_mspgl/mesh.py | 54 ++++++++++++--- blender/io_mspgl/properties.py | 2 + 4 files changed, 132 insertions(+), 58 deletions(-) diff --git a/blender/io_mspgl/export_mesh.py b/blender/io_mspgl/export_mesh.py index b0f186db..34b0fc50 100644 --- a/blender/io_mspgl/export_mesh.py +++ b/blender/io_mspgl/export_mesh.py @@ -1,5 +1,7 @@ +import itertools + class MeshExporter: - def export_mesh(self, ctx, mesh_or_obj): + def export_mesh(self, ctx, mesh_or_obj, *, submesh_index = -1): from .mesh import Mesh, create_mesh_from_object if type(mesh_or_obj)==Mesh: @@ -9,7 +11,11 @@ class MeshExporter: mesh = create_mesh_from_object(task, mesh_or_obj) from .datafile import Resource, Statement, Token - resource = Resource(mesh.name+".mesh", "mesh") + name = mesh.name + if submesh_index>=0: + name += "_{}".format(submesh_index) + name += ".mesh" + resource = Resource(name, "mesh") statements = resource.statements task = ctx.task("Creating statements", 1.0) @@ -53,13 +59,21 @@ class MeshExporter: st.append(Token("PADDING{}_UBYTE".format(pad))) stride += pad + if submesh_index>=0: + vertex_indices = [v.index for v in itertools.chain.from_iterable(b.vertices for b in mesh.batches if b.material_index==submesh_index)] + first_index = min(vertex_indices) + last_index = max(vertex_indices) + else: + first_index = 0 + last_index = len(mesh.vertices)-1 + normal = None color = None uvs = [None]*len(mesh.uv_layers) tan = None group = None weight = None - for v in mesh.vertices: + for v in mesh.vertices[first_index:last_index+1]: if v.normal!=normal: st.sub.append(Statement("normal", *v.normal)) normal = v.normal @@ -91,11 +105,14 @@ class MeshExporter: statements.append(st) for b in mesh.batches: + if submesh_index>=0 and b.material_index!=submesh_index: + continue + st = Statement("batch", Token(b.primitive_type)) if b.primitive_type=="PATCHES": st.sub.append(Statement("patch_size", b.patch_size)) for i in range(0, len(b.vertices), 32): - st.sub.append(Statement("indices", *(v.index for v in b.vertices[i:i+32]))) + st.sub.append(Statement("indices", *(v.index-first_index for v in b.vertices[i:i+32]))) statements.append(st) task.set_progress(1.0) diff --git a/blender/io_mspgl/export_object.py b/blender/io_mspgl/export_object.py index 989d6758..1f2a7cec 100644 --- a/blender/io_mspgl/export_object.py +++ b/blender/io_mspgl/export_object.py @@ -10,6 +10,14 @@ class ObjectExporter: return lods + def collect_submeshes(self, obj): + if not obj.material_slots: + return [(None, -1)] + elif obj.use_submeshes and len(obj.material_slots)>1: + return [(s.material, i) for i, s in enumerate(obj.material_slots)] + else: + return [(obj.material_slots[0].material, -1)] + def export_object_resources(self, ctx, obj, resources): lods = self.collect_object_lods(obj) @@ -20,29 +28,39 @@ class ObjectExporter: mesh_export = MeshExporter() material_export = MaterialExporter() - 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)) - - 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': - tech_name = material.name+".tech" - if tech_name not in resources: - material = Material(material) - material_export.export_technique_resources(subtask, material, resources) - resources[tech_name] = material_export.export_technique(material, 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: - subtask = task.task(l.data, 1.0) - mesh = create_mesh_from_object(subtask, l) - mesh_res = mesh_export.export_mesh(subtask, mesh) - resources[mesh_name] = mesh_res + submeshes = [] + for i, l in enumerate(lods): + submeshes.append((i, l, self.collect_submeshes(l))) + + ctx.set_slices(sum(len(s[2]) for s in submeshes)) + for i, l, s in submeshes: + for m, j in s: + label = "LOD {}".format(i) + if j>=0: + label += " submesh {}".format(j) + task = ctx.next_slice(label) + + if m: + subtask = task.task(m, 0.1) + if m.render_mode!='EXTERNAL': + tech_name = m.name+".tech" + if tech_name not in resources: + material = Material(m) + material_export.export_technique_resources(subtask, material, resources) + resources[tech_name] = material_export.export_technique(material, resources) + elif "stub.tech" not in resources: + resources["stub.tech"] = self.export_stub_technique() + + mesh_name = l.data.name + if j>=0: + mesh_name += "_{}".format(j) + mesh_name += ".mesh" + + if mesh_name not in resources: + subtask = task.task(l.data, 1.0) + mesh = create_mesh_from_object(subtask, l) + mesh_res = mesh_export.export_mesh(subtask, mesh, submesh_index=j) + resources[mesh_name] = mesh_res def export_object(self, obj, resources): if obj.type!='MESH': @@ -58,37 +76,40 @@ class ObjectExporter: center, radius = compute_bounding_sphere([v.co for v in obj.data.vertices]) statements.append(Statement("bounding_sphere_hint", *center, radius)) - prev_mesh = None - prev_tech = None for i, l in enumerate(lods): lod_st = [] - if l.data.name!=prev_mesh: - mesh_res = resources[l.data.name+".mesh"] - lod_st.append(obj_res.create_reference_statement("mesh", mesh_res)) + for m, j in self.collect_submeshes(l): + sub_st = [] + + mesh_name = l.data.name + if j>=0: + mesh_name += "_{}".format(j) + mesh_name += ".mesh" - prev_mesh = l.data.name + sub_st.append(obj_res.create_reference_statement("mesh", resources[mesh_name])) - material = None - if l.material_slots: - material = l.material_slots[0].material - if material: - if material.render_mode=='EXTERNAL': - tech_name = material.technique + if not m: + tech_name = "stub.tech" + elif m.render_mode=='EXTERNAL': + tech_name = m.technique else: - tech_name = material.name+".tech" - else: - tech_name = "stub.tech" + tech_name = m.name+".tech" + + if m and m.render_mode=='EXTERNAL': + sub_st.append(Statement("technique", m.technique)) + else: + sub_st.append(obj_res.create_reference_statement("technique", resources[tech_name])) - if tech_name!=prev_tech: - if material and material.render_mode=='EXTERNAL': - lod_st.append(Statement("technique", material.technique)) + if j>0: + st = Statement("submesh") + st.sub = sub_st + lod_st.append(st) else: - lod_st.append(obj_res.create_reference_statement("technique", resources[tech_name])) - prev_tech = tech_name + lod_st += sub_st if i>0: - st = Statement("level_of_detail", i) + st = Statement("level_of_detail") st.sub = lod_st statements.append(st) else: diff --git a/blender/io_mspgl/mesh.py b/blender/io_mspgl/mesh.py index c738b8f3..25167a3a 100644 --- a/blender/io_mspgl/mesh.py +++ b/blender/io_mspgl/mesh.py @@ -81,6 +81,7 @@ class VertexGroup: class Batch: def __init__(self, pt): self.primitive_type = pt + self.material_index = -1 self.patch_size = 0 self.vertices = [] @@ -561,6 +562,12 @@ class Mesh: while len(v.groups)best_score: best_score = score @@ -972,18 +1004,20 @@ def create_mesh_from_object(ctx, obj): task = ctx.task("Triangulating", 0.3) mesh.prepare_triangles(task) - task = ctx.task("Smoothing", 0.5) + task = ctx.task("Smoothing", 0.45) mesh.prepare_smoothing(task) if mesh.splat_sources: - task = ctx.task("Splat weights", 0.6) + task = ctx.task("Splat weights", 0.55) mesh.prepare_splat_weights(task) else: - task = ctx.task("Vertex groups", 0.6) + task = ctx.task("Vertex groups", 0.55) mesh.prepare_vertex_groups(obj) - task = ctx.task("Preparing UVs", 0.75) + task = ctx.task("Preparing UVs", 0.7) mesh.prepare_uv(task) - task = ctx.task("Preparing vertex colors", 0.85) + task = ctx.task("Preparing vertex colors", 0.8) mesh.prepare_colors(task) + task = ctx.task("Preparing submeshes", 0.9) + mesh.prepare_submeshes(task) task = ctx.task("Render sequence", 1.0) mesh.prepare_sequence(task) diff --git a/blender/io_mspgl/properties.py b/blender/io_mspgl/properties.py index 274aba52..e7b036f5 100644 --- a/blender/io_mspgl/properties.py +++ b/blender/io_mspgl/properties.py @@ -81,6 +81,7 @@ class MspGLObjectProperties(bpy.types.Panel): obj = context.active_object self.layout.prop(obj, "compound") + self.layout.prop(obj, "use_submeshes") self.layout.prop(obj, "lod_for_parent") if obj.lod_for_parent: self.layout.prop(obj, "lod_index") @@ -264,6 +265,7 @@ def register_properties(): bpy.types.Mesh.tangent_uvtex = bpy.props.StringProperty(name="Tangent UV layer", description="UV layer to use as basis for tangent vectors", default="") bpy.types.Object.compound = bpy.props.BoolProperty(name="Compound with parent", description="Join this object to its parent when exporting") + bpy.types.Object.use_submeshes = bpy.props.BoolProperty(name="Use submeshes", description="Split the object's mesh into submeshes by material", default=True) 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) -- 2.45.2