Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Funky Blending layer support #250

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions korlib/bumpmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions korlib/bumpmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions korlib/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
92 changes: 92 additions & 0 deletions korman/exporter/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ def export_material(self, bo, bm):
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
Expand Down Expand Up @@ -357,6 +362,93 @@ def export_waveset_material(self, bo, bm):
# 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)
Expand Down
22 changes: 22 additions & 0 deletions korman/korlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,28 @@
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))]
dpogue marked this conversation as resolved.
Show resolved Hide resolved

mipmap.setRawImage(bytes(buf))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++ uses setImageData instead of setRawImage?


def create_bump_LUT(mipmap):
kLUTHeight = 16
kLUTWidth = 16
Expand Down
21 changes: 21 additions & 0 deletions korman/properties/prop_texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
23 changes: 23 additions & 0 deletions korman/ui/ui_texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,26 @@ def _has_animation_data(self, context):
return True

return False


class PlasmaFunkyLayerPanel(TextureButtonsPanel, bpy.types.Panel):
dpogue marked this conversation as resolved.
Show resolved Hide resolved
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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should probably be a separate set of properties that display as angles in the UI. You could have another set of properties as your universal getters. IIRC angle float types in blender return in radians so the export math would be simpler.