From ecc5bf277d5a36e2f6e5c110fe9c230637b7f287 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 25 Jun 2015 17:31:06 -0400 Subject: [PATCH 1/3] Store Layer keys, not objects... --- korman/exporter/manager.py | 4 ++-- korman/exporter/material.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py index 9d6ef91..4eda89d 100644 --- a/korman/exporter/manager.py +++ b/korman/exporter/manager.py @@ -200,13 +200,13 @@ class ExportManager: location = self._pages[bl.plasma_object.page] return self._nodes[location].key - def get_textures_page(self, layer): + def get_textures_page(self, key): """Gets the appropriate page for a texture for a given plLayer""" # The point of this is to account for per-page textures... if "Textures" in self._pages: return self._pages["Textures"] else: - return layer.key.location + return key.location def has_coordiface(self, bo): if bo.type in {"CAMERA", "EMPTY", "LAMP"}: diff --git a/korman/exporter/material.py b/korman/exporter/material.py index ef8c2a2..98ce223 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -352,10 +352,10 @@ class MaterialConverter: key = _Texture(texture=texture, use_alpha=has_alpha) if key not in self._pending: print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key))) - self._pending[key] = [layer,] + self._pending[key] = [layer.key,] else: print(" Found another user of '{}'".format(texture.image.name)) - self._pending[key].append(layer) + self._pending[key].append(layer.key) def _export_texture_type_none(self, bo, hsgmat, layer, texture): # We'll allow this, just for sanity's sake... @@ -431,7 +431,7 @@ class MaterialConverter: print(" Adding to Layer(s)") for layer in layers: - print(" {}".format(layer.key.name)) + print(" {}".format(layer.name)) page = mgr.get_textures_page(layer) # Layer's page or Textures.prp # If we haven't created this plMipmap in the page (either layer's page or Textures.prp), @@ -447,7 +447,7 @@ class MaterialConverter: pages[page] = mipmap else: mipmap = pages[page] - layer.texture = mipmap.key + layer.object.texture = mipmap.key def get_materials(self, bo): return self._obj2mat[bo] From 9c4dea823562f05512045dee14467134557218e7 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 25 Jun 2015 19:12:17 -0400 Subject: [PATCH 2/3] Fix calculate alpha boogs --- korman/exporter/material.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 98ce223..7d36516 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -87,10 +87,9 @@ class _GLTexture: # NOTE: the variable names are correct for GL_RGBA. We'll still get the right values for # BGRA, obviously, but red will suddenly be... blue. Yeah. if calc_alpha: - for i in range(size, 4): - base = i*4 - r, g, b = buf[base:base+2] - buf[base+3] = int((r + g + b) / 3) + for i in range(0, size, 4): + r, g, b = buf[i:i+3] + buf[i+3] = int((r + g + b) / 3) return bytes(buf) def _get_integer(self, arg): From 8ba4d8287f000ed6956d871999281d6631accdc5 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 25 Jun 2015 18:02:47 -0400 Subject: [PATCH 3/3] Implement stencil layers --- korman/exporter/material.py | 116 ++++++++++++++++++++++++------------ korman/exporter/mesh.py | 10 +++- 2 files changed, 85 insertions(+), 41 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 7d36516..578ea29 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -107,7 +107,7 @@ class _GLTexture: class _Texture: - def __init__(self, texture=None, image=None, use_alpha=None): + def __init__(self, texture=None, image=None, use_alpha=None, force_calc_alpha=False): assert (texture or image) if texture is not None: @@ -119,7 +119,10 @@ class _Texture: self.calc_alpha = False self.mipmap = False - if use_alpha is None: + if force_calc_alpha or self.calc_alpha: + self.calc_alpha = True + self.use_alpha = True + elif use_alpha is None: self.use_alpha = (image.channels == 4 and image.use_alpha) else: self.use_alpha = use_alpha @@ -168,13 +171,25 @@ class MaterialConverter: self._exporter = weakref.ref(exporter) self._pending = {} self._alphatest = {} + self._tex_exporters = { + "ENVRIONMENT_MAP": self._export_texture_type_environment_map, + "IMAGE": self._export_texture_type_image, + "NONE": self._export_texture_type_none, + } def export_material(self, bo, bm): """Exports a Blender Material as an hsGMaterial""" print(" Exporting Material '{}'".format(bm.name)) hsgmat = self._mgr.add_object(hsGMaterial, name=bm.name, bl=bo) - self._export_texture_slots(bo, bm, hsgmat) + slots = [slot for slot in bm.texture_slots if slot is not None and slot.use and + slot.texture is not None and slot.texture.type in self._tex_exporters] + + # Okay, I know this isn't Pythonic... But we're doing it this way because we might actually + # export many slots in one go. Think stencils. + i = 0 + while i < len(slots): + i += self._export_texture_slot(bo, bm, hsgmat, slots, i) # 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 @@ -192,43 +207,64 @@ class MaterialConverter: # Looks like we're done... return hsgmat.key - def _export_texture_slots(self, bo, bm, hsgmat): - for slot in bm.texture_slots: - if slot is None or not slot.use: - continue - - name = "{}_{}".format(bm.name, slot.name) - print(" Exporting Plasma Layer '{}'".format(name)) - layer = self._mgr.add_object(plLayer, name=name, bl=bo) - self._propagate_material_settings(bm, layer) - - # UVW Channel - for i, uvchan in enumerate(bo.data.tessface_uv_textures): - if uvchan.name == slot.uv_layer: - layer.UVWSrc = i - print(" Using UV Map #{} '{}'".format(i, name)) - break - else: - print(" No UVMap specified... Blindly using the first one, maybe it exists :|") + def _export_texture_slot(self, bo, bm, hsgmat, slots, idx): + slot = slots[idx] + num_exported = 1 + + name = "{}_{}".format(bm.name, slot.name) + print(" Exporting Plasma Layer '{}'".format(name)) + layer = self._mgr.add_object(plLayer, name=name, bl=bo) + self._propagate_material_settings(bm, layer) + + # UVW Channel + for i, uvchan in enumerate(bo.data.tessface_uv_textures): + if uvchan.name == slot.uv_layer: + layer.UVWSrc = i + print(" Using UV Map #{} '{}'".format(i, name)) + break + else: + print(" No UVMap specified... Blindly using the first one, maybe it exists :|") - # General texture flags and such - state = layer.state + state = layer.state + if slot.use_stencil: + hsgmat.compFlags |= hsGMaterial.kCompNeedsBlendChannel + state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor + state.clampFlags |= hsGMatState.kClampTexture + state.ZFlags |= hsGMatState.kZNoZWrite + layer.ambient = hsColorRGBA(1.0, 1.0, 1.0, 1.0) + + # Plasma actually wants the next layer first, so let's export him + nextIdx = idx + 1 + if len(slots) == nextIdx: + raise ExportError("Texture Slot '{}' wants to be a stencil, but there are no more TextureSlots.".format(slot.name)) + print(" --- BEGIN STENCIL ---") + self._export_texture_slot(bo, bm, hsgmat, slots, nextIdx) + print(" --- END STENCIL ---") + num_exported += 1 + + # Now that we've exported the bugger, flag him as binding with this texture + prev_layer = hsgmat.layers[-1].object + prev_state = prev_layer.state + prev_state.miscFlags |= hsGMatState.kMiscBindNext | hsGMatState.kMiscRestartPassHere + if not prev_state.blendFlags & hsGMatState.kBlendMask: + prev_state.blendFlags |= hsGMatState.kBlendAlpha + else: + # Standard layer flags ahoy if slot.blend_type == "ADD": state.blendFlags |= hsGMatState.kBlendAdd elif slot.blend_type == "MULTIPLY": state.blendFlags |= hsGMatState.kBlendMult - # Export the specific texture type - texture = slot.texture - export_fn = "_export_texture_type_{}".format(texture.type.lower()) - if not hasattr(self, export_fn): - raise explosions.UnsupportedTextureError(texture, bm) - getattr(self, export_fn)(bo, hsgmat, layer, texture) - hsgmat.addLayer(layer.key) + # Export the specific texture type + texture = slot.texture + self._tex_exporters[texture.type](bo, hsgmat, layer, slot) + hsgmat.addLayer(layer.key) + return num_exported - def _export_texture_type_environment_map(self, bo, hsgmat, layer, texture): + def _export_texture_type_environment_map(self, bo, hsgmat, layer, slot): """Exports a Blender EnvironmentMapTexture to a plLayer""" + texture = slot.texture bl_env = texture.environment_map if bl_env.source in {"STATIC", "ANIMATED"}: if bl_env.mapping == "PLANE" and self._mgr.getVer() >= pvMoul: @@ -323,21 +359,25 @@ class MaterialConverter: return pl_env.key - def _export_texture_type_image(self, bo, hsgmat, layer, texture): + def _export_texture_type_image(self, bo, hsgmat, layer, slot): """Exports a Blender ImageTexture to a plLayer""" + texture = slot.texture # Does the image have any alpha at all? - has_alpha = texture.use_calculate_alpha or self._test_image_alpha(texture.image) + has_alpha = texture.use_calculate_alpha or slot.use_stencil or self._test_image_alpha(texture.image) if (texture.image.use_alpha and texture.use_alpha) and not has_alpha: warning = "'{}' wants to use alpha, but '{}' is opaque".format(texture.name, texture.image.name) self._exporter().report.warn(warning, indent=3) # First, let's apply any relevant flags state = layer.state - if texture.invert_alpha and has_alpha: - state.blendFlags |= hsGMatState.kBlendInvertAlpha - if texture.use_alpha and has_alpha: - state.blendFlags |= hsGMatState.kBlendAlpha + if not slot.use_stencil: + # mutually exclusive blend flags + if texture.use_alpha and has_alpha: + state.blendFlags |= hsGMatState.kBlendAlpha + + if texture.invert_alpha and has_alpha: + state.blendFlags |= hsGMatState.kBlendInvertAlpha if texture.extension == "CLIP": state.clampFlags |= hsGMatState.kClampTexture @@ -348,7 +388,7 @@ class MaterialConverter: if texture.image is None: bitmap = self.add_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo) else: - key = _Texture(texture=texture, use_alpha=has_alpha) + key = _Texture(texture=texture, use_alpha=has_alpha, force_calc_alpha=slot.use_stencil) if key not in self._pending: print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key))) self._pending[key] = [layer.key,] diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 7318b73..d4d0f5a 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -42,7 +42,7 @@ class _RenderLevel: # Naive... BlendSpans (any blending on the first layer) are MAJOR_BLEND if blendSpan: - self.major = self.MAJOR_BLEND + self.major = self.MAJOR_DEFAULT # We use the blender material's pass index (which we stashed in the hsGMaterial) to increment # the render pass, just like it says... @@ -69,8 +69,12 @@ class _RenderLevel: class _DrawableCriteria: def __init__(self, hsgmat, pass_index): - _layer = hsgmat.layers[0].object # better doggone well have a layer... - self.blend_span = bool(_layer.state.blendFlags & hsGMatState.kBlendMask) + for layer in hsgmat.layers: + if layer.object.state.blendFlags & hsGMatState.kBlendMask: + self.blend_span = True + break + else: + self.blend_span = False self.criteria = 0 # TODO self.render_level = _RenderLevel(hsgmat, pass_index, self.blend_span)