Browse Source

Improve support for VtxNonPreshaded and RT Lights.

This appropriately marks spans as VtxNonPreshaded if they use vertex
alpha or runtime lighting. In those cases, we properly fold the runtime
light color into the vertex color as expected by Plasma.
pull/210/head
Adam Johnson 4 years ago
parent
commit
94398fd293
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 169
      korman/exporter/mesh.py

169
korman/exporter/mesh.py

@ -14,6 +14,7 @@
# along with Korman. If not, see <http://www.gnu.org/licenses/>.
import bpy
import itertools
from PyHSPlasma import *
from math import fabs
import weakref
@ -29,6 +30,29 @@ _WARN_VERTS_PER_SPAN = 0x8000
_VERTEX_COLOR_LAYERS = {"col", "color", "colour"}
class _GeoSpan:
def __init__(self, bo, bm, geospan, pass_index=None):
self.geospan = geospan
self.pass_index = pass_index if pass_index is not None else 0
self.mult_color = self._determine_mult_color(bo, bm)
def _determine_mult_color(self, bo, bm):
"""Determines the color all vertex colors should be multipled by in this span."""
if self.geospan.props & plGeometrySpan.kDiffuseFoldedIn:
color = bm.diffuse_color
base_layer = self._find_bottom_of_stack()
return (color.r, color.b, color.g, base_layer.opacity)
if not bo.plasma_modifiers.lighting.preshade:
return (0.0, 0.0, 0.0, 0.0)
return (1.0, 1.0, 1.0, 1.0)
def _find_bottom_of_stack(self) -> plLayerInterface:
base_layer = self.geospan.material.object.layers[0].object
while base_layer.underLay is not None:
base_layer = base_layer.underLay.object
return base_layer
class _RenderLevel:
MAJOR_OPAQUE = 0
MAJOR_FRAMEBUF = 1
@ -231,15 +255,48 @@ class MeshConverter(_MeshManager):
return (num_user_texs, total_texs, max_user_texs)
def _create_geospan(self, bo, mesh, bm, hsgmatKey):
def _check_vtx_alpha(self, mesh, material_idx):
if material_idx is not None:
polygons = (i for i in mesh.polygons if i.material_index == material_idx)
else:
polygons = mesh.polygons
alpha_layer = self._find_vtx_alpha_layer(mesh.vertex_colors)
if alpha_layer is None:
return False
alpha_loops = (alpha_layer[i.loop_start:i.loop_start+i.loop_total] for i in polygons)
opaque = (sum(i.color) == len(i.color) for i in itertools.chain.from_iterable(alpha_loops))
has_alpha = not all(opaque)
return has_alpha
def _check_vtx_nonpreshaded(self, bo, mesh, material_idx, base_layer):
def check_layer_shading_animation(layer):
if isinstance(layer, plLayerAnimationBase):
return layer.opacityCtl is not None or layer.preshadeCtl is not None or layer.runtimeCtl is not None
if layer.underLay is not None:
return check_layer_shading_animation(layer.underLay.object)
return False
# TODO: if this is an avatar, we can't be non-preshaded.
if check_layer_shading_animation(base_layer):
return False
if base_layer.state.shadeFlags & hsGMatState.kShadeEmissive:
return False
mods = bo.plasma_modifiers
if mods.lighting.rt_lights:
return True
if mods.lightmap.bake_lightmap:
return True
if self._check_vtx_alpha(mesh, material_idx):
return True
return False
def _create_geospan(self, bo, mesh, material_idx, bm, hsgmatKey):
"""Initializes a plGeometrySpan from a Blender Object and an hsGMaterial"""
geospan = plGeometrySpan()
geospan.material = hsgmatKey
# Mark us as needing a BlendSpan if the material require blending
if hsgmatKey.object.layers[0].object.state.blendFlags & hsGMatState.kBlendMask:
geospan.props |= plGeometrySpan.kRequiresBlending
# GeometrySpan format
# For now, we really only care about the number of UVW Channels
user_uvws, total_uvws, max_user_uvws = self._calc_num_uvchans(bo, mesh)
@ -247,10 +304,22 @@ class MeshConverter(_MeshManager):
raise explosions.TooManyUVChannelsError(bo, bm, user_uvws, max_user_uvws)
geospan.format = total_uvws
# Begin total guesswork WRT flags
mods = bo.plasma_modifiers
if mods.lightmap.enabled:
def is_alpha_blended(layer):
if layer.state.blendFlags & hsGMatState.kBlendMask:
return True
if layer.underLay is not None:
return is_alpha_blended(layer.underLay.object)
return False
base_layer = hsgmatKey.object.layers[0].object
if is_alpha_blended(base_layer) or self._check_vtx_alpha(mesh, material_idx):
geospan.props |= plGeometrySpan.kRequiresBlending
if self._check_vtx_nonpreshaded(bo, mesh, material_idx, base_layer):
geospan.props |= plGeometrySpan.kLiteVtxNonPreshaded
if (geospan.props & plGeometrySpan.kLiteMask) != plGeometrySpan.kLiteMaterial:
geospan.props |= plGeometrySpan.kDiffuseFoldedIn
mods = bo.plasma_modifiers
if mods.lighting.rt_lights:
geospan.props |= plGeometrySpan.kPropRunTimeLight
if not bm.use_shadows:
@ -292,7 +361,7 @@ class MeshConverter(_MeshManager):
dspan.composeGeometry(True, True)
inc_progress()
def _export_geometry(self, bo, mesh, materials, geospans):
def _export_geometry(self, bo, mesh, materials, geospans, mat2span_LUT):
# Recall that materials is a mapping of exported materials to blender material indices.
# Therefore, geodata maps blender material indices to working geometry data.
# Maybe the logic is a bit inverted, but it keeps the inner loop simple.
@ -301,16 +370,8 @@ class MeshConverter(_MeshManager):
# Locate relevant vertex color layers now...
lm = bo.plasma_modifiers.lightmap
has_vtx_alpha = False
color, alpha = None, None
for vcol_layer in mesh.tessface_vertex_colors:
name = vcol_layer.name.lower()
if name in _VERTEX_COLOR_LAYERS:
color = vcol_layer.data
elif name == "autocolor" and color is None and not lm.bake_lightmap:
color = vcol_layer.data
elif name == "alpha":
alpha = vcol_layer.data
color = None if lm.bake_lightmap else self._find_vtx_color_layer(mesh.tessface_vertex_colors)
alpha = self._find_vtx_alpha_layer(mesh.tessface_vertex_colors)
# Convert Blender faces into things we can stuff into libHSPlasma
for i, tessface in enumerate(mesh.tessfaces):
@ -340,10 +401,8 @@ class MeshConverter(_MeshManager):
else:
src = alpha[i]
# average color becomes the alpha value
tessface_alphas = (((src.color1[0] + src.color1[1] + src.color1[2]) / 3),
((src.color2[0] + src.color2[1] + src.color2[2]) / 3),
((src.color3[0] + src.color3[1] + src.color3[2]) / 3),
((src.color4[0] + src.color4[1] + src.color4[2]) / 3))
tessface_alphas = ((sum(src.color1) / 3), (sum(src.color2) / 3),
(sum(src.color3) / 3), (sum(src.color4) / 3))
if bumpmap is not None:
gradPass = []
@ -373,10 +432,16 @@ class MeshConverter(_MeshManager):
for j, vertex in enumerate(tessface.vertices):
uvws = tuple([uvw[j] for uvw in tessface_uvws])
# Grab VCols
vertex_color = (int(tessface_colors[j][0] * 255), int(tessface_colors[j][1] * 255),
int(tessface_colors[j][2] * 255), int(tessface_alphas[j] * 255))
has_vtx_alpha |= bool(tessface_alphas[j] < 1.0)
# Calculate vertex colors.
if mat2span_LUT:
mult_color = geospans[mat2span_LUT[tessface.material_index]].mult_color
else:
mult_color = (1.0, 1.0, 1.0, 1.0)
tessface_color, tessface_alpha = tessface_colors[j], tessface_alphas[j]
vertex_color = (int(tessface_color[0] * mult_color[0] * 255),
int(tessface_color[1] * mult_color[1] * 255),
int(tessface_color[2] * mult_color[2] * 255),
int(tessface_alpha * mult_color[0] * 255))
# Now, we'll index into the vertex dict using the per-face elements :(
# We're using tuples because lists are not hashable. The many mathutils and PyHSPlasma
@ -433,7 +498,7 @@ class MeshConverter(_MeshManager):
# Time to finish it up...
for i, data in enumerate(geodata.values()):
geospan = geospans[i][0]
geospan = geospans[i].geospan
numVerts = len(data.vertices)
numUVs = geospan.format & plGeometrySpan.kUVCountMask
@ -454,10 +519,6 @@ class MeshConverter(_MeshManager):
uvMap[numUVs - 1].normalize()
vtx.uvs = uvMap
# Mark us for blending if we have a alpha vertex paint layer
if has_vtx_alpha:
geospan.props |= plGeometrySpan.kRequiresBlending
# If we're still here, let's add our data to the GeometrySpan
geospan.indices = data.triangles
geospan.vertices = data.vertices
@ -540,18 +601,18 @@ class MeshConverter(_MeshManager):
return None
# Step 1: Export all of the doggone materials.
geospans = self._export_material_spans(bo, mesh, materials)
geospans, mat2span_LUT = self._export_material_spans(bo, mesh, materials)
# Step 2: Export Blender mesh data to Plasma GeometrySpans
self._export_geometry(bo, mesh, materials, geospans)
self._export_geometry(bo, mesh, materials, geospans, mat2span_LUT)
# Step 3: Add plGeometrySpans to the appropriate DSpan and create indices
_diindices = {}
for geospan, pass_index in geospans:
dspan = self._find_create_dspan(bo, geospan, pass_index)
for i in geospans:
dspan = self._find_create_dspan(bo, i.geospan, i.pass_index)
self._report.msg("Exported hsGMaterial '{}' geometry into '{}'",
geospan.material.name, dspan.key.name, indent=1)
idx = dspan.addSourceSpan(geospan)
i.geospan.material.name, dspan.key.name, indent=1)
idx = dspan.addSourceSpan(i.geospan)
diidx = _diindices.setdefault(dspan, [])
diidx.append(idx)
@ -571,20 +632,25 @@ class MeshConverter(_MeshManager):
if len(materials) > 1:
msg = "'{}' is a WaveSet -- only one material is supported".format(bo.name)
self._exporter().report.warn(msg, indent=1)
matKey = self.material.export_waveset_material(bo, materials[0][1])
geospan = self._create_geospan(bo, mesh, materials[0][1], matKey)
blmat = materials[0][1]
matKey = self.material.export_waveset_material(bo, blmat)
geospan = self._create_geospan(bo, mesh, None, blmat, matKey)
# FIXME: Can some of this be generalized?
geospan.props |= (plGeometrySpan.kWaterHeight | plGeometrySpan.kLiteVtxNonPreshaded |
plGeometrySpan.kPropReverseSort | plGeometrySpan.kPropNoShadow)
geospan.waterHeight = bo.location[2]
return [(geospan, 0)]
return [_GeoSpan(bo, blmat, geospan)], None
else:
geospans = [None] * len(materials)
for i, (_, blmat) in enumerate(materials):
mat2span_LUT = {}
for i, (blmat_idx, blmat) in enumerate(materials):
matKey = self.material.export_material(bo, blmat)
geospans[i] = (self._create_geospan(bo, mesh, blmat, matKey), blmat.pass_index)
return geospans
geospans[i] = _GeoSpan(bo, blmat,
self._create_geospan(bo, mesh, blmat_idx, blmat, matKey),
blmat.pass_index)
mat2span_LUT[blmat_idx] = i
return geospans, mat2span_LUT
def _find_create_dspan(self, bo, geospan, pass_index):
location = self._mgr.get_location(bo)
@ -620,6 +686,21 @@ class MeshConverter(_MeshManager):
else:
return self._dspans[location][crit]
def _find_vtx_alpha_layer(self, color_collection):
alpha_layer = next((i for i in color_collection if i.name.lower() == "alpha"), None)
if alpha_layer is not None:
return alpha_layer.data
return None
def _find_vtx_color_layer(self, color_collection):
manual_layer = next((i for i in color_collection if i.name.lower() in _VERTEX_COLOR_LAYERS), None)
if manual_layer is not None:
return manual_layer.data
baked_layer = color_collection.get("autocolor")
if baked_layer is not None:
return baked_layer.data
return None
@property
def _mgr(self):
return self._exporter().mgr

Loading…
Cancel
Save