|
|
@ -87,10 +87,9 @@ class _GLTexture: |
|
|
|
# NOTE: the variable names are correct for GL_RGBA. We'll still get the right values for |
|
|
|
# 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. |
|
|
|
# BGRA, obviously, but red will suddenly be... blue. Yeah. |
|
|
|
if calc_alpha: |
|
|
|
if calc_alpha: |
|
|
|
for i in range(size, 4): |
|
|
|
for i in range(0, size, 4): |
|
|
|
base = i*4 |
|
|
|
r, g, b = buf[i:i+3] |
|
|
|
r, g, b = buf[base:base+2] |
|
|
|
buf[i+3] = int((r + g + b) / 3) |
|
|
|
buf[base+3] = int((r + g + b) / 3) |
|
|
|
|
|
|
|
return bytes(buf) |
|
|
|
return bytes(buf) |
|
|
|
|
|
|
|
|
|
|
|
def _get_integer(self, arg): |
|
|
|
def _get_integer(self, arg): |
|
|
@ -108,7 +107,7 @@ class _GLTexture: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _Texture: |
|
|
|
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) |
|
|
|
assert (texture or image) |
|
|
|
|
|
|
|
|
|
|
|
if texture is not None: |
|
|
|
if texture is not None: |
|
|
@ -120,7 +119,10 @@ class _Texture: |
|
|
|
self.calc_alpha = False |
|
|
|
self.calc_alpha = False |
|
|
|
self.mipmap = 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) |
|
|
|
self.use_alpha = (image.channels == 4 and image.use_alpha) |
|
|
|
else: |
|
|
|
else: |
|
|
|
self.use_alpha = use_alpha |
|
|
|
self.use_alpha = use_alpha |
|
|
@ -169,13 +171,25 @@ class MaterialConverter: |
|
|
|
self._exporter = weakref.ref(exporter) |
|
|
|
self._exporter = weakref.ref(exporter) |
|
|
|
self._pending = {} |
|
|
|
self._pending = {} |
|
|
|
self._alphatest = {} |
|
|
|
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): |
|
|
|
def export_material(self, bo, bm): |
|
|
|
"""Exports a Blender Material as an hsGMaterial""" |
|
|
|
"""Exports a Blender Material as an hsGMaterial""" |
|
|
|
print(" Exporting Material '{}'".format(bm.name)) |
|
|
|
print(" Exporting Material '{}'".format(bm.name)) |
|
|
|
|
|
|
|
|
|
|
|
hsgmat = self._mgr.add_object(hsGMaterial, name=bm.name, bl=bo) |
|
|
|
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 |
|
|
|
# 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 |
|
|
|
# material had no Textures, we will need to initialize a default layer |
|
|
@ -193,10 +207,9 @@ class MaterialConverter: |
|
|
|
# Looks like we're done... |
|
|
|
# Looks like we're done... |
|
|
|
return hsgmat.key |
|
|
|
return hsgmat.key |
|
|
|
|
|
|
|
|
|
|
|
def _export_texture_slots(self, bo, bm, hsgmat): |
|
|
|
def _export_texture_slot(self, bo, bm, hsgmat, slots, idx): |
|
|
|
for slot in bm.texture_slots: |
|
|
|
slot = slots[idx] |
|
|
|
if slot is None or not slot.use: |
|
|
|
num_exported = 1 |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
name = "{}_{}".format(bm.name, slot.name) |
|
|
|
name = "{}_{}".format(bm.name, slot.name) |
|
|
|
print(" Exporting Plasma Layer '{}'".format(name)) |
|
|
|
print(" Exporting Plasma Layer '{}'".format(name)) |
|
|
@ -212,8 +225,31 @@ class MaterialConverter: |
|
|
|
else: |
|
|
|
else: |
|
|
|
print(" No UVMap specified... Blindly using the first one, maybe it exists :|") |
|
|
|
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": |
|
|
|
if slot.blend_type == "ADD": |
|
|
|
state.blendFlags |= hsGMatState.kBlendAdd |
|
|
|
state.blendFlags |= hsGMatState.kBlendAdd |
|
|
|
elif slot.blend_type == "MULTIPLY": |
|
|
|
elif slot.blend_type == "MULTIPLY": |
|
|
@ -221,15 +257,14 @@ class MaterialConverter: |
|
|
|
|
|
|
|
|
|
|
|
# Export the specific texture type |
|
|
|
# Export the specific texture type |
|
|
|
texture = slot.texture |
|
|
|
texture = slot.texture |
|
|
|
export_fn = "_export_texture_type_{}".format(texture.type.lower()) |
|
|
|
self._tex_exporters[texture.type](bo, hsgmat, layer, slot) |
|
|
|
if not hasattr(self, export_fn): |
|
|
|
|
|
|
|
raise explosions.UnsupportedTextureError(texture, bm) |
|
|
|
|
|
|
|
getattr(self, export_fn)(bo, hsgmat, layer, texture) |
|
|
|
|
|
|
|
hsgmat.addLayer(layer.key) |
|
|
|
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""" |
|
|
|
"""Exports a Blender EnvironmentMapTexture to a plLayer""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
texture = slot.texture |
|
|
|
bl_env = texture.environment_map |
|
|
|
bl_env = texture.environment_map |
|
|
|
if bl_env.source in {"STATIC", "ANIMATED"}: |
|
|
|
if bl_env.source in {"STATIC", "ANIMATED"}: |
|
|
|
if bl_env.mapping == "PLANE" and self._mgr.getVer() >= pvMoul: |
|
|
|
if bl_env.mapping == "PLANE" and self._mgr.getVer() >= pvMoul: |
|
|
@ -324,21 +359,25 @@ class MaterialConverter: |
|
|
|
|
|
|
|
|
|
|
|
return pl_env.key |
|
|
|
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""" |
|
|
|
"""Exports a Blender ImageTexture to a plLayer""" |
|
|
|
|
|
|
|
texture = slot.texture |
|
|
|
|
|
|
|
|
|
|
|
# Does the image have any alpha at all? |
|
|
|
# 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: |
|
|
|
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) |
|
|
|
warning = "'{}' wants to use alpha, but '{}' is opaque".format(texture.name, texture.image.name) |
|
|
|
self._exporter().report.warn(warning, indent=3) |
|
|
|
self._exporter().report.warn(warning, indent=3) |
|
|
|
|
|
|
|
|
|
|
|
# First, let's apply any relevant flags |
|
|
|
# First, let's apply any relevant flags |
|
|
|
state = layer.state |
|
|
|
state = layer.state |
|
|
|
if texture.invert_alpha and has_alpha: |
|
|
|
if not slot.use_stencil: |
|
|
|
state.blendFlags |= hsGMatState.kBlendInvertAlpha |
|
|
|
# mutually exclusive blend flags |
|
|
|
if texture.use_alpha and has_alpha: |
|
|
|
if texture.use_alpha and has_alpha: |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if texture.invert_alpha and has_alpha: |
|
|
|
|
|
|
|
state.blendFlags |= hsGMatState.kBlendInvertAlpha |
|
|
|
if texture.extension == "CLIP": |
|
|
|
if texture.extension == "CLIP": |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
|
|
|
|
|
|
|
|
@ -349,13 +388,13 @@ class MaterialConverter: |
|
|
|
if texture.image is None: |
|
|
|
if texture.image is None: |
|
|
|
bitmap = self.add_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo) |
|
|
|
bitmap = self.add_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo) |
|
|
|
else: |
|
|
|
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: |
|
|
|
if key not in self._pending: |
|
|
|
print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key))) |
|
|
|
print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key))) |
|
|
|
self._pending[key] = [layer,] |
|
|
|
self._pending[key] = [layer.key,] |
|
|
|
else: |
|
|
|
else: |
|
|
|
print(" Found another user of '{}'".format(texture.image.name)) |
|
|
|
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): |
|
|
|
def _export_texture_type_none(self, bo, hsgmat, layer, texture): |
|
|
|
# We'll allow this, just for sanity's sake... |
|
|
|
# We'll allow this, just for sanity's sake... |
|
|
@ -431,7 +470,7 @@ class MaterialConverter: |
|
|
|
|
|
|
|
|
|
|
|
print(" Adding to Layer(s)") |
|
|
|
print(" Adding to Layer(s)") |
|
|
|
for layer in layers: |
|
|
|
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 |
|
|
|
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), |
|
|
|
# If we haven't created this plMipmap in the page (either layer's page or Textures.prp), |
|
|
@ -447,7 +486,7 @@ class MaterialConverter: |
|
|
|
pages[page] = mipmap |
|
|
|
pages[page] = mipmap |
|
|
|
else: |
|
|
|
else: |
|
|
|
mipmap = pages[page] |
|
|
|
mipmap = pages[page] |
|
|
|
layer.texture = mipmap.key |
|
|
|
layer.object.texture = mipmap.key |
|
|
|
|
|
|
|
|
|
|
|
def get_materials(self, bo): |
|
|
|
def get_materials(self, bo): |
|
|
|
return self._obj2mat[bo] |
|
|
|
return self._obj2mat[bo] |
|
|
|