Browse Source

Initial implementation of bumpmapping

A few unfinished bits:

1.  This doesn't properly handle multi-material objects, which are
    permitted to have separate bumpmaps on each material.

2.  This doesn't generate the BumpLutRamp image for the Du, Dv, Dw
    layers.
pull/50/head
Darryl Pogue 7 years ago
parent
commit
25d92fc7f4
No known key found for this signature in database
GPG Key ID: CB824715C3E6FD41
  1. 86
      korman/exporter/material.py
  2. 97
      korman/exporter/mesh.py

86
korman/exporter/material.py

@ -109,6 +109,7 @@ class _Texture:
class MaterialConverter:
def __init__(self, exporter):
self._obj2mat = {}
self._bumpMats = {}
self._exporter = weakref.ref(exporter)
self._pending = {}
self._alphatest = {}
@ -143,7 +144,17 @@ class MaterialConverter:
# Loop over layers
for idx, slot in slots:
if slot.use_stencil:
if slot.use_map_normal:
if bo in self._bumpMats:
raise ExportError("Material '{}' has more than one bumpmap layer".format(bm.name))
bump_layers = self.export_bumpmap_slot(bo, bm, hsgmat, slot, idx)
hsgmat.addLayer(bump_layers[0].key) # Du
hsgmat.addLayer(bump_layers[1].key) # Dw
hsgmat.addLayer(bump_layers[2].key) # Dv
hsgmat.addLayer(bump_layers[3].key) # Normal map
self._bumpMats[bo] = (bump_layers[3].UVWSrc, bump_layers[3].transform)
elif slot.use_stencil:
stencils.append((idx, slot))
else:
tex_layer = self.export_texture_slot(bo, bm, hsgmat, slot, idx)
@ -194,6 +205,74 @@ class MaterialConverter:
# Wasn't that easy?
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)
print(" Exporting Plasma Bumpmap Layer '{}'".format(name))
# The normal map layer
nm_layer = self._mgr.add_object(plLayer, name=name, bl=bo)
# UVW Channel
if slot.texture_coords == "UV":
for i, uvchan in enumerate(bo.data.uv_layers):
if uvchan.name == slot.uv_layer:
nm_layer.UVWSrc = i
print(" Using UV Map #{} '{}'".format(i, name))
break
else:
print(" No UVMap specified... Blindly using the first one, maybe it exists :|")
# Transform
xform = hsMatrix44()
xform.setTranslate(hsVector3(*slot.offset))
xform.setScale(hsVector3(*slot.scale))
nm_layer.transform = xform
state = nm_layer.state
state.blendFlags = hsGMatState.kBlendDot3
state.miscFlags = hsGMatState.kMiscBumpLayer
nm_layer.ambient = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
nm_layer.preshade = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
nm_layer.runtime = hsColorRGBA(1.0, 0.0, 0.0, 1.0) # Solid Red
nm_layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
texture = slot.texture
# Export the specific texture type
self._tex_exporters[texture.type](bo, nm_layer, slot)
# 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)
for layer in [du_layer, dw_layer, dv_layer]:
layer.ambient = hsColorRGBA(1.0, 1.0, 1.0, 1.0)
layer.preshade = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
layer.runtime = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
layer.state.ZFlags = hsGMatState.kZNoZWrite
layer.state.clampFlags = hsGMatState.kClampTexture
layer.state.miscFlags = hsGMatState.kMiscBindNext
layer.state.blendFlags = hsGMatState.kBlendAdd
du_layer.state.blendFlags = hsGMatState.kBlendMADD
du_layer.state.miscFlags |= hsGMatState.kMiscBumpDu | hsGMatState.kMiscRestartPassHere
dw_layer.state.miscFlags |= hsGMatState.kMiscBumpDw
dv_layer.state.miscFlags |= hsGMatState.kMiscBumpDv
du_uv = len(bo.data.uv_layers)
du_layer.UVWSrc = du_uv
dw_layer.UVWSrc = du_uv | plLayerInterface.kUVWNormal
dv_layer.UVWSrc = du_uv + 1
return (du_layer, dw_layer, dv_layer, nm_layer)
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)
@ -476,7 +555,7 @@ class MaterialConverter:
# First, let's apply any relevant flags
state = layer.state
if not slot.use_stencil:
if not slot.use_stencil and not slot.use_map_normal:
# mutually exclusive blend flags
if texture.use_alpha and has_alpha:
if slot.blend_type == "ADD":
@ -605,6 +684,9 @@ class MaterialConverter:
def get_materials(self, bo):
return self._obj2mat.get(bo, [])
def has_bump_layer(self, bo):
return self._bumpMats.get(bo, None)
def get_texture_animation_key(self, bo, bm, tex_name=None, tex_slot=None):
"""Finds or creates the appropriate key for sending messages to an animated Texture"""
assert tex_name or tex_slot

97
korman/exporter/mesh.py

