From 78d1c1f05ab383c8b8cfc6a578f73054e0f9c432 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 | 44 +++++++++++++++ korlib/bumpmap.h | 1 + korlib/module.cpp | 1 + korman/exporter/material.py | 92 +++++++++++++++++++++++++++++++ korman/korlib/__init__.py | 24 ++++++++ korman/properties/prop_texture.py | 21 +++++++ korman/ui/ui_texture.py | 22 ++++++++ 7 files changed, 205 insertions(+) diff --git a/korlib/bumpmap.cpp b/korlib/bumpmap.cpp index 33cd6be..11e36b4 100644 --- a/korlib/bumpmap.cpp +++ b/korlib/bumpmap.cpp @@ -26,6 +26,50 @@ 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; + int additive = 0; + if (!PyArg_ParseTuple(args, "O|i", &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) { + if (x < y) + x = y; + + *pix++ = MakeUInt32Color(1.0f, 1.0f, 1.0f, x); + } 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..d517a22 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): + 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 + + tr0 = layer_props.funky_near_trans + op0 = layer_props.funky_near_opaq + tr1 = layer_props.funky_far_trans + op1 = layer_props.funky_far_opaq + + if funky_type != "FunkyDist": + tr0 = max(min(tr0, 180.0), 0.0) + op0 = max(min(op0, 180.0), 0.0) + tr1 = max(min(tr1, 180.0), 0.0) + op1 = max(min(op1, 180.0), 0.0) + + if tr0 > tr1: + tr0, tr1 = tr1, tr0 + op0, op1 = op1, op0 + + if tr0 == op0 or op1 == tr1: + return None + + additive = tr0 > op0 and op1 > tr1 + + if funky_type != "FunkyDist": + tr0 = math.cos(tr0 * (math.pi / 180.0)) + op0 = math.cos(op0 * (math.pi / 180.0)) + op1 = math.cos(op1 * (math.pi / 180.0)) + tr1 = math.cos(tr1 * (math.pi / 180.0)) + + uvwXfm = hsMatrix44() + uvwXfm[0,0] = uvwXfm[1,1] = uvwXfm[2,2] = 0.0 + + if op0 != tr0: + uvwXfm[0,2] = -1.0 / (tr0 - op0) + uvwXfm[0,3] = uvwXfm[0,2] * -tr0 + else: + uvwXfm[0,3] = 1.0 + + if op1 != tr1: + uvwXfm[1,2] = -1.0 / (tr1 - op1) + uvwXfm[1,3] = uvwXfm[1,2] * -tr1 + 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..563261c 100644 --- a/korman/korlib/__init__.py +++ b/korman/korlib/__init__.py @@ -29,6 +29,30 @@ 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*j*4 + end = start + 4 + + if additive: + if x < y: + 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..3e04d45 100644 --- a/korman/ui/ui_texture.py +++ b/korman/ui/ui_texture.py @@ -119,3 +119,25 @@ 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")