From cb45d5979d8c45227c719903b0fbd28d4c8bdf95 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 Jan 2019 20:56:47 -0500 Subject: [PATCH 1/4] Enforce lightmap material uniqueness and fix other issues Two problems were fixed here: - Materials were always exported unconditionally, meaning that we were wasting time processing the same data over and over. This could have generated "interesting" data (eg multiple hsGMaterials and friends with the same name) that Plasma would have barfed on. - Lightmaps were being applied to the incorrect materials --- korman/exporter/material.py | 39 ++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 6468975..5d652d4 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -178,9 +178,23 @@ class MaterialConverter: def export_material(self, bo, bm): """Exports a Blender Material as an hsGMaterial""" - self._report.msg("Exporting Material '{}'", bm.name, indent=1) - hsgmat = self._mgr.add_object(hsGMaterial, name=bm.name, bl=bo) + # Sometimes, a material might need to be single-use. Right now, the most apparent example + # of that situation is when a lightmap image is baked. Wavesets are in the same boat, but + # that's a special case as of the writing of this code. + single_user = self._requires_single_user_material(bo, bm) + if single_user: + mat_name = "{}_{}".format(bo.name, bm.name) + self._report.msg("Exporting Material '{}' as single user '{}'", bm.name, mat_name, indent=1) + hgmat = None + else: + mat_name = bm.name + self._report.msg("Exporting Material '{}'", mat_name, indent=1) + hsgmat = self._mgr.find_key(hsGMaterial, name=mat_name, bl=bo) + if hsgmat is not None: + return hsgmat + + hsgmat = self._mgr.add_object(hsGMaterial, name=mat_name, bl=bo) slots = [(idx, slot) for idx, slot in enumerate(bm.texture_slots) if self._can_export_texslot(slot)] # There is a major difference in how Blender and Plasma handle stencils. @@ -227,7 +241,7 @@ class MaterialConverter: curr_stencils = len(stencils) for i in range(curr_stencils): stencil_idx, stencil = stencils[i] - stencil_name = "STENCILGEN_{}@{}_{}".format(stencil.name, bm.name, slot.name) + stencil_name = "STENCILGEN_{}@{}_{}".format(stencil.name, hsgmat.key.name, slot.name) stencil_layer = self.export_texture_slot(bo, bm, hsgmat, stencil, stencil_idx, name=stencil_name) if i+1 < curr_stencils: stencil_layer.state.miscFlags |= hsGMatState.kMiscBindNext @@ -236,7 +250,7 @@ class MaterialConverter: # Plasma makes several assumptions that every hsGMaterial has at least one layer. If this # material had no Textures, we will need to initialize a default layer if not hsgmat.layers: - layer = self._mgr.add_object(plLayer, name="{}_AutoLayer".format(bm.name), bl=bo) + layer = self._mgr.add_object(plLayer, name="{}_AutoLayer".format(mat_name), bl=bo) self._propagate_material_settings(bm, layer) hsgmat.addLayer(layer.key) @@ -266,7 +280,7 @@ class MaterialConverter: return hsgmat.key def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx): - name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) + name = "{}_{}".format(hsgmat.key.name, slot.name) self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2) # Okay, now we need to make 3 layers for the Du, Dw, and Dv @@ -315,7 +329,7 @@ class MaterialConverter: def export_texture_slot(self, bo, bm, hsgmat, slot, idx, name=None, blend_flags=True): if name is None: - name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) + name = "{}_{}".format(hsgmat.key.name, slot.name) self._report.msg("Exporting Plasma Layer '{}'", name, indent=2) layer = self._mgr.add_object(plLayer, name=name, bl=bo) if bm is not None and not slot.use_map_normal: @@ -973,7 +987,10 @@ class MaterialConverter: if not tex_name in bm.texture_slots: raise ExportError("Texture '{}' not used in Material '{}'".format(bm.name, tex_name)) - name = "{}_{}_LayerAnim".format(bm.name, tex_name) + if self._requires_single_user_material(bo, bm): + name = "{}_{}_{}_LayerAnim".format(bo.name, bm.name, tex_name) + else: + name = "{}_{}_LayerAnim".format(bm.name, tex_name) layer = texture.plasma_layer pClass = plLayerSDLAnimation if layer.anim_sdl_var else plLayerAnimation return self._mgr.find_create_key(pClass, bl=bo, name=name) @@ -1016,6 +1033,14 @@ class MaterialConverter: def _report(self): return self._exporter().report + def _requires_single_user_material(self, bo, bm): + modifiers = bo.plasma_modifiers + if modifiers.lightmap.bake_lightmap: + return True + if modifiers.water_basic.enabled: + return True + return False + def _test_image_alpha(self, image): """Tests to see if this image has any alpha data""" From df73d26896858f2b8987eb4c6321ad652173b074 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 Jan 2019 20:59:14 -0500 Subject: [PATCH 2/4] Fix if-in-dict antipattern in material code --- korman/exporter/material.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 5d652d4..fa976d8 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -255,10 +255,8 @@ class MaterialConverter: hsgmat.addLayer(layer.key) # Cache this material for later - if bo in self._obj2mat: - self._obj2mat[bo].append(hsgmat.key) - else: - self._obj2mat[bo] = [hsgmat.key] + mat_list = self._obj2mat.setdefault(bo, []) + mat_list.append(hsgmat.key) # Looks like we're done... return hsgmat.key From 83afcf57d8aa8127b5537a5344c0138cf30db39b Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 Jan 2019 21:26:19 -0500 Subject: [PATCH 3/4] Better single user name generation --- korman/exporter/material.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index fa976d8..0763561 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -184,7 +184,7 @@ class MaterialConverter: # that's a special case as of the writing of this code. single_user = self._requires_single_user_material(bo, bm) if single_user: - mat_name = "{}_{}".format(bo.name, bm.name) + mat_name = "{}_AutoSingle".format(bm.name) if bo.name == bm.name else "{}_{}".format(bo.name, bm.name) self._report.msg("Exporting Material '{}' as single user '{}'", bm.name, mat_name, indent=1) hgmat = None else: From f5d5be256bfd6342640916f53e4b9e766b917515 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 Jan 2019 21:26:46 -0500 Subject: [PATCH 4/4] Don't use `add_object` in material converter --- korman/exporter/material.py | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 0763561..2157530 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -241,7 +241,7 @@ class MaterialConverter: curr_stencils = len(stencils) for i in range(curr_stencils): stencil_idx, stencil = stencils[i] - stencil_name = "STENCILGEN_{}@{}_{}".format(stencil.name, hsgmat.key.name, slot.name) + stencil_name = "STENCILGEN_{}@{}_{}".format(stencil.name, bm.name, slot.name) stencil_layer = self.export_texture_slot(bo, bm, hsgmat, stencil, stencil_idx, name=stencil_name) if i+1 < curr_stencils: stencil_layer.state.miscFlags |= hsGMatState.kMiscBindNext @@ -250,7 +250,7 @@ class MaterialConverter: # Plasma makes several assumptions that every hsGMaterial has at least one layer. If this # material had no Textures, we will need to initialize a default layer if not hsgmat.layers: - layer = self._mgr.add_object(plLayer, name="{}_AutoLayer".format(mat_name), bl=bo) + layer = self._mgr.find_create_object(plLayer, name="{}_AutoLayer".format(bm.name), bl=bo) self._propagate_material_settings(bm, layer) hsgmat.addLayer(layer.key) @@ -278,13 +278,13 @@ class MaterialConverter: return hsgmat.key def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx): - name = "{}_{}".format(hsgmat.key.name, slot.name) + name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2) # Okay, now we need to make 3 layers for the Du, Dw, and Dv - du_layer = self._mgr.add_object(plLayer, name="{}_DU_BumpLut".format(name), bl=bo) - dw_layer = self._mgr.add_object(plLayer, name="{}_DW_BumpLut".format(name), bl=bo) - dv_layer = self._mgr.add_object(plLayer, name="{}_DV_BumpLut".format(name), bl=bo) + du_layer = self._mgr.find_create_object(plLayer, name="{}_DU_BumpLut".format(name), bl=bo) + dw_layer = self._mgr.find_create_object(plLayer, name="{}_DW_BumpLut".format(name), bl=bo) + dv_layer = self._mgr.find_create_object(plLayer, name="{}_DV_BumpLut".format(name), bl=bo) for layer in (du_layer, dw_layer, dv_layer): layer.ambient = hsColorRGBA(1.0, 1.0, 1.0, 1.0) @@ -327,9 +327,9 @@ class MaterialConverter: def export_texture_slot(self, bo, bm, hsgmat, slot, idx, name=None, blend_flags=True): if name is None: - name = "{}_{}".format(hsgmat.key.name, slot.name) + name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) self._report.msg("Exporting Plasma Layer '{}'", name, indent=2) - layer = self._mgr.add_object(plLayer, name=name, bl=bo) + layer = self._mgr.find_create_object(plLayer, name=name, bl=bo) if bm is not None and not slot.use_map_normal: self._propagate_material_settings(bm, layer) @@ -541,21 +541,12 @@ class MaterialConverter: def export_dynamic_env(self, bo, layer, texture, pl_class): - # To protect the user from themselves, let's check to make sure that a DEM/DCM matching this - # viewpoint object has not already been exported... bl_env = texture.environment_map viewpt = bl_env.viewpoint_object if viewpt is None: viewpt = bo - name = "{}_DynEnvMap".format(viewpt.name) + name = "{}_DynEnvMap".format(texture.name) pl_env = self._mgr.find_object(pl_class, bl=bo, name=name) - if pl_env is not None: - self._report.msg("EnvMap for viewpoint {} already exported... NOTE: Your settings here will be overridden by the previous object!", - viewpt.name, indent=3) - if isinstance(pl_env, plDynamicCamMap): - pl_env.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) - pl_env.addMatLayer(layer.key) - return pl_env # Ensure POT oRes = bl_env.resolution @@ -564,7 +555,7 @@ class MaterialConverter: self._report.msg("Overriding EnvMap size to ({}x{}) -- POT", eRes, eRes, indent=3) # And now for the general ho'hum-ness - pl_env = self._mgr.add_object(pl_class, bl=bo, name=name) + pl_env = self._mgr.find_create_object(pl_class, bl=bo, name=name) pl_env.hither = bl_env.clip_start pl_env.yon = bl_env.clip_end pl_env.refreshRate = 0.01 if bl_env.source == "ANIMATED" else 0.0 @@ -604,7 +595,7 @@ class MaterialConverter: # This is really just so we don't raise any eyebrows if anyone is looking at the files. # If you're disabling DCMs, then you're obviuously trolling! # Cyan generates a single color image, but we'll just set the layer colors and go away. - fake_layer = self._mgr.add_object(plLayer, bl=bo, name="{}_DisabledDynEnvMap".format(viewpt.name)) + fake_layer = self._mgr.find_create_object(plLayer, bl=bo, name="{}_DisabledDynEnvMap".format(texture.name)) fake_layer.ambient = layer.ambient fake_layer.preshade = layer.preshade fake_layer.runtime = layer.runtime @@ -985,10 +976,7 @@ class MaterialConverter: if not tex_name in bm.texture_slots: raise ExportError("Texture '{}' not used in Material '{}'".format(bm.name, tex_name)) - if self._requires_single_user_material(bo, bm): - name = "{}_{}_{}_LayerAnim".format(bo.name, bm.name, tex_name) - else: - name = "{}_{}_LayerAnim".format(bm.name, tex_name) + name = "{}_{}_LayerAnim".format(bm.name, tex_name) layer = texture.plasma_layer pClass = plLayerSDLAnimation if layer.anim_sdl_var else plLayerAnimation return self._mgr.find_create_key(pClass, bl=bo, name=name)