From 553481f6c0e5daf7a386195eee855959f7a8082a Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Wed, 28 Apr 2021 17:38:34 -0700 Subject: [PATCH] Add Funky Blending layer support --- korlib/bumpmap.cpp | 41 ++++++++++++++ korlib/bumpmap.h | 1 + korlib/module.cpp | 1 + korman/exporter/material.py | 92 +++++++++++++++++++++++++++++++ korman/korlib/__init__.py | 22 ++++++++ korman/properties/prop_texture.py | 21 +++++++ korman/ui/ui_texture.py | 23 ++++++++ 7 files changed, 201 insertions(+) diff --git a/korlib/bumpmap.cpp b/korlib/bumpmap.cpp index 33cd6be..008c7a8 100644 --- a/korlib/bumpmap.cpp +++ b/korlib/bumpmap.cpp @@ -26,6 +26,47 @@ static uint32_t MakeUInt32Color(float r, float g, float b, float a) { (uint32_t(b * 255.9f) << 0); } +PyObject* create_funky_ramp(PyObject*, PyObject* args) { + static const int kLUTHeight = 16; + static const int kLUTWidth = 16; + static const int kBufSz = kLUTWidth * kLUTHeight * sizeof(uint32_t); + + pyMipmap* pymipmap; + bool additive = false; + if (!PyArg_ParseTuple(args, "O|b", &pymipmap, &additive)) { + PyErr_SetString(PyExc_TypeError, "create_funky_ramp expects a plMipmap and an optional boolean"); + return NULL; + } + + plMipmap* texture = plMipmap::Convert(pymipmap->fThis, false); + if (!texture) { + PyErr_SetString(PyExc_TypeError, "create_funky_ramp expects a plMipmap"); + return NULL; + } + + texture->Create(kLUTWidth, kLUTHeight, 1, plBitmap::kUncompressed, plBitmap::kRGB8888); + + uint8_t data[kBufSz]; + uint32_t* pix = (uint32_t*)data; + + for (int i = 0; i < kLUTHeight; ++i) { + for (int j = 0; j < kLUTWidth; ++j) { + float x = float(j) / (kLUTWidth - 1); + float y = float(i) / (kLUTHeight - 1); + + if (additive) { + *pix++ = MakeUInt32Color(1.0f, 1.0f, 1.0f, std::max(x, y)); + } else { + *pix++ = MakeUInt32Color(1.0f, 1.0f, 1.0f, x * y); + } + } + } + + texture->setImageData(data, kBufSz); + + Py_RETURN_NONE; +} + PyObject* create_bump_LUT(PyObject*, PyObject* args) { static const int kLUTHeight = 16; static const int kLUTWidth = 16; diff --git a/korlib/bumpmap.h b/korlib/bumpmap.h index b950faf..30e240a 100644 --- a/korlib/bumpmap.h +++ b/korlib/bumpmap.h @@ -19,6 +19,7 @@ #include "korlib.h" +PyObject* create_funky_ramp(PyObject*, PyObject* args); PyObject* create_bump_LUT(PyObject*, PyObject* args); #endif // _KORLIB_BUMPMAP_H diff --git a/korlib/module.cpp b/korlib/module.cpp index fd291ce..578b69a 100644 --- a/korlib/module.cpp +++ b/korlib/module.cpp @@ -22,6 +22,7 @@ #define KORLIB_API_VERSION 2 static PyMethodDef korlib_Methods[] = { + { _pycs("create_funky_ramp"), (PyCFunction)create_funky_ramp, METH_VARARGS, NULL }, { _pycs("create_bump_LUT"), (PyCFunction)create_bump_LUT, METH_VARARGS, NULL }, { _pycs("inspect_vorbisfile"), (PyCFunction)inspect_vorbisfile, METH_VARARGS, NULL }, { _pycs("scale_image"), (PyCFunction)scale_image, METH_KEYWORDS | METH_VARARGS, NULL }, diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 997bbc1..5fcd24f 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -253,6 +253,11 @@ class MaterialConverter: if i+1 < curr_stencils: stencil_layer.state.miscFlags |= hsGMatState.kMiscBindNext hsgmat.addLayer(stencil_layer.key) + if slot.texture.plasma_layer.funky_type != "FunkyNone": + funky_ramp = self._export_funky_slot(bo, bm, hsgmat, slot, idx) + if funky_ramp: + tex_layer.state.miscFlags |= hsGMatState.kMiscBindNext + hsgmat.addLayer(funky_ramp.key) # Plasma makes several assumptions that every hsGMaterial has at least one layer. If this # material had no Textures, we will need to initialize a default layer @@ -357,6 +362,93 @@ class MaterialConverter: # Wasn't that easy? return hsgmat.key + def _export_funky_slot(self, bo, bm, hsgmat, slot, idx) -> plLayerInterface: + name = "{}_{}_funkRamp".format(hsgmat.key.name, slot.name) + self._report.msg("Exporting Plasma Funky Ramp Layer '{}'", name, indent=2) + + texture = slot.texture + layer_props = texture.plasma_layer + funky_type = layer_props.funky_type + + near_trans = layer_props.funky_near_trans + near_opaq = layer_props.funky_near_opaq + far_trans = layer_props.funky_far_trans + far_opaq = layer_props.funky_far_opaq + + if funky_type != "FunkyDist": + near_trans = max(min(near_trans, 180.0), 0.0) + near_opaq = max(min(near_opaq, 180.0), 0.0) + far_trans = max(min(far_trans, 180.0), 0.0) + far_opaq = max(min(far_opaq, 180.0), 0.0) + + if near_trans > far_trans: + near_trans, far_trans = far_trans, near_trans + near_opaq, far_opaq = far_opaq, near_opaq + + if near_trans == near_opaq or far_opaq == far_trans: + return None + + additive = near_trans > near_opaq and far_opaq > far_trans + + if funky_type != "FunkyDist": + near_trans = math.cos(near_trans * (math.pi / 180.0)) + near_opaq = math.cos(near_opaq * (math.pi / 180.0)) + far_opaq = math.cos(far_opaq * (math.pi / 180.0)) + far_trans = math.cos(far_trans * (math.pi / 180.0)) + + uvwXfm = hsMatrix44() + uvwXfm[0,0] = uvwXfm[1,1] = uvwXfm[2,2] = 0.0 + + if near_opaq != near_trans: + uvwXfm[0,2] = -1.0 / (near_trans - near_opaq) + uvwXfm[0,3] = uvwXfm[0,2] * -near_trans + else: + uvwXfm[0,3] = 1.0 + + if far_opaq != far_trans: + uvwXfm[1,2] = -1.0 / (far_trans - far_opaq) + uvwXfm[1,3] = uvwXfm[1,2] * -far_trans + else: + uvwXfm[1,3] = 1.0 + + ramp_layer = self._mgr.find_create_object(plLayer, name=name, bl=bo) + + rampName = "FunkyRampAdd" if additive else "FunkyRampMult" + page = self._mgr.get_textures_page(ramp_layer.key) + ramp_key = self._mgr.find_key(plMipmap, loc=page, name=rampName) + + if ramp_key is None: + funkRamp = plMipmap(rampName, 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888) + create_funky_ramp(funkRamp, additive) + self._mgr.AddObject(page, funkRamp) + ramp_key = funkRamp.key + + ramp_layer.texture = ramp_key + ramp_layer.ambient = hsColorRGBA(1.0, 1.0, 1.0, 1.0) + ramp_layer.preshade = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + ramp_layer.runtime = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + ramp_layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0) + + ramp_layer.state.ZFlags = hsGMatState.kZNoZWrite + ramp_layer.state.clampFlags = hsGMatState.kClampTexture + ramp_layer.state.blendFlags = hsGMatState.kBlendAlpha | hsGMatState.kBlendNoTexColor | hsGMatState.kBlendAlphaMult + + ramp_layer.transform = uvwXfm + + if funky_type == "FunkyDist": + ramp_layer.UVWSrc = plLayerInterface.kUVWPosition + ramp_layer.state.miscFlags |= hsGMatState.kMiscNoShadowAlpha + elif funky_type == "FunkyNormal": + ramp_layer.UVWSrc = plLayerInterface.kUVWNormal + elif funky_type == "FunkyUp": + ramp_layer.UVWSrc = plLayerInterface.kUVWNormal + ramp_layer.state.miscFlags |= hsGMatState.kMiscOrthoProjection + elif funky_type == "FunkyReflect": + ramp_layer.UVWSrc = plLayerInterface.kUVWReflect + + return ramp_layer + + def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx): name = "{}_{}".format(hsgmat.key.name, slot.name) self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2) diff --git a/korman/korlib/__init__.py b/korman/korlib/__init__.py index 1b6270d..ff6a599 100644 --- a/korman/korlib/__init__.py +++ b/korman/korlib/__init__.py @@ -29,6 +29,28 @@ except ImportError as ex: msg = "Korlib C Module did not load correctly." print(msg, "Using PyKorlib :(", sep=' ') + def create_funky_ramp(mipmap, additive=False): + kLUTHeight = 16 + kLUTWidth = 16 + + buf = bytearray(kLUTHeight * kLUTWidth * 4) + + for i in range(kLUTHeight): + for j in range(kLUTWidth): + x = j / (kLUTWidth - 1); + y = i / (kLUTHeight - 1); + + start = i * kLUTWidth * 4 + j * 4 + end = start + 4 + + if additive: + x = max(x, y) + buf[start:end] = [b for b in (255, 255, 255, int(x * 255.9))] + else: + buf[start:end] = [b for b in (255, 255, 255, int((x * y) * 255.9))] + + mipmap.setRawImage(bytes(buf)) + def create_bump_LUT(mipmap): kLUTHeight = 16 kLUTWidth = 16 diff --git a/korman/properties/prop_texture.py b/korman/properties/prop_texture.py index aaa22a3..aff59b0 100644 --- a/korman/properties/prop_texture.py +++ b/korman/properties/prop_texture.py @@ -99,3 +99,24 @@ class PlasmaLayer(bpy.types.PropertyGroup): description="Don't save the depth information, allowing rendering of layers behind this one", default=False, options=set()) + + funky_type = EnumProperty(name="Funky Blending Type", + description="Type of special funky layer blending", + items=[("FunkyNone", "None", "No funky layer blending"), + ("FunkyDist", "Distance", "Distance-based funky layer blending"), + ("FunkyNormal", "Normal", "Normal angle-based funky layer blending"), + ("FunkyReflect", "Reflect", "Reflection angle-based funky layer blending"), + ("FunkyUp", "Up", "Upwards angle-based funky layer blending")], + default="FunkyNone") + funky_near_trans = FloatProperty(name="Near Transparent", + description="Nearest distance at which the layer is fully transparent", + min=0.0, default=0.0, subtype="DISTANCE", unit="LENGTH") + funky_near_opaq = FloatProperty(name="Near Opaque", + description="Nearest distance at which the layer is fully opaque", + min=0.0, default=0.0, subtype="DISTANCE", unit="LENGTH") + funky_far_opaq = FloatProperty(name="Far Opaque", + description="Farthest distance at which the layer is fully opaque", + min=0.0, default=15.0, subtype="DISTANCE", unit="LENGTH") + funky_far_trans = FloatProperty(name="Far Transparent", + description="Farthest distance at which the layer is fully transparent", + min=0.0, default=20.0, subtype="DISTANCE", unit="LENGTH") diff --git a/korman/ui/ui_texture.py b/korman/ui/ui_texture.py index 8955e73..a362b69 100644 --- a/korman/ui/ui_texture.py +++ b/korman/ui/ui_texture.py @@ -119,3 +119,26 @@ class PlasmaLayerPanel(TextureButtonsPanel, bpy.types.Panel): return True return False + + +class PlasmaFunkyLayerPanel(TextureButtonsPanel, bpy.types.Panel): + bl_label = "Plasma Funky Layer" + + def draw(self, context): + texture, slot = context.texture, getattr(context, "texture_slot", None) + use_stencil = slot.use_stencil if slot is not None else False + layer_props = texture.plasma_layer + layout = self.layout + + layout.prop(layer_props, "funky_type") + + if layer_props.funky_type != "FunkyNone": + col = layout.column(align=True) + col.prop(layer_props, "funky_near_trans") + col.prop(layer_props, "funky_near_opaq") + col.prop(layer_props, "funky_far_opaq") + col.prop(layer_props, "funky_far_trans") + + if layer_props.funky_type != "FunkyDist": + # Mention that values are angles + layout.label("Values should be viewing angles in degrees between 0 and 180.", icon="RESTRICT_VIEW_OFF")