Skip to content

Commit 553481f

Browse files
committed
Add Funky Blending layer support
1 parent 91bace8 commit 553481f

7 files changed

Lines changed: 201 additions & 0 deletions

File tree

korlib/bumpmap.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,47 @@ static uint32_t MakeUInt32Color(float r, float g, float b, float a) {
2626
(uint32_t(b * 255.9f) << 0);
2727
}
2828

29+
PyObject* create_funky_ramp(PyObject*, PyObject* args) {
30+
static const int kLUTHeight = 16;
31+
static const int kLUTWidth = 16;
32+
static const int kBufSz = kLUTWidth * kLUTHeight * sizeof(uint32_t);
33+
34+
pyMipmap* pymipmap;
35+
bool additive = false;
36+
if (!PyArg_ParseTuple(args, "O|b", &pymipmap, &additive)) {
37+
PyErr_SetString(PyExc_TypeError, "create_funky_ramp expects a plMipmap and an optional boolean");
38+
return NULL;
39+
}
40+
41+
plMipmap* texture = plMipmap::Convert(pymipmap->fThis, false);
42+
if (!texture) {
43+
PyErr_SetString(PyExc_TypeError, "create_funky_ramp expects a plMipmap");
44+
return NULL;
45+
}
46+
47+
texture->Create(kLUTWidth, kLUTHeight, 1, plBitmap::kUncompressed, plBitmap::kRGB8888);
48+
49+
uint8_t data[kBufSz];
50+
uint32_t* pix = (uint32_t*)data;
51+
52+
for (int i = 0; i < kLUTHeight; ++i) {
53+
for (int j = 0; j < kLUTWidth; ++j) {
54+
float x = float(j) / (kLUTWidth - 1);
55+
float y = float(i) / (kLUTHeight - 1);
56+
57+
if (additive) {
58+
*pix++ = MakeUInt32Color(1.0f, 1.0f, 1.0f, std::max(x, y));
59+
} else {
60+
*pix++ = MakeUInt32Color(1.0f, 1.0f, 1.0f, x * y);
61+
}
62+
}
63+
}
64+
65+
texture->setImageData(data, kBufSz);
66+
67+
Py_RETURN_NONE;
68+
}
69+
2970
PyObject* create_bump_LUT(PyObject*, PyObject* args) {
3071
static const int kLUTHeight = 16;
3172
static const int kLUTWidth = 16;

korlib/bumpmap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "korlib.h"
2121

22+
PyObject* create_funky_ramp(PyObject*, PyObject* args);
2223
PyObject* create_bump_LUT(PyObject*, PyObject* args);
2324

2425
#endif // _KORLIB_BUMPMAP_H

korlib/module.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define KORLIB_API_VERSION 2
2323

2424
static PyMethodDef korlib_Methods[] = {
25+
{ _pycs("create_funky_ramp"), (PyCFunction)create_funky_ramp, METH_VARARGS, NULL },
2526
{ _pycs("create_bump_LUT"), (PyCFunction)create_bump_LUT, METH_VARARGS, NULL },
2627
{ _pycs("inspect_vorbisfile"), (PyCFunction)inspect_vorbisfile, METH_VARARGS, NULL },
2728
{ _pycs("scale_image"), (PyCFunction)scale_image, METH_KEYWORDS | METH_VARARGS, NULL },

korman/exporter/material.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ def export_material(self, bo, bm):
253253
if i+1 < curr_stencils:
254254
stencil_layer.state.miscFlags |= hsGMatState.kMiscBindNext
255255
hsgmat.addLayer(stencil_layer.key)
256+
if slot.texture.plasma_layer.funky_type != "FunkyNone":
257+
funky_ramp = self._export_funky_slot(bo, bm, hsgmat, slot, idx)
258+
if funky_ramp:
259+
tex_layer.state.miscFlags |= hsGMatState.kMiscBindNext
260+
hsgmat.addLayer(funky_ramp.key)
256261

257262
# Plasma makes several assumptions that every hsGMaterial has at least one layer. If this
258263
# material had no Textures, we will need to initialize a default layer
@@ -357,6 +362,93 @@ def export_waveset_material(self, bo, bm):
357362
# Wasn't that easy?
358363
return hsgmat.key
359364

365+
def _export_funky_slot(self, bo, bm, hsgmat, slot, idx) -> plLayerInterface:
366+
name = "{}_{}_funkRamp".format(hsgmat.key.name, slot.name)
367+
self._report.msg("Exporting Plasma Funky Ramp Layer '{}'", name, indent=2)
368+
369+
texture = slot.texture
370+
layer_props = texture.plasma_layer
371+
funky_type = layer_props.funky_type
372+
373+
near_trans = layer_props.funky_near_trans
374+
near_opaq = layer_props.funky_near_opaq
375+
far_trans = layer_props.funky_far_trans
376+
far_opaq = layer_props.funky_far_opaq
377+
378+
if funky_type != "FunkyDist":
379+
near_trans = max(min(near_trans, 180.0), 0.0)
380+
near_opaq = max(min(near_opaq, 180.0), 0.0)
381+
far_trans = max(min(far_trans, 180.0), 0.0)
382+
far_opaq = max(min(far_opaq, 180.0), 0.0)
383+
384+
if near_trans > far_trans:
385+
near_trans, far_trans = far_trans, near_trans
386+
near_opaq, far_opaq = far_opaq, near_opaq
387+
388+
if near_trans == near_opaq or far_opaq == far_trans:
389+
return None
390+
391+
additive = near_trans > near_opaq and far_opaq > far_trans
392+
393+
if funky_type != "FunkyDist":
394+
near_trans = math.cos(near_trans * (math.pi / 180.0))
395+
near_opaq = math.cos(near_opaq * (math.pi / 180.0))
396+
far_opaq = math.cos(far_opaq * (math.pi / 180.0))
397+
far_trans = math.cos(far_trans * (math.pi / 180.0))
398+
399+
uvwXfm = hsMatrix44()
400+
uvwXfm[0,0] = uvwXfm[1,1] = uvwXfm[2,2] = 0.0
401+
402+
if near_opaq != near_trans:
403+
uvwXfm[0,2] = -1.0 / (near_trans - near_opaq)
404+
uvwXfm[0,3] = uvwXfm[0,2] * -near_trans
405+
else:
406+
uvwXfm[0,3] = 1.0
407+
408+
if far_opaq != far_trans:
409+
uvwXfm[1,2] = -1.0 / (far_trans - far_opaq)
410+
uvwXfm[1,3] = uvwXfm[1,2] * -far_trans
411+
else:
412+
uvwXfm[1,3] = 1.0
413+
414+
ramp_layer = self._mgr.find_create_object(plLayer, name=name, bl=bo)
415+
416+
rampName = "FunkyRampAdd" if additive else "FunkyRampMult"
417+
page = self._mgr.get_textures_page(ramp_layer.key)
418+
ramp_key = self._mgr.find_key(plMipmap, loc=page, name=rampName)
419+
420+
if ramp_key is None:
421+
funkRamp = plMipmap(rampName, 16, 16, 1, plBitmap.kUncompressed, plBitmap.kRGB8888)
422+
create_funky_ramp(funkRamp, additive)
423+
self._mgr.AddObject(page, funkRamp)
424+
ramp_key = funkRamp.key
425+
426+
ramp_layer.texture = ramp_key
427+
ramp_layer.ambient = hsColorRGBA(1.0, 1.0, 1.0, 1.0)
428+
ramp_layer.preshade = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
429+
ramp_layer.runtime = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
430+
ramp_layer.specular = hsColorRGBA(0.0, 0.0, 0.0, 1.0)
431+
432+
ramp_layer.state.ZFlags = hsGMatState.kZNoZWrite
433+
ramp_layer.state.clampFlags = hsGMatState.kClampTexture
434+
ramp_layer.state.blendFlags = hsGMatState.kBlendAlpha | hsGMatState.kBlendNoTexColor | hsGMatState.kBlendAlphaMult
435+
436+
ramp_layer.transform = uvwXfm
437+
438+
if funky_type == "FunkyDist":
439+
ramp_layer.UVWSrc = plLayerInterface.kUVWPosition
440+
ramp_layer.state.miscFlags |= hsGMatState.kMiscNoShadowAlpha
441+
elif funky_type == "FunkyNormal":
442+
ramp_layer.UVWSrc = plLayerInterface.kUVWNormal
443+
elif funky_type == "FunkyUp":
444+
ramp_layer.UVWSrc = plLayerInterface.kUVWNormal
445+
ramp_layer.state.miscFlags |= hsGMatState.kMiscOrthoProjection
446+
elif funky_type == "FunkyReflect":
447+
ramp_layer.UVWSrc = plLayerInterface.kUVWReflect
448+
449+
return ramp_layer
450+
451+
360452
def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx):
361453
name = "{}_{}".format(hsgmat.key.name, slot.name)
362454
self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2)

