From 25d92fc7f459e1ca27e957421728865a6826a5a1 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Thu, 29 Dec 2016 20:57:48 -0800 Subject: [PATCH 01/15] 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. --- korman/exporter/material.py | 86 +++++++++++++++++++++++++++++++- korman/exporter/mesh.py | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 2 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 6ddcdaf..2cf6208 100644 --- a/korman/exporter/material.py +++ b/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 diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 204806d..18ff54d 100644 --- a/korman/exporter/mesh.py +++ b/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... From 11716fce3b5b9e29934c993bb9af84a130e71854 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Thu, 29 Dec 2016 21:25:18 -0800 Subject: [PATCH 02/15] Match PlasmaMax's BumpMtl options The "strength" of the normal mapping is the red channel of its runtime colour. If the normal map is a specular map, it should be Add blending, otherwise it should use MADD blending. --- korman/exporter/material.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 2cf6208..c252bc3 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -233,9 +233,11 @@ class MaterialConverter: state.blendFlags = hsGMatState.kBlendDot3 state.miscFlags = hsGMatState.kMiscBumpLayer + strength = max(min(1.0, slot.normal_factor), 0.0) + 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.runtime = hsColorRGBA(strength, 0.0, 0.0, 1.0) nm_layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0) texture = slot.texture @@ -260,7 +262,9 @@ class MaterialConverter: layer.state.miscFlags = hsGMatState.kMiscBindNext layer.state.blendFlags = hsGMatState.kBlendAdd - du_layer.state.blendFlags = hsGMatState.kBlendMADD + if not slot.use_map_specular: + 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 From 082eb0e6ca3ed252a1dbb4215b3c60ca55cba158 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 00:36:34 -0800 Subject: [PATCH 03/15] Generate the BumpLutTexture --- korlib/texture.cpp | 85 +++++++++++++++++++++++++++++++++++++ korman/exporter/material.py | 13 +++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/korlib/texture.cpp b/korlib/texture.cpp index af4f0f6..54a5eca 100644 --- a/korlib/texture.cpp +++ b/korlib/texture.cpp @@ -361,6 +361,90 @@ static PyObject* pyGLTexture_store_in_mipmap(pyGLTexture* self, PyObject* args) Py_RETURN_NONE; } + +static uint32_t MakeUInt32Color(float r, float g, float b, float a) +{ + return (uint32_t(a * 255.9f) << 24) + |(uint32_t(r * 255.9f) << 16) + |(uint32_t(g * 255.9f) << 8) + |(uint32_t(b * 255.9f) << 0); +} + +static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) { + const int kLUTHeight = 16; + const int kLUTWidth = 16; + + pyMipmap* pymipmap; + if (!PyArg_ParseTuple(args, "O", &pymipmap)) { + PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); + return NULL; + } + + plMipmap* texture = plMipmap::Convert(pymipmap->fThis, false); + if (!texture) { + PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); + return NULL; + } + + texture->Create(kLUTWidth, kLUTHeight, 1, plBitmap::kUncompressed, plBitmap::kRGB8888); + + int delH = (kLUTHeight - 1) / 5; + int startH = delH / 2 + 1; + int doneH = 0; + + uint32_t* pix = (uint32_t*)const_cast(texture->getImageData()); + int i; + + // Red ramps, one with G,B = 0,0, one with G,B = 127,127 + for (i = 0; i < startH; i++) { + for(int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(x, 0.0f, 0.0f, 1.0f); + } + } + doneH = i; + for (i = i; i < doneH + delH; i++) { + for (int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(x, 0.5f, 0.5f, 1.0f); + } + } + doneH = i; + + // Green ramps, one with R,B = 0,0, one with R,B = 127,127 + for (i = i; i < doneH + delH; i++) { + for (int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.0f, x, 0.0f, 1.0f); + } + } + doneH = i; + for (i = i; i < doneH + delH; i++) { + for (int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.5f, x, 0.5f, 1.0f); + } + } + doneH = i; + + // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 + for (i = i; i < doneH + delH; i++) { + for (int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.0f, 0.0f, x, 1.0f); + } + } + doneH = i; + for (i = i; i < kLUTHeight; i++) { + for (int j = 0; j < kLUTWidth; j++) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.5f, 0.5f, x, 1.0f); + } + } + + Py_RETURN_NONE; +} + static PyMethodDef pyGLTexture_Methods[] = { { _pycs("__enter__"), (PyCFunction)pyGLTexture__enter__, METH_NOARGS, NULL }, { _pycs("__exit__"), (PyCFunction)pyGLTexture__enter__, METH_VARARGS, NULL }, @@ -368,6 +452,7 @@ static PyMethodDef pyGLTexture_Methods[] = { { _pycs("generate_mipmap"), (PyCFunction)pyGLTexture_generate_mipmap, METH_NOARGS, NULL }, { _pycs("get_level_data"), (PyCFunction)pyGLTexture_get_level_data, METH_KEYWORDS | METH_VARARGS, NULL }, { _pycs("store_in_mipmap"), (PyCFunction)pyGLTexture_store_in_mipmap, METH_VARARGS, NULL }, + { _pycs("create_bump_LUT"), (PyCFunction)pyGLTexture_create_bump_LUT, METH_STATIC | METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/korman/exporter/material.py b/korman/exporter/material.py index c252bc3..d2a4b09 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -110,6 +110,7 @@ class MaterialConverter: def __init__(self, exporter): self._obj2mat = {} self._bumpMats = {} + self._bumpLUT = None self._exporter = weakref.ref(exporter) self._pending = {} self._alphatest = {} @@ -274,8 +275,18 @@ class MaterialConverter: dw_layer.UVWSrc = du_uv | plLayerInterface.kUVWNormal dv_layer.UVWSrc = du_uv + 1 - return (du_layer, dw_layer, dv_layer, nm_layer) + if self._bumpLUT is None: + self._bumpLUT = plMipmap("BumpLutTexture") + GLTexture.create_bump_LUT(self._bumpLUT) + + page = self._mgr.get_textures_page(du_layer.key) + self._mgr.AddObject(page, self._bumpLUT) + du_layer.texture = self._bumpLUT.key + dw_layer.texture = self._bumpLUT.key + dv_layer.texture = self._bumpLUT.key + + 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: From ce5ef0d35c7ed28d42c47112c5b8ba6ad0cae11d Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 00:36:58 -0800 Subject: [PATCH 04/15] Generate _korlib with the right name --- korlib/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/korlib/CMakeLists.txt b/korlib/CMakeLists.txt index 265ea13..784bd23 100644 --- a/korlib/CMakeLists.txt +++ b/korlib/CMakeLists.txt @@ -38,6 +38,7 @@ include_directories(${PYTHON_INCLUDE_DIRS}) include_directories(${Vorbis_INCLUDE_DIR}) add_library(_korlib SHARED ${korlib_HEADERS} ${korlib_SOURCES}) +set_target_properties(_korlib PROPERTIES PREFIX "") if(NOT WIN32) set_target_properties(_korlib PROPERTIES SUFFIX ".so") else() From a8c84d0a4574cda26f5b87e97bb949e370fed465 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 01:38:08 -0800 Subject: [PATCH 05/15] Add pure-Python create_bump_LUT function --- korman/exporter/material.py | 2 +- korman/korlib/texture.py | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index d2a4b09..424a21f 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -276,7 +276,7 @@ class MaterialConverter: dv_layer.UVWSrc = du_uv + 1 if self._bumpLUT is None: - self._bumpLUT = plMipmap("BumpLutTexture") + self._bumpLUT = plMipmap("BumpLutTexture", 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888) GLTexture.create_bump_LUT(self._bumpLUT) page = self._mgr.get_textures_page(du_layer.key) diff --git a/korman/korlib/texture.py b/korman/korlib/texture.py index 11a289e..baa30e9 100644 --- a/korman/korlib/texture.py +++ b/korman/korlib/texture.py @@ -186,3 +186,41 @@ class GLTexture: func = mipmap.CompressImage if compression == plBitmap.kDirectXCompression else mipmap.setLevel for i, level in enumerate(data): func(i, level) + + + @staticmethod + def create_bump_LUT(mipmap): + kLUTHeight = 16 + kLUTWidth = 16 + + buf = bytearray(kLUTHeight * kLUTWidth * 4) + + denom = kLUTWidth - 1 + delH = (kLUTHeight - 1) // 5 + startH = delH // 2 + 1 + doneH = 0 + + doneH = startH * kLUTWidth * 4 + buf[0:doneH] = [b for x in range(kLUTWidth) for b in [0, 0, int((x / denom) * 255.9), 255]] * startH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [127, 127, int((x / denom) * 255.9), 255]] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [0, int((x / denom) * 255.9), 0, 255]] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [127, int((x / denom) * 255.9), 127, 255]] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [int((x / denom) * 255.9), 0, 0, 255]] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [int((x / denom) * 255.9), 127, 127, 255]] * startH + + mipmap.setRawImage(bytes(buf)) From 4cf19333986da1a943134d5b03faef139f56301b Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 01:38:43 -0800 Subject: [PATCH 06/15] Avoid normalmap export code duplication --- korman/exporter/material.py | 108 ++++++++++++++---------------------- 1 file changed, 41 insertions(+), 67 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 424a21f..bf8fa0c 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -145,21 +145,22 @@ class MaterialConverter: # Loop over layers for idx, slot in slots: + # Prepend any BumpMapping magic layers if slot.use_map_normal: if bo in self._bumpMats: raise ExportError("Material '{}' has more than one bumpmap layer".format(bm.name)) + du, dw, dv = self.export_bumpmap_slot(bo, bm, hsgmat, slot, idx) + hsgmat.addLayer(du.key) # Du + hsgmat.addLayer(dw.key) # Dw + hsgmat.addLayer(dv.key) # Dv - 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: + if slot.use_stencil: stencils.append((idx, slot)) else: tex_layer = self.export_texture_slot(bo, bm, hsgmat, slot, idx) hsgmat.addLayer(tex_layer.key) + if slot.use_map_normal: + self._bumpMats[bo] = (tex_layer.UVWSrc, tex_layer.transform) if stencils: tex_state = tex_layer.state if not tex_state.blendFlags & hsGMatState.kBlendMask: @@ -206,62 +207,26 @@ 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 - - strength = max(min(1.0, slot.normal_factor), 0.0) - - 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(strength, 0.0, 0.0, 1.0) - 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) - + print(" Exporting Plasma Bumpmap Layers for '{}'".format(name)) # 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]: + 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 + state = layer.state + state.ZFlags = hsGMatState.kZNoZWrite + state.clampFlags = hsGMatState.kClampTexture + state.miscFlags = hsGMatState.kMiscBindNext + state.blendFlags = hsGMatState.kBlendAdd if not slot.use_map_specular: du_layer.state.blendFlags = hsGMatState.kBlendMADD @@ -286,14 +251,14 @@ class MaterialConverter: dw_layer.texture = self._bumpLUT.key dv_layer.texture = self._bumpLUT.key - return (du_layer, dw_layer, dv_layer, nm_layer) + return (du_layer, dw_layer, dv_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) print(" Exporting Plasma Layer '{}'".format(name)) layer = self._mgr.add_object(plLayer, name=name, bl=bo) - if bm is not None: + if bm is not None and not slot.use_map_normal: self._propagate_material_settings(bm, layer) # UVW Channel @@ -312,7 +277,7 @@ class MaterialConverter: xform.setScale(hsVector3(*slot.scale)) layer.transform = xform - wantStencil, canStencil = slot.use_stencil, slot.use_stencil and bm is not None + wantStencil, canStencil = slot.use_stencil, slot.use_stencil and bm is not None and not slot.use_map_normal if wantStencil and not canStencil: self._exporter().report.warn("{} wants to stencil, but this is not a real Material".format(slot.name)) @@ -334,25 +299,34 @@ class MaterialConverter: texture = slot.texture # Apply custom layer properties - layer_props = texture.plasma_layer - layer.opacity = layer_props.opacity / 100 - if layer_props.opacity < 100: - state.blendFlags |= hsGMatState.kBlendAlpha - if layer_props.alpha_halo: - state.blendFlags |= hsGMatState.kBlendAlphaTestHigh - if layer_props.z_bias: - state.ZFlags |= hsGMatState.kZIncLayer - if layer_props.skip_depth_test: - state.ZFlags |= hsGMatState.kZNoZRead - if layer_props.skip_depth_write: - state.ZFlags |= hsGMatState.kZNoZWrite + if slot.use_map_normal: + state.blendFlags = hsGMatState.kBlendDot3 + state.miscFlags = hsGMatState.kMiscBumpLayer + strength = max(min(1.0, slot.normal_factor), 0.0) + layer.ambient = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + layer.preshade = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + layer.runtime = hsColorRGBA(strength, 0.0, 0.0, 1.0) + layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + else: + layer_props = texture.plasma_layer + layer.opacity = layer_props.opacity / 100 + if layer_props.opacity < 100: + state.blendFlags |= hsGMatState.kBlendAlpha + if layer_props.alpha_halo: + state.blendFlags |= hsGMatState.kBlendAlphaTestHigh + if layer_props.z_bias: + state.ZFlags |= hsGMatState.kZIncLayer + if layer_props.skip_depth_test: + state.ZFlags |= hsGMatState.kZNoZRead + if layer_props.skip_depth_write: + state.ZFlags |= hsGMatState.kZNoZWrite # Export the specific texture type self._tex_exporters[texture.type](bo, layer, slot) # Export any layer animations - # NOTE: animated stencils are nonsense. - if not slot.use_stencil: + # NOTE: animated stencils and bumpmaps are nonsense. + if not slot.use_stencil and not slot.use_map_normal: layer = self._export_layer_animations(bo, bm, slot, idx, layer) return layer From 00536bb7cb15a257faf363d592a422481d789070 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 01:44:20 -0800 Subject: [PATCH 07/15] Clean up the easy bits of mesh exporting --- korman/exporter/mesh.py | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 18ff54d..859c3ed 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -300,8 +300,8 @@ class MeshConverter: # 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): + geospan.format += 2 # We dded 2 special bumpmapping UV layers + for v, vtx in enumerate(data.vertices): uvMap = vtx.uvs uvMap[geospan.format - 2].normalize() uvMap[geospan.format - 1].normalize() @@ -326,25 +326,15 @@ class MeshConverter: delta = uv0[notUV] - uv1[notUV] if fabs(delta) < kRealSmall: - if uv0[iUV] - uv1[iUV] < 0: - return v1 - v0 - else: - return v0 - v1 + return v1 - v0 if uv0[iUV] - uv1[iUV] < 0 else v0 - v1 delta = uv2[notUV] - uv1[notUV] if fabs(delta) < kRealSmall: - if uv2[iUV] - uv1[iUV] < 0: - return v1 - v2 - else: - return v2 - v1 + return v1 - v2 if uv2[iUV] - uv1[iUV] < 0 else v2 - v1 delta = uv2[notUV] - uv0[notUV] if fabs(delta) < kRealSmall: - if uv2[iUV] - uv0[iUV] < 0: - return v0 - v2 - else: - return v2 - v0 - + return v0 - v2 if uv2[iUV] - uv0[iUV] < 0 else v2 - v0 # On to the real fun... delta = uv0[notUV] - uv1[notUV] @@ -359,12 +349,7 @@ class MeshConverter: v2Mv1 *= delta v2uv = (uv2[iUV] - uv1[iUV]) * delta - if v0uv > v2uv: - return v0Mv1 - v2Mv1 - else: - return v2Mv1 - v0Mv1 - - + return v0Mv1 - v2Mv1 if v0uv > v2uv else 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 From 2a693a03c25bc0c963606cf56171f42356f03d86 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 19:05:49 -0800 Subject: [PATCH 08/15] More cleanups --- korman/exporter/manager.py | 12 +++++++----- korman/exporter/material.py | 21 +++++++++++---------- korman/exporter/mesh.py | 24 +++++++++++++++--------- korman/korlib/texture.py | 12 ++++++------ 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py index 4541354..e331aa8 100644 --- a/korman/exporter/manager.py +++ b/korman/exporter/manager.py @@ -174,14 +174,16 @@ class ExportManager: key = self.add_object(pl=pClass, name=name, bl=bl, so=so).key return key - def find_key(self, pClass, bl=None, name=None, so=None): + def find_key(self, pClass, bl=None, name=None, so=None, loc=None): """Given a blender Object and a Plasma class, find (or create) an exported plKey""" - assert bl or so + assert loc or bl or so - if so is None: - location = self._pages[bl.plasma_object.page] - else: + if loc is not None: + location = loc + elif so is not None: location = so.key.location + else: + location = self._pages[bl.plasma_object.page] if name is None: if bl is not None: diff --git a/korman/exporter/material.py b/korman/exporter/material.py index bf8fa0c..0dba72e 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -110,7 +110,6 @@ class MaterialConverter: def __init__(self, exporter): self._obj2mat = {} self._bumpMats = {} - self._bumpLUT = None self._exporter = weakref.ref(exporter) self._pending = {} self._alphatest = {} @@ -240,16 +239,18 @@ class MaterialConverter: dw_layer.UVWSrc = du_uv | plLayerInterface.kUVWNormal dv_layer.UVWSrc = du_uv + 1 - if self._bumpLUT is None: - self._bumpLUT = plMipmap("BumpLutTexture", 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888) - GLTexture.create_bump_LUT(self._bumpLUT) + page = self._mgr.get_textures_page(du_layer.key) + LUT_key = self._mgr.find_key(plMipmap, loc=page, name="BumpLutTexture") - page = self._mgr.get_textures_page(du_layer.key) - self._mgr.AddObject(page, self._bumpLUT) + if LUT_key is None: + bumpLUT = plMipmap("BumpLutTexture", 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888) + GLTexture.create_bump_LUT(bumpLUT) + self._mgr.AddObject(page, bumpLUT) + LUT_key = bumpLUT.key - du_layer.texture = self._bumpLUT.key - dw_layer.texture = self._bumpLUT.key - dv_layer.texture = self._bumpLUT.key + du_layer.texture = LUT_key + dw_layer.texture = LUT_key + dv_layer.texture = LUT_key return (du_layer, dw_layer, dv_layer) @@ -673,7 +674,7 @@ class MaterialConverter: def get_materials(self, bo): return self._obj2mat.get(bo, []) - def has_bump_layer(self, bo): + def get_bump_layer(self, bo): return self._bumpMats.get(bo, None) def get_texture_animation_key(self, bo, bm, tex_name=None, tex_slot=None): diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 859c3ed..15cfa52 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -175,7 +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) + has_bumpmap = self.material.get_bump_layer(bo) # Locate relevant vertex color layers now... color, alpha = None, None @@ -225,13 +225,19 @@ class MeshConverter: 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])]) + 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])]) + 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): + 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 @@ -301,7 +307,7 @@ class MeshConverter: # If we're bump mapping, we need to normalize our magic UVW channels if has_bumpmap is not None: geospan.format += 2 # We dded 2 special bumpmapping UV layers - for v, vtx in enumerate(data.vertices): + for vtx in data.vertices: uvMap = vtx.uvs uvMap[geospan.format - 2].normalize() uvMap[geospan.format - 1].normalize() @@ -317,9 +323,9 @@ class MeshConverter: 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] + uv0 = (uvws[0][uvIdx][0], uvws[0][uvIdx][1], 0.0) + uv1 = (uvws[1][uvIdx][0], uvws[1][uvIdx][1], 0.0) + uv2 = (uvws[2][uvIdx][0], uvws[2][uvIdx][1], 0.0) notUV = int(not iUV) kRealSmall = 1.e-6 diff --git a/korman/korlib/texture.py b/korman/korlib/texture.py index baa30e9..2d4e4f7 100644 --- a/korman/korlib/texture.py +++ b/korman/korlib/texture.py @@ -201,26 +201,26 @@ class GLTexture: doneH = 0 doneH = startH * kLUTWidth * 4 - buf[0:doneH] = [b for x in range(kLUTWidth) for b in [0, 0, int((x / denom) * 255.9), 255]] * startH + buf[0:doneH] = [b for x in range(kLUTWidth) for b in (0, 0, int((x / denom) * 255.9), 255)] * startH startH = doneH doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [127, 127, int((x / denom) * 255.9), 255]] * delH + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, 127, int((x / denom) * 255.9), 255)] * delH startH = doneH doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [0, int((x / denom) * 255.9), 0, 255]] * delH + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (0, int((x / denom) * 255.9), 0, 255)] * delH startH = doneH doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [127, int((x / denom) * 255.9), 127, 255]] * delH + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, int((x / denom) * 255.9), 127, 255)] * delH startH = doneH doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [int((x / denom) * 255.9), 0, 0, 255]] * delH + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 0, 0, 255)] * delH startH = doneH doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in [int((x / denom) * 255.9), 127, 127, 255]] * startH + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 127, 127, 255)] * startH mipmap.setRawImage(bytes(buf)) From 3753714cd6d607ad2ad3a4522005288928360a0e Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Fri, 30 Dec 2016 20:39:31 -0800 Subject: [PATCH 09/15] Cleanup the C++ code --- korlib/texture.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/korlib/texture.cpp b/korlib/texture.cpp index 54a5eca..fa3cd4d 100644 --- a/korlib/texture.cpp +++ b/korlib/texture.cpp @@ -364,10 +364,10 @@ static PyObject* pyGLTexture_store_in_mipmap(pyGLTexture* self, PyObject* args) static uint32_t MakeUInt32Color(float r, float g, float b, float a) { - return (uint32_t(a * 255.9f) << 24) - |(uint32_t(r * 255.9f) << 16) - |(uint32_t(g * 255.9f) << 8) - |(uint32_t(b * 255.9f) << 0); + return (uint32_t(a * 255.9f) << 24) | + (uint32_t(r * 255.9f) << 16) | + (uint32_t(g * 255.9f) << 8) | + (uint32_t(b * 255.9f) << 0); } static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) { @@ -392,19 +392,20 @@ static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) int startH = delH / 2 + 1; int doneH = 0; - uint32_t* pix = (uint32_t*)const_cast(texture->getImageData()); + uint8_t* data = new uint8_t[texture->getTotalSize()]; + uint32_t* pix = (uint32_t*)data; int i; // Red ramps, one with G,B = 0,0, one with G,B = 127,127 - for (i = 0; i < startH; i++) { - for(int j = 0; j < kLUTWidth; j++) { + for (i = 0; i < startH; ++i) { + for(int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(x, 0.0f, 0.0f, 1.0f); } } doneH = i; - for (i = i; i < doneH + delH; i++) { - for (int j = 0; j < kLUTWidth; j++) { + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(x, 0.5f, 0.5f, 1.0f); } @@ -412,15 +413,15 @@ static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) doneH = i; // Green ramps, one with R,B = 0,0, one with R,B = 127,127 - for (i = i; i < doneH + delH; i++) { - for (int j = 0; j < kLUTWidth; j++) { + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(0.0f, x, 0.0f, 1.0f); } } doneH = i; - for (i = i; i < doneH + delH; i++) { - for (int j = 0; j < kLUTWidth; j++) { + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(0.5f, x, 0.5f, 1.0f); } @@ -428,20 +429,22 @@ static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) doneH = i; // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 - for (i = i; i < doneH + delH; i++) { - for (int j = 0; j < kLUTWidth; j++) { + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(0.0f, 0.0f, x, 1.0f); } } doneH = i; - for (i = i; i < kLUTHeight; i++) { - for (int j = 0; j < kLUTWidth; j++) { + for (i = i; i < kLUTHeight; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { float x = float(j) / (kLUTWidth - 1); *pix++ = MakeUInt32Color(0.5f, 0.5f, x, 1.0f); } } + texture->setImageData(data, texture->getTotalSize()); + Py_RETURN_NONE; } @@ -449,10 +452,10 @@ static PyMethodDef pyGLTexture_Methods[] = { { _pycs("__enter__"), (PyCFunction)pyGLTexture__enter__, METH_NOARGS, NULL }, { _pycs("__exit__"), (PyCFunction)pyGLTexture__enter__, METH_VARARGS, NULL }, + { _pycs("create_bump_LUT"), (PyCFunction)pyGLTexture_create_bump_LUT, METH_STATIC | METH_VARARGS, NULL }, { _pycs("generate_mipmap"), (PyCFunction)pyGLTexture_generate_mipmap, METH_NOARGS, NULL }, { _pycs("get_level_data"), (PyCFunction)pyGLTexture_get_level_data, METH_KEYWORDS | METH_VARARGS, NULL }, { _pycs("store_in_mipmap"), (PyCFunction)pyGLTexture_store_in_mipmap, METH_VARARGS, NULL }, - { _pycs("create_bump_LUT"), (PyCFunction)pyGLTexture_create_bump_LUT, METH_STATIC | METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; From 887700f472618e85fabedfee328b04be6439507a Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Mon, 13 Feb 2017 21:44:59 -0800 Subject: [PATCH 10/15] Move create_bump_LUT to top level korlib --- korlib/CMakeLists.txt | 2 + korlib/bumpmap.cpp | 113 ++++++++++++++++++++++++++++++++++++ korlib/bumpmap.h | 29 +++++++++ korlib/module.cpp | 2 + korlib/texture.cpp | 88 ---------------------------- korman/exporter/material.py | 2 +- korman/korlib/__init__.py | 36 ++++++++++++ korman/korlib/texture.py | 38 ------------ 8 files changed, 183 insertions(+), 127 deletions(-) create mode 100644 korlib/bumpmap.cpp create mode 100644 korlib/bumpmap.h diff --git a/korlib/CMakeLists.txt b/korlib/CMakeLists.txt index 784bd23..6cbcc30 100644 --- a/korlib/CMakeLists.txt +++ b/korlib/CMakeLists.txt @@ -19,6 +19,7 @@ find_package(Vorbis REQUIRED) # Da files set(korlib_HEADERS buffer.h + bumpmap.h korlib.h sound.h texture.h @@ -26,6 +27,7 @@ set(korlib_HEADERS set(korlib_SOURCES buffer.cpp + bumpmap.cpp module.cpp sound.cpp texture.cpp diff --git a/korlib/bumpmap.cpp b/korlib/bumpmap.cpp new file mode 100644 index 0000000..00ca20f --- /dev/null +++ b/korlib/bumpmap.cpp @@ -0,0 +1,113 @@ +/* This file is part of Korman. + * + * Korman is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Korman is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Korman. If not, see . + */ + +#include "bumpmap.h" +#include + +static uint32_t MakeUInt32Color(float r, float g, float b, float a) { + return (uint32_t(a * 255.9f) << 24) | + (uint32_t(r * 255.9f) << 16) | + (uint32_t(g * 255.9f) << 8) | + (uint32_t(b * 255.9f) << 0); +} + +typedef struct { + PyObject_HEAD + plMipmap* fThis; + bool fPyOwned; +} pyMipmap; + +extern "C" { + +PyObject* create_bump_LUT(PyObject*, PyObject* args) { + const int kLUTHeight = 16; + const int kLUTWidth = 16; + + pyMipmap* pymipmap; + if (!PyArg_ParseTuple(args, "O", &pymipmap)) { + PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); + return NULL; + } + + plMipmap* texture = plMipmap::Convert(pymipmap->fThis, false); + if (!texture) { + PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); + return NULL; + } + + texture->Create(kLUTWidth, kLUTHeight, 1, plBitmap::kUncompressed, plBitmap::kRGB8888); + + int delH = (kLUTHeight - 1) / 5; + int startH = delH / 2 + 1; + int doneH = 0; + + uint8_t* data = new uint8_t[texture->getTotalSize()]; + uint32_t* pix = (uint32_t*)data; + int i; + + // Red ramps, one with G,B = 0,0, one with G,B = 127,127 + for (i = 0; i < startH; ++i) { + for(int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(x, 0.0f, 0.0f, 1.0f); + } + } + doneH = i; + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(x, 0.5f, 0.5f, 1.0f); + } + } + doneH = i; + + // Green ramps, one with R,B = 0,0, one with R,B = 127,127 + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.0f, x, 0.0f, 1.0f); + } + } + doneH = i; + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.5f, x, 0.5f, 1.0f); + } + } + doneH = i; + + // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 + for (i = i; i < doneH + delH; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.0f, 0.0f, x, 1.0f); + } + } + doneH = i; + for (i = i; i < kLUTHeight; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + *pix++ = MakeUInt32Color(0.5f, 0.5f, x, 1.0f); + } + } + + texture->setImageData(data, texture->getTotalSize()); + + Py_RETURN_NONE; +} + +}; diff --git a/korlib/bumpmap.h b/korlib/bumpmap.h new file mode 100644 index 0000000..13a2d9a --- /dev/null +++ b/korlib/bumpmap.h @@ -0,0 +1,29 @@ +/* This file is part of Korman. + * + * Korman is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Korman is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Korman. If not, see . + */ + +#ifndef _KORLIB_BUMPMAP_H +#define _KORLIB_BUMPMAP_H + +#include "korlib.h" + +extern "C" { + +PyObject* create_bump_LUT(PyObject*, PyObject* args); + +}; + +#endif // _KORLIB_BUMPMAP_H + diff --git a/korlib/module.cpp b/korlib/module.cpp index 6ff987c..8850715 100644 --- a/korlib/module.cpp +++ b/korlib/module.cpp @@ -15,12 +15,14 @@ */ #include "buffer.h" +#include "bumpmap.h" #include "sound.h" #include "texture.h" extern "C" { static PyMethodDef korlib_Methods[] = { + { _pycs("create_bump_LUT"), (PyCFunction)create_bump_LUT, METH_VARARGS, NULL }, { _pycs("inspect_vorbisfile"), (PyCFunction)inspect_vorbisfile, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL }, diff --git a/korlib/texture.cpp b/korlib/texture.cpp index fa3cd4d..af4f0f6 100644 --- a/korlib/texture.cpp +++ b/korlib/texture.cpp @@ -361,98 +361,10 @@ static PyObject* pyGLTexture_store_in_mipmap(pyGLTexture* self, PyObject* args) Py_RETURN_NONE; } - -static uint32_t MakeUInt32Color(float r, float g, float b, float a) -{ - return (uint32_t(a * 255.9f) << 24) | - (uint32_t(r * 255.9f) << 16) | - (uint32_t(g * 255.9f) << 8) | - (uint32_t(b * 255.9f) << 0); -} - -static PyObject* pyGLTexture_create_bump_LUT(pyGLTexture* self, PyObject* args) { - const int kLUTHeight = 16; - const int kLUTWidth = 16; - - pyMipmap* pymipmap; - if (!PyArg_ParseTuple(args, "O", &pymipmap)) { - PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); - return NULL; - } - - plMipmap* texture = plMipmap::Convert(pymipmap->fThis, false); - if (!texture) { - PyErr_SetString(PyExc_TypeError, "create_bump_LUT expects a plMipmap"); - return NULL; - } - - texture->Create(kLUTWidth, kLUTHeight, 1, plBitmap::kUncompressed, plBitmap::kRGB8888); - - int delH = (kLUTHeight - 1) / 5; - int startH = delH / 2 + 1; - int doneH = 0; - - uint8_t* data = new uint8_t[texture->getTotalSize()]; - uint32_t* pix = (uint32_t*)data; - int i; - - // Red ramps, one with G,B = 0,0, one with G,B = 127,127 - for (i = 0; i < startH; ++i) { - for(int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(x, 0.0f, 0.0f, 1.0f); - } - } - doneH = i; - for (i = i; i < doneH + delH; ++i) { - for (int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(x, 0.5f, 0.5f, 1.0f); - } - } - doneH = i; - - // Green ramps, one with R,B = 0,0, one with R,B = 127,127 - for (i = i; i < doneH + delH; ++i) { - for (int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(0.0f, x, 0.0f, 1.0f); - } - } - doneH = i; - for (i = i; i < doneH + delH; ++i) { - for (int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(0.5f, x, 0.5f, 1.0f); - } - } - doneH = i; - - // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 - for (i = i; i < doneH + delH; ++i) { - for (int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(0.0f, 0.0f, x, 1.0f); - } - } - doneH = i; - for (i = i; i < kLUTHeight; ++i) { - for (int j = 0; j < kLUTWidth; ++j) { - float x = float(j) / (kLUTWidth - 1); - *pix++ = MakeUInt32Color(0.5f, 0.5f, x, 1.0f); - } - } - - texture->setImageData(data, texture->getTotalSize()); - - Py_RETURN_NONE; -} - static PyMethodDef pyGLTexture_Methods[] = { { _pycs("__enter__"), (PyCFunction)pyGLTexture__enter__, METH_NOARGS, NULL }, { _pycs("__exit__"), (PyCFunction)pyGLTexture__enter__, METH_VARARGS, NULL }, - { _pycs("create_bump_LUT"), (PyCFunction)pyGLTexture_create_bump_LUT, METH_STATIC | METH_VARARGS, NULL }, { _pycs("generate_mipmap"), (PyCFunction)pyGLTexture_generate_mipmap, METH_NOARGS, NULL }, { _pycs("get_level_data"), (PyCFunction)pyGLTexture_get_level_data, METH_KEYWORDS | METH_VARARGS, NULL }, { _pycs("store_in_mipmap"), (PyCFunction)pyGLTexture_store_in_mipmap, METH_VARARGS, NULL }, diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 0dba72e..5ab8a2c 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -244,7 +244,7 @@ class MaterialConverter: if LUT_key is None: bumpLUT = plMipmap("BumpLutTexture", 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888) - GLTexture.create_bump_LUT(bumpLUT) + create_bump_LUT(bumpLUT) self._mgr.AddObject(page, bumpLUT) LUT_key = bumpLUT.key diff --git a/korman/korlib/__init__.py b/korman/korlib/__init__.py index 9a4c22d..77b9457 100644 --- a/korman/korlib/__init__.py +++ b/korman/korlib/__init__.py @@ -18,6 +18,42 @@ try: except ImportError: from .texture import * + def create_bump_LUT(mipmap): + kLUTHeight = 16 + kLUTWidth = 16 + + buf = bytearray(kLUTHeight * kLUTWidth * 4) + + denom = kLUTWidth - 1 + delH = (kLUTHeight - 1) // 5 + startH = delH // 2 + 1 + doneH = 0 + + doneH = startH * kLUTWidth * 4 + buf[0:doneH] = [b for x in range(kLUTWidth) for b in (0, 0, int((x / denom) * 255.9), 255)] * startH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, 127, int((x / denom) * 255.9), 255)] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (0, int((x / denom) * 255.9), 0, 255)] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, int((x / denom) * 255.9), 127, 255)] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 0, 0, 255)] * delH + + startH = doneH + doneH += delH * kLUTWidth * 4 + buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 127, 127, 255)] * startH + + mipmap.setRawImage(bytes(buf)) + def inspect_voribsfile(stream, header): raise NotImplementedError("Ogg Vorbis not supported unless _korlib is compiled") diff --git a/korman/korlib/texture.py b/korman/korlib/texture.py index 2d4e4f7..11a289e 100644 --- a/korman/korlib/texture.py +++ b/korman/korlib/texture.py @@ -186,41 +186,3 @@ class GLTexture: func = mipmap.CompressImage if compression == plBitmap.kDirectXCompression else mipmap.setLevel for i, level in enumerate(data): func(i, level) - - - @staticmethod - def create_bump_LUT(mipmap): - kLUTHeight = 16 - kLUTWidth = 16 - - buf = bytearray(kLUTHeight * kLUTWidth * 4) - - denom = kLUTWidth - 1 - delH = (kLUTHeight - 1) // 5 - startH = delH // 2 + 1 - doneH = 0 - - doneH = startH * kLUTWidth * 4 - buf[0:doneH] = [b for x in range(kLUTWidth) for b in (0, 0, int((x / denom) * 255.9), 255)] * startH - - startH = doneH - doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, 127, int((x / denom) * 255.9), 255)] * delH - - startH = doneH - doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (0, int((x / denom) * 255.9), 0, 255)] * delH - - startH = doneH - doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (127, int((x / denom) * 255.9), 127, 255)] * delH - - startH = doneH - doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 0, 0, 255)] * delH - - startH = doneH - doneH += delH * kLUTWidth * 4 - buf[startH:doneH] = [b for x in range(kLUTWidth) for b in (int((x / denom) * 255.9), 127, 127, 255)] * startH - - mipmap.setRawImage(bytes(buf)) From b1dc8a7dfe66699b90f71e064710e4c71006f2b2 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Mon, 13 Feb 2017 21:45:31 -0800 Subject: [PATCH 11/15] Fix for compiling with Python 3.6 Ported from libHSPlasma --- korlib/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/korlib/CMakeLists.txt b/korlib/CMakeLists.txt index 6cbcc30..0b05775 100644 --- a/korlib/CMakeLists.txt +++ b/korlib/CMakeLists.txt @@ -5,10 +5,13 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # Stolen shamelessly from PyHSPlasma find_package(PythonLibs REQUIRED) -find_package(PythonInterp "${PYTHONLIBS_VERSION_STRING}" REQUIRED) +# Use only the major.minor version -- no patch, no +, etc +STRING(REGEX REPLACE "([0-9]\\.[0-9])[0-9.+]*" "\\1" PYTHONLIBS_VERSION_STRING_FILTERED "${PYTHONLIBS_VERSION_STRING}") +find_package(PythonInterp "${PYTHONLIBS_VERSION_STRING_FILTERED}" REQUIRED) # make sure the versions match -if (NOT "${PYTHONLIBS_VERSION_STRING}" STREQUAL "${PYTHON_VERSION_STRING}") - message(FATAL_ERROR "Versions of Python libraries (${PYTHONLIBS_VERSION_STRING}) and Python interpreter (${PYTHON_VERSION_STRING}) do not match. Please configure the paths manually.") +STRING(REGEX REPLACE "([0-9]\\.[0-9])[0-9.+]*" "\\1" PYTHON_VERSION_STRING_FILTERED "${PYTHON_VERSION_STRING}") +if (NOT "${PYTHONLIBS_VERSION_STRING_FILTERED}" STREQUAL "${PYTHON_VERSION_STRING_FILTERED}") + message(FATAL_ERROR "Versions of Python libraries (${PYTHONLIBS_VERSION_STRING_FILTERED}) and Python interpreter (${PYTHON_VERSION_STRING_FILTERED}) do not match. Please configure the paths manually.") endif() find_package(HSPlasma REQUIRED) From fc4229d4c411979c2f1438d6e3ae9d3e341c1721 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Mon, 13 Feb 2017 21:45:51 -0800 Subject: [PATCH 12/15] Force layers after a bumpmap to their own pass --- korman/exporter/material.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 5ab8a2c..5ac85d3 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -141,6 +141,7 @@ class MaterialConverter: if num_stencils > _MAX_STENCILS: raise ExportError("Material '{}' uses too many stencils. The maximum is {}".format(bm.name, _MAX_STENCILS)) stencils = [] + restart_pass_next = False # Loop over layers for idx, slot in slots: @@ -157,9 +158,15 @@ class MaterialConverter: stencils.append((idx, slot)) else: tex_layer = self.export_texture_slot(bo, bm, hsgmat, slot, idx) + if restart_pass_next: + tex_layer.state.miscFlags |= hsGMatState.kMiscRestartPassHere + restart_pass_next = False hsgmat.addLayer(tex_layer.key) if slot.use_map_normal: self._bumpMats[bo] = (tex_layer.UVWSrc, tex_layer.transform) + # After a bumpmap layer(s), the next layer *must* be in a + # new pass, otherwise it gets added in non-intuitive ways + restart_pass_next = True if stencils: tex_state = tex_layer.state if not tex_state.blendFlags & hsGMatState.kBlendMask: From c52335fb475306d9041df3b21a627665958bbfab Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 8 Jun 2017 22:58:50 -0400 Subject: [PATCH 13/15] Don't preempt UVChannel sanity checks in bumpmaps This makes UV channel calculations and errors a little bit more sane. --- korman/exporter/explosions.py | 6 +++--- korman/exporter/mesh.py | 32 +++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/korman/exporter/explosions.py b/korman/exporter/explosions.py index 715680b..331c173 100644 --- a/korman/exporter/explosions.py +++ b/korman/exporter/explosions.py @@ -29,9 +29,9 @@ class ExportAssertionError(ExportError): class TooManyUVChannelsError(ExportError): - def __init__(self, obj, mat): - msg = "There are too many UV Textures on the material '{}' associated with object '{}'.".format( - mat.name, obj.name) + def __init__(self, obj, mat, numUVTexs, maxUVTexCount=8): + msg = "There are too many UV Textures on the material '{}' associated with object '{}'. You can have at most {} (there are {})".format( + mat.name, obj.name, maxUVTexCount, numUVTexs) super(ExportError, self).__init__(msg) diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 15cfa52..dce9a49 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -119,6 +119,24 @@ class MeshConverter: self._dspans = {} self._mesh_geospans = {} + def _calc_num_uvchans(self, bo, mesh): + max_user_texs = plGeometrySpan.kUVCountMask + num_user_texs = len(mesh.tessface_uv_textures) + total_texs = num_user_texs + + # Bump Mapping requires 2 magic channels + if self.material.get_bump_layer(bo) is not None: + total_texs += 2 + max_user_texs -= 2 + + # Lightmapping requires its own LIGHTMAPGEN channel + # NOTE: the LIGHTMAPGEN texture has already been created, so it is in num_user_texs + if bo.plasma_modifiers.lightmap.enabled: + num_user_texs -= 1 + max_user_texs -= 1 + + return (num_user_texs, total_texs, max_user_texs) + def _create_geospan(self, bo, mesh, bm, hsgmatKey): """Initializes a plGeometrySpan from a Blender Object and an hsGMaterial""" geospan = plGeometrySpan() @@ -126,10 +144,10 @@ class MeshConverter: # GeometrySpan format # For now, we really only care about the number of UVW Channels - numUVWchans = len(mesh.tessface_uv_textures) - if numUVWchans > plGeometrySpan.kUVCountMask: - raise explosions.TooManyUVChannelsError(bo, bm) - geospan.format = numUVWchans + user_uvws, total_uvws, max_user_uvws = self._calc_num_uvchans(bo, mesh) + if total_uvws > plGeometrySpan.kUVCountMask: + raise explosions.TooManyUVChannelsError(bo, bm, user_uvws, max_user_uvws) + geospan.format = total_uvws # Begin total guesswork WRT flags mods = bo.plasma_modifiers @@ -294,6 +312,7 @@ class MeshConverter: for i, data in enumerate(geodata): geospan = geospans[i][0] numVerts = len(data.vertices) + numUVs = geospan.format & plGeometrySpan.kUVCountMask # There is a soft limit of 0x8000 vertices per span in Plasma, but the limit is # theoretically 0xFFFF because this field is a 16-bit integer. However, bad things @@ -306,11 +325,10 @@ class MeshConverter: # If we're bump mapping, we need to normalize our magic UVW channels if has_bumpmap is not None: - geospan.format += 2 # We dded 2 special bumpmapping UV layers for vtx in data.vertices: uvMap = vtx.uvs - uvMap[geospan.format - 2].normalize() - uvMap[geospan.format - 1].normalize() + uvMap[numUVs - 2].normalize() + uvMap[numUVs - 1].normalize() vtx.uvs = uvMap # If we're still here, let's add our data to the GeometrySpan From 64369b836dbb2e90f4fcf071a9ffa3723a90a323 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 8 Jun 2017 23:04:37 -0400 Subject: [PATCH 14/15] Update some bumpmap variables to obey The Zen --- korman/exporter/material.py | 8 ++++---- korman/exporter/mesh.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 5ac85d3..3a792af 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -109,7 +109,7 @@ class _Texture: class MaterialConverter: def __init__(self, exporter): self._obj2mat = {} - self._bumpMats = {} + self._bump_mats = {} self._exporter = weakref.ref(exporter) self._pending = {} self._alphatest = {} @@ -147,7 +147,7 @@ class MaterialConverter: for idx, slot in slots: # Prepend any BumpMapping magic layers if slot.use_map_normal: - if bo in self._bumpMats: + if bo in self._bump_mats: raise ExportError("Material '{}' has more than one bumpmap layer".format(bm.name)) du, dw, dv = self.export_bumpmap_slot(bo, bm, hsgmat, slot, idx) hsgmat.addLayer(du.key) # Du @@ -163,7 +163,7 @@ class MaterialConverter: restart_pass_next = False hsgmat.addLayer(tex_layer.key) if slot.use_map_normal: - self._bumpMats[bo] = (tex_layer.UVWSrc, tex_layer.transform) + self._bump_mats[bo] = (tex_layer.UVWSrc, tex_layer.transform) # After a bumpmap layer(s), the next layer *must* be in a # new pass, otherwise it gets added in non-intuitive ways restart_pass_next = True @@ -682,7 +682,7 @@ class MaterialConverter: return self._obj2mat.get(bo, []) def get_bump_layer(self, bo): - return self._bumpMats.get(bo, None) + return self._bump_mats.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""" diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index dce9a49..8c75398 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -193,7 +193,7 @@ class MeshConverter: def _export_geometry(self, bo, mesh, materials, geospans): geodata = [_GeoData(len(mesh.vertices)) for i in materials] - has_bumpmap = self.material.get_bump_layer(bo) + bumpmap = self.material.get_bump_layer(bo) # Locate relevant vertex color layers now... color, alpha = None, None @@ -236,7 +236,7 @@ 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: + if bumpmap is not None: gradPass = [] gradUVWs = [] @@ -256,8 +256,8 @@ class MeshConverter: 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) + dPosDu += self._get_bump_gradient(bumpmap[1], gradUVWs[p], mesh, vids, bumpmap[0], 0) + dPosDv += self._get_bump_gradient(bumpmap[1], gradUVWs[p], mesh, vids, bumpmap[0], 1) dPosDv = -dPosDv # Convert to per-material indices @@ -292,7 +292,7 @@ class MeshConverter: face_verts.append(data.blender2gs[vertex][coluv]) - if has_bumpmap is not None: + if bumpmap is not None: idx = len(uvws) geoVert = data.vertices[data.blender2gs[vertex][coluv]] # We can't edit in place :\ @@ -324,7 +324,7 @@ class MeshConverter: 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: + if bumpmap is not None: for vtx in data.vertices: uvMap = vtx.uvs uvMap[numUVs - 2].normalize() @@ -346,18 +346,18 @@ class MeshConverter: uv2 = (uvws[2][uvIdx][0], uvws[2][uvIdx][1], 0.0) notUV = int(not iUV) - kRealSmall = 1.e-6 + _REAL_SMALL = 0.000001 delta = uv0[notUV] - uv1[notUV] - if fabs(delta) < kRealSmall: + if fabs(delta) < _REAL_SMALL: return v1 - v0 if uv0[iUV] - uv1[iUV] < 0 else v0 - v1 delta = uv2[notUV] - uv1[notUV] - if fabs(delta) < kRealSmall: + if fabs(delta) < _REAL_SMALL: return v1 - v2 if uv2[iUV] - uv1[iUV] < 0 else v2 - v1 delta = uv2[notUV] - uv0[notUV] - if fabs(delta) < kRealSmall: + if fabs(delta) < _REAL_SMALL: return v0 - v2 if uv2[iUV] - uv0[iUV] < 0 else v2 - v0 # On to the real fun... From 08d93dd7970f063297436378e7a5e9a319e13dab Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 9 Jun 2017 16:07:09 -0400 Subject: [PATCH 15/15] Update GeoVertex UV touching for HSPlasma changes plGeometrySpan.TempVertex.uvs now returns an immutable tuple, so we have to convert that to a list. Furthermore, I took the liberty of changing to code to avoid spin-washing on the first encounter of a bumpmapped vertex. --- korman/exporter/mesh.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 8c75398..98fb63c 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -285,21 +285,31 @@ class MeshConverter: geoVertex.normal = hsVector3(*tessface.normal) 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) + uvs = [hsVector3(uv[0], 1.0 - uv[1], 0.0) for uv in uvws] + if bumpmap is not None: + uvs.append(dPosDu) + uvs.append(dPosDv) + geoVertex.uvs = uvs + + idx = len(data.vertices) + data.blender2gs[vertex][coluv] = idx data.vertices.append(geoVertex) - - face_verts.append(data.blender2gs[vertex][coluv]) - - if 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 + face_verts.append(idx) + else: + # If we have a bump mapping layer, then we need to add the bump gradients for + # this face to the vertex's magic channels + if bumpmap is not None: + num_user_uvs = len(uvws) + geoVertex = data.vertices[data.blender2gs[vertex][coluv]] + + # Unfortunately, PyHSPlasma returns a copy of everything. Previously, editing + # in place would result in silent failures; however, as of python_refactor, + # PyHSPlasma now returns tuples to indicate this. + geoUVs = list(geoVertex.uvs) + geoUVs[num_user_uvs] += dPosDu + geoUVs[num_user_uvs+1] += dPosDv + geoVertex.uvs = geoUVs + face_verts.append(data.blender2gs[vertex][coluv]) # Convert to triangles, if need be... if len(face_verts) == 3: