|
|
@ -205,7 +205,8 @@ class MaterialConverter: |
|
|
|
mat_prefix = "RTLit_" |
|
|
|
mat_prefix = "RTLit_" |
|
|
|
else: |
|
|
|
else: |
|
|
|
mat_prefix = "" |
|
|
|
mat_prefix = "" |
|
|
|
mat_name = "".join((mat_prefix, bm.name)) |
|
|
|
mat_prefix2 = "NonVtxP_" if self._exporter().mesh.is_nonpreshaded(bo, bm) else "" |
|
|
|
|
|
|
|
mat_name = "".join((mat_prefix, mat_prefix2, bm.name)) |
|
|
|
self._report.msg("Exporting Material '{}'", mat_name, indent=1) |
|
|
|
self._report.msg("Exporting Material '{}'", mat_name, indent=1) |
|
|
|
hsgmat = self._mgr.find_key(hsGMaterial, name=mat_name, bl=bo) |
|
|
|
hsgmat = self._mgr.find_key(hsGMaterial, name=mat_name, bl=bo) |
|
|
|
if hsgmat is not None: |
|
|
|
if hsgmat is not None: |
|
|
@ -489,8 +490,7 @@ class MaterialConverter: |
|
|
|
else: |
|
|
|
else: |
|
|
|
layer_props = texture.plasma_layer |
|
|
|
layer_props = texture.plasma_layer |
|
|
|
layer.opacity = layer_props.opacity / 100 |
|
|
|
layer.opacity = layer_props.opacity / 100 |
|
|
|
if layer_props.opacity < 100 and not state.blendFlags & hsGMatState.kBlendMask: |
|
|
|
self._handle_layer_opacity(layer, layer_props.opacity) |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha |
|
|
|
|
|
|
|
if layer_props.alpha_halo: |
|
|
|
if layer_props.alpha_halo: |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlphaTestHigh |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlphaTestHigh |
|
|
|
if layer_props.z_bias: |
|
|
|
if layer_props.z_bias: |
|
|
@ -501,7 +501,7 @@ class MaterialConverter: |
|
|
|
state.ZFlags |= hsGMatState.kZNoZWrite |
|
|
|
state.ZFlags |= hsGMatState.kZNoZWrite |
|
|
|
|
|
|
|
|
|
|
|
# Export the specific texture type |
|
|
|
# Export the specific texture type |
|
|
|
self._tex_exporters[texture.type](bo, layer, slot) |
|
|
|
self._tex_exporters[texture.type](bo, layer, slot, idx) |
|
|
|
|
|
|
|
|
|
|
|
# Export any layer animations |
|
|
|
# Export any layer animations |
|
|
|
# NOTE: animated stencils and bumpmaps are nonsense. |
|
|
|
# NOTE: animated stencils and bumpmaps are nonsense. |
|
|
@ -533,9 +533,10 @@ class MaterialConverter: |
|
|
|
|
|
|
|
|
|
|
|
fcurves = [] |
|
|
|
fcurves = [] |
|
|
|
|
|
|
|
|
|
|
|
# Base layers get all of the fcurves for animating things like the diffuse color |
|
|
|
# Base layers get all of the fcurves for animating things like the diffuse color. Danger, |
|
|
|
|
|
|
|
# however, the user can insert fake base layers on top, so be careful. |
|
|
|
texture = tex_slot.texture if tex_slot is not None else None |
|
|
|
texture = tex_slot.texture if tex_slot is not None else None |
|
|
|
if idx == 0: |
|
|
|
if idx == 0 or base_layer.state.miscFlags & hsGMatState.kMiscRestartPassHere: |
|
|
|
harvest_fcurves(bm, fcurves) |
|
|
|
harvest_fcurves(bm, fcurves) |
|
|
|
harvest_fcurves(texture, fcurves) |
|
|
|
harvest_fcurves(texture, fcurves) |
|
|
|
elif tex_slot is not None: |
|
|
|
elif tex_slot is not None: |
|
|
@ -602,10 +603,14 @@ class MaterialConverter: |
|
|
|
return ctrl |
|
|
|
return ctrl |
|
|
|
|
|
|
|
|
|
|
|
def _export_layer_opacity_animation(self, bo, bm, tex_slot, base_layer, fcurves): |
|
|
|
def _export_layer_opacity_animation(self, bo, bm, tex_slot, base_layer, fcurves): |
|
|
|
|
|
|
|
# Dumb function to intercept the opacity values and properly flag the base layer |
|
|
|
|
|
|
|
def process_opacity(value): |
|
|
|
|
|
|
|
self._handle_layer_opacity(base_layer, value) |
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
|
|
|
|
for i in fcurves: |
|
|
|
for i in fcurves: |
|
|
|
if i.data_path == "plasma_layer.opacity": |
|
|
|
if i.data_path == "plasma_layer.opacity": |
|
|
|
base_layer.state.blendFlags |= hsGMatState.kBlendAlpha |
|
|
|
ctrl = self._exporter().animation.make_scalar_leaf_controller(i, process_opacity) |
|
|
|
ctrl = self._exporter().animation.make_scalar_leaf_controller(i) |
|
|
|
|
|
|
|
return ctrl |
|
|
|
return ctrl |
|
|
|
return None |
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
@ -620,7 +625,7 @@ class MaterialConverter: |
|
|
|
return ctrl |
|
|
|
return ctrl |
|
|
|
return None |
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
def _export_texture_type_environment_map(self, bo, layer, slot): |
|
|
|
def _export_texture_type_environment_map(self, bo, layer, slot, idx): |
|
|
|
"""Exports a Blender EnvironmentMapTexture to a plLayer""" |
|
|
|
"""Exports a Blender EnvironmentMapTexture to a plLayer""" |
|
|
|
|
|
|
|
|
|
|
|
texture = slot.texture |
|
|
|
texture = slot.texture |
|
|
@ -765,7 +770,7 @@ class MaterialConverter: |
|
|
|
|
|
|
|
|
|
|
|
return pl_env |
|
|
|
return pl_env |
|
|
|
|
|
|
|
|
|
|
|
def _export_texture_type_image(self, bo, layer, slot): |
|
|
|
def _export_texture_type_image(self, bo, layer, slot, idx): |
|
|
|
"""Exports a Blender ImageTexture to a plLayer""" |
|
|
|
"""Exports a Blender ImageTexture to a plLayer""" |
|
|
|
texture = slot.texture |
|
|
|
texture = slot.texture |
|
|
|
layer_props = texture.plasma_layer |
|
|
|
layer_props = texture.plasma_layer |
|
|
@ -796,6 +801,11 @@ class MaterialConverter: |
|
|
|
if texture.invert_alpha and has_alpha: |
|
|
|
if texture.invert_alpha and has_alpha: |
|
|
|
state.blendFlags |= hsGMatState.kBlendInvertAlpha |
|
|
|
state.blendFlags |= hsGMatState.kBlendInvertAlpha |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Not really mutually exclusive, but if this isn't the first slot and there's no alpha, |
|
|
|
|
|
|
|
# then this is probably a new base layer, meaning that we need to restart the render pass. |
|
|
|
|
|
|
|
if not has_alpha and idx > 0: |
|
|
|
|
|
|
|
state.miscFlags |= hsGMatState.kMiscRestartPassHere |
|
|
|
|
|
|
|
|
|
|
|
if texture.extension in {"CLIP", "EXTEND"}: |
|
|
|
if texture.extension in {"CLIP", "EXTEND"}: |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
|
|
|
|
|
|
|
|
@ -838,11 +848,11 @@ class MaterialConverter: |
|
|
|
mipmap=mipmap, allowed_formats=allowed_formats, |
|
|
|
mipmap=mipmap, allowed_formats=allowed_formats, |
|
|
|
indent=3) |
|
|
|
indent=3) |
|
|
|
|
|
|
|
|
|
|
|
def _export_texture_type_none(self, bo, layer, texture): |
|
|
|
def _export_texture_type_none(self, bo, layer, slot, idx): |
|
|
|
# We'll allow this, just for sanity's sake... |
|
|
|
# We'll allow this, just for sanity's sake... |
|
|
|
pass |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
def _export_texture_type_blend(self, bo, layer, slot): |
|
|
|
def _export_texture_type_blend(self, bo, layer, slot, idx): |
|
|
|
state = layer.state |
|
|
|
state = layer.state |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor |
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
|
state.clampFlags |= hsGMatState.kClampTexture |
|
|
@ -1255,6 +1265,7 @@ class MaterialConverter: |
|
|
|
return self._bump_mats.get(bo, None) |
|
|
|
return self._bump_mats.get(bo, None) |
|
|
|
|
|
|
|
|
|
|
|
def get_material_ambient(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
def get_material_ambient(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
|
|
|
|
# Although Plasma calls this the ambient color, it is actually always used as the emissive color. |
|
|
|
emit_scale = bm.emit * 0.5 |
|
|
|
emit_scale = bm.emit * 0.5 |
|
|
|
if emit_scale > 0.0: |
|
|
|
if emit_scale > 0.0: |
|
|
|
if color is None: |
|
|
|
if color is None: |
|
|
@ -1267,15 +1278,32 @@ class MaterialConverter: |
|
|
|
return utils.color(bpy.context.scene.world.ambient_color) |
|
|
|
return utils.color(bpy.context.scene.world.ambient_color) |
|
|
|
|
|
|
|
|
|
|
|
def get_material_preshade(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
def get_material_preshade(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
if bo.plasma_modifiers.lighting.rt_lights: |
|
|
|
# This color is always used for shading. In all lighting equations, it represents the world |
|
|
|
return hsColorRGBA.kBlack |
|
|
|
# ambient color. Anyway, if we have a manual (read: animated color), just dump that out. |
|
|
|
if color is None: |
|
|
|
if color is not None: |
|
|
|
color = bm.diffuse_color |
|
|
|
|
|
|
|
return utils.color(color) |
|
|
|
return utils.color(color) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Runtime lit objects want light from runtime lights, so they have an ambient world color |
|
|
|
|
|
|
|
# of black - and yes, this is an ambient world color. But it gets more fascinating... |
|
|
|
|
|
|
|
# The color has been folded into the vertex colors for nonpreshaded, so for nonpreshaded, |
|
|
|
|
|
|
|
# we'll want black if it's ONLY runtime lighting (and white for lightmaps). Otherwise, |
|
|
|
|
|
|
|
# just use the material color for now. |
|
|
|
|
|
|
|
if self._exporter().mesh.is_nonpreshaded(bo, bm): |
|
|
|
|
|
|
|
if bo.plasma_modifiers.lightmap.bake_lightmap: |
|
|
|
|
|
|
|
return hsColorRGBA.kWhite |
|
|
|
|
|
|
|
elif not bo.plasma_modifiers.lighting.preshade: |
|
|
|
|
|
|
|
return hsColorRGBA.kBlack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Gulp |
|
|
|
|
|
|
|
return utils.color(bm.diffuse_color) |
|
|
|
|
|
|
|
|
|
|
|
def get_material_runtime(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
def get_material_runtime(self, bo, bm, color: Union[None, mathutils.Color] = None) -> hsColorRGBA: |
|
|
|
if not bo.plasma_modifiers.lighting.preshade: |
|
|
|
# The layer runstime color has no effect if the lighting equation is kLiteVtxNonPreshaded, |
|
|
|
|
|
|
|
# so return black to prevent animations from being exported. |
|
|
|
|
|
|
|
if self._exporter().mesh.is_nonpreshaded(bo, bm): |
|
|
|
return hsColorRGBA.kBlack |
|
|
|
return hsColorRGBA.kBlack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Hmm... |
|
|
|
if color is None: |
|
|
|
if color is None: |
|
|
|
color = bm.diffuse_color |
|
|
|
color = bm.diffuse_color |
|
|
|
return utils.color(color) |
|
|
|
return utils.color(color) |
|
|
@ -1297,6 +1325,13 @@ class MaterialConverter: |
|
|
|
pClass = plLayerSDLAnimation if texture is not None and texture.plasma_layer.anim_sdl_var else plLayerAnimation |
|
|
|
pClass = plLayerSDLAnimation if texture is not None and texture.plasma_layer.anim_sdl_var else plLayerAnimation |
|
|
|
return self._mgr.find_create_key(pClass, bl=bo, name=name) |
|
|
|
return self._mgr.find_create_key(pClass, bl=bo, name=name) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_layer_opacity(self, layer: plLayerInterface, value: float): |
|
|
|
|
|
|
|
if value < 100: |
|
|
|
|
|
|
|
base_layer = layer.bottomOfStack.object |
|
|
|
|
|
|
|
state = base_layer.state |
|
|
|
|
|
|
|
if not state.blendFlags & hsGMatState.kBlendMask: |
|
|
|
|
|
|
|
state.blendFlags |= hsGMatState.kBlendAlpha |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
@property |
|
|
|
def _mgr(self): |
|
|
|
def _mgr(self): |
|
|
|
return self._exporter().mgr |
|
|
|
return self._exporter().mgr |
|
|
@ -1337,6 +1372,26 @@ class MaterialConverter: |
|
|
|
layer.specularPower = min(100.0, float(bm.specular_hardness)) |
|
|
|
layer.specularPower = min(100.0, float(bm.specular_hardness)) |
|
|
|
layer.LODBias = -1.0 |
|
|
|
layer.LODBias = -1.0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def requires_material_shading(self, bm: bpy.types.Material) -> bool: |
|
|
|
|
|
|
|
"""Determines if this material requires the lighting equation we all know and love |
|
|
|
|
|
|
|
(kLiteMaterial) in order to display opacity and color animations.""" |
|
|
|
|
|
|
|
if bm.animation_data is not None and bm.animation_data.action is not None: |
|
|
|
|
|
|
|
if any((i.data_path == "diffuse_color" for i in bm.animation_data.action.fcurves)): |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for slot in filter(lambda x: x and x.use and x.texture, bm.texture_slots): |
|
|
|
|
|
|
|
tex = slot.texture |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO (someday): I think PlasmaMax will actually bake some opacities into the vertices |
|
|
|
|
|
|
|
# so that kLiteVtxNonPreshaded can be used. Might be a good idea at some point. |
|
|
|
|
|
|
|
if tex.plasma_layer.opacity < 100: |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if tex.animation_data is not None and tex.animation_data.action is not None: |
|
|
|
|
|
|
|
if any((i.data_path == "plasma_layer.opacity" for i in tex.animation_data.action.fcurves)): |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def _requires_single_user(self, bo, bm): |
|
|
|
def _requires_single_user(self, bo, bm): |
|
|
|
if bo.data.show_double_sided: |
|
|
|
if bo.data.show_double_sided: |
|
|
|
return True |
|
|
|
return True |
|
|
|