@ -15,6 +15,7 @@
import bpy
from PyHSPlasma import *
from math import fabs
import weakref
from . import explosions
@ -174,6 +175,7 @@ class MeshConverter:
def _export_geometry(self, bo, mesh, materials, geospans):
geodata = [_GeoData(len(mesh.vertices)) for i in materials]
has_bumpmap = self.material.has_bump_layer(bo)
# Locate relevant vertex color layers now...
color, alpha = None, None
@ -191,6 +193,8 @@ class MeshConverter:
data = geodata[tessface.material_index]
face_verts = []
use_smooth = tessface.use_smooth
dPosDu = hsVector3(0.0, 0.0, 0.0)
dPosDv = hsVector3(0.0, 0.0, 0.0)
# Unpack the UV coordinates from each UV Texture layer
# NOTE: Blender has no third (W) coordinate
@ -214,6 +218,24 @@ class MeshConverter:
((src.color3[0] + src.color3[1] + src.color3[2]) / 3),
((src.color4[0] + src.color4[1] + src.color4[2]) / 3))
if has_bumpmap is not None:
gradPass = []
gradUVWs = []
if len(tessface.vertices) != 3:
gradPass.append([tessface.vertices[0], tessface.vertices[1], tessface.vertices[2]])
gradPass.append([tessface.vertices[0], tessface.vertices[2], tessface.vertices[3]])
gradUVWs.append([tuple([uvw[0] for uvw in tessface_uvws]), tuple([uvw[1] for uvw in tessface_uvws]), tuple([uvw[2] for uvw in tessface_uvws])])
gradUVWs.append([tuple([uvw[0] for uvw in tessface_uvws]), tuple([uvw[2] for uvw in tessface_uvws]), tuple([uvw[3] for uvw in tessface_uvws])])
else:
gradPass.append(tessface.vertices)
gradUVWs.append([tuple([uvw[0] for uvw in tessface_uvws]), tuple([uvw[1] for uvw in tessface_uvws]), tuple([uvw[2] for uvw in tessface_uvws])])
for p,vids in enumerate(gradPass):
dPosDu += self._get_bump_gradient(has_bumpmap[1], gradUVWs[p], mesh, vids, has_bumpmap[0], 0)
dPosDv += self._get_bump_gradient(has_bumpmap[1], gradUVWs[p], mesh, vids, has_bumpmap[0], 1)
dPosDv = -dPosDv
# Convert to per-material indices
for j, vertex in enumerate(tessface.vertices):
uvws = tuple([uvw[j] for uvw in tessface_uvws])
@ -240,10 +262,21 @@ class MeshConverter:
geoVertex.color = hsColor32(*vertex_color)
geoVertex.uvs = [hsVector3(uv[0], 1.0 - uv[1], 0.0) for uv in uvws]
data.blender2gs[vertex][coluv] = len(data.vertices)
data.vertices.append(geoVertex)
face_verts.append(data.blender2gs[vertex][coluv])
if has_bumpmap is not None:
idx = len(uvws)
geoVert = data.vertices[data.blender2gs[vertex][coluv]]
# We can't edit in place :\
uvMaps = geoVert.uvs
uvMaps[idx] += dPosDu
uvMaps[idx + 1] += dPosDv
geoVert.uvs = uvMaps
# Convert to triangles, if need be...
if len(face_verts) == 3:
data.triangles += face_verts
@ -265,10 +298,74 @@ class MeshConverter:
if numVerts > _WARN_VERTS_PER_SPAN:
raise explosions.TooManyVerticesError(bo.data.name, geospan.material.name, numVerts)
# If we're bump mapping, we need to normalize our magic UVW channels
if has_bumpmap is not None:
geospan.format += 2
for v,vtx in enumerate(data.vertices):
uvMap = vtx.uvs
uvMap[geospan.format - 2].normalize()
uvMap[geospan.format - 1].normalize()
vtx.uvs = uvMap
# If we're still here, let's add our data to the GeometrySpan
geospan.indices = data.triangles
geospan.vertices = data.vertices
def _get_bump_gradient(self, xform, uvws, mesh, vIds, uvIdx, iUV):
v0 = hsVector3(*mesh.vertices[vIds[0]].co)
v1 = hsVector3(*mesh.vertices[vIds[1]].co)
v2 = hsVector3(*mesh.vertices[vIds[2]].co)
uv0 = [(uv[0], uv[1], 0.0) for uv in uvws[0]][uvIdx]
uv1 = [(uv[0], uv[1], 0.0) for uv in uvws[1]][uvIdx]
uv2 = [(uv[0], uv[1], 0.0) for uv in uvws[2]][uvIdx]
notUV = int(not iUV)
kRealSmall = 1.e-6
delta = uv0[notUV] - uv1[notUV]
if fabs(delta) < kRealSmall:
if uv0[iUV] - uv1[iUV] < 0:
return v1 - v0
else:
return v0 - v1
delta = uv2[notUV] - uv1[notUV]
if fabs(delta) < kRealSmall:
if uv2[iUV] - uv1[iUV] < 0:
return v1 - v2
else:
return v2 - v1
delta = uv2[notUV] - uv0[notUV]
if fabs(delta) < kRealSmall:
if uv2[iUV] - uv0[iUV] < 0:
return v0 - v2
else:
return v2 - v0
# On to the real fun...
delta = uv0[notUV] - uv1[notUV]
delta = 1.0 / delta
v0Mv1 = v0 - v1
v0Mv1 *= delta
v0uv = (uv0[iUV] - uv1[iUV]) * delta
delta = uv2[notUV] - uv1[notUV]
delta = 1.0 / delta
v2Mv1 = v2 - v1
v2Mv1 *= delta
v2uv = (uv2[iUV] - uv1[iUV]) * delta
if v0uv > v2uv:
return v0Mv1 - v2Mv1
else:
return v2Mv1 - v0Mv1
def export_object(self, bo):
# If this object has modifiers, then it's a unique mesh, and we don't need to try caching it
# Otherwise, let's *try* to share meshes as best we can...

Loading…
Cancel
Save