korman/korlib/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@
2929
msg = "Korlib C Module did not load correctly."
3030
print(msg, "Using PyKorlib :(", sep=' ')
3131

32+
def create_funky_ramp(mipmap, additive=False):
33+
kLUTHeight = 16
34+
kLUTWidth = 16
35+
36+
buf = bytearray(kLUTHeight * kLUTWidth * 4)
37+
38+
for i in range(kLUTHeight):
39+
for j in range(kLUTWidth):
40+
x = j / (kLUTWidth - 1);
41+
y = i / (kLUTHeight - 1);
42+
43+
start = i * kLUTWidth * 4 + j * 4
44+
end = start + 4
45+
46+
if additive:
47+
x = max(x, y)
48+
buf[start:end] = [b for b in (255, 255, 255, int(x * 255.9))]
49+
else:
50+
buf[start:end] = [b for b in (255, 255, 255, int((x * y) * 255.9))]
51+
52+
mipmap.setRawImage(bytes(buf))
53+
3254
def create_bump_LUT(mipmap):
3355
kLUTHeight = 16
3456
kLUTWidth = 16

korman/properties/prop_texture.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,24 @@ class PlasmaLayer(bpy.types.PropertyGroup):
9999
description="Don't save the depth information, allowing rendering of layers behind this one",
100100
default=False,
101101
options=set())
102+
103+
funky_type = EnumProperty(name="Funky Blending Type",
104+
description="Type of special funky layer blending",
105+
items=[("FunkyNone", "None", "No funky layer blending"),
106+
("FunkyDist", "Distance", "Distance-based funky layer blending"),
107+
("FunkyNormal", "Normal", "Normal angle-based funky layer blending"),
108+
("FunkyReflect", "Reflect", "Reflection angle-based funky layer blending"),
109+
("FunkyUp", "Up", "Upwards angle-based funky layer blending")],
110+
default="FunkyNone")
111+
funky_near_trans = FloatProperty(name="Near Transparent",
112+
description="Nearest distance at which the layer is fully transparent",
113+
min=0.0, default=0.0, subtype="DISTANCE", unit="LENGTH")
114+
funky_near_opaq = FloatProperty(name="Near Opaque",
115+
description="Nearest distance at which the layer is fully opaque",
116+
min=0.0, default=0.0, subtype="DISTANCE", unit="LENGTH")
117+
funky_far_opaq = FloatProperty(name="Far Opaque",
118+
description="Farthest distance at which the layer is fully opaque",
119+
min=0.0, default=15.0, subtype="DISTANCE", unit="LENGTH")
120+
funky_far_trans = FloatProperty(name="Far Transparent",
121+
description="Farthest distance at which the layer is fully transparent",
122+
min=0.0, default=20.0, subtype="DISTANCE", unit="LENGTH")

korman/ui/ui_texture.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,26 @@ def _has_animation_data(self, context):
119119
return True
120120

121121
return False
122+
123+
124+
class PlasmaFunkyLayerPanel(TextureButtonsPanel, bpy.types.Panel):
125+
bl_label = "Plasma Funky Layer"
126+
127+
def draw(self, context):
128+
texture, slot = context.texture, getattr(context, "texture_slot", None)
129+
use_stencil = slot.use_stencil if slot is not None else False
130+
layer_props = texture.plasma_layer
131+
layout = self.layout
132+
133+
layout.prop(layer_props, "funky_type")
134+
135+
if layer_props.funky_type != "FunkyNone":
136+
col = layout.column(align=True)
137+
col.prop(layer_props, "funky_near_trans")
138+
col.prop(layer_props, "funky_near_opaq")
139+
col.prop(layer_props, "funky_far_opaq")
140+
col.prop(layer_props, "funky_far_trans")
141+
142+
if layer_props.funky_type != "FunkyDist":
143+
# Mention that values are angles
144+
layout.label("Values should be viewing angles in degrees between 0 and 180.", icon="RESTRICT_VIEW_OFF")

0 commit comments

Comments
 (0)