From 0922a64ed3368d4248960f2d65bb8f34d5c6ff7a Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 15 Sep 2015 23:02:35 +0200 Subject: [PATCH] Add fragment light LUT support --- include/c3d/light.h | 12 +++++++---- include/c3d/lightlut.h | 15 ++++++++++++++ source/light.c | 18 +++++++++++++++++ source/lightenv.c | 41 ++++++++++++++++++++++++++++++++++--- source/lightlut.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 include/c3d/lightlut.h create mode 100644 source/lightlut.c diff --git a/include/c3d/light.h b/include/c3d/light.h index 99b4f29..f91f910 100644 --- a/include/c3d/light.h +++ b/include/c3d/light.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "lightlut.h" //----------------------------------------------------------------------------- // Material @@ -42,8 +43,8 @@ enum C3DF_LightEnv_MtlDirty = BIT(1), C3DF_LightEnv_LCDirty = BIT(2), -#define C3DF_LightEnv_LutDirty(n) BIT(27+(n)) -#define C3DF_LightEnv_LutDirtyAll (0x1F<<27) +#define C3DF_LightEnv_LutDirty(n) BIT(26+(n)) +#define C3DF_LightEnv_LutDirtyAll (0x3F<<26) }; struct C3D_LightEnv_t @@ -51,7 +52,7 @@ struct C3D_LightEnv_t void (* Update)(C3D_LightEnv* env); void (* Dirty)(C3D_LightEnv* env); u32 flags; - void* luts[5]; + C3D_LightLut* luts[6]; float ambient[3]; C3D_Light* lights[8]; C3D_LightEnvConf conf; @@ -63,6 +64,7 @@ void C3D_LightEnvBind(C3D_LightEnv* env); void C3D_LightEnvMaterial(C3D_LightEnv* env, const C3D_Material* mtl); void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b); +void C3D_LightEnvLut(C3D_LightEnv* env, int lutId, int input, bool abs, C3D_LightLut* lut); //----------------------------------------------------------------------------- // Light @@ -100,7 +102,7 @@ struct C3D_Light_t { u16 flags, id; C3D_LightEnv* parent; - void *lut_SP, *lut_DA; + C3D_LightLut *lut_SP, *lut_DA; float color[3]; C3D_LightConf conf; }; @@ -114,5 +116,7 @@ void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos); void C3D_LightShadowEnable(C3D_Light* light, bool enable); void C3D_LightSpotEnable(C3D_Light* light, bool enable); void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z); +void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut); void C3D_LightDistAttnEnable(C3D_Light* light, bool enable); void C3D_LightDistAttn(C3D_Light* light, float bias, float scale); +void C3D_LightDistAttnLut(C3D_Light* light, C3D_LightLut* lut); diff --git a/include/c3d/lightlut.h b/include/c3d/lightlut.h new file mode 100644 index 0000000..b718b97 --- /dev/null +++ b/include/c3d/lightlut.h @@ -0,0 +1,15 @@ +#pragma once +#include "types.h" +#include + +typedef struct +{ + u32 data[256]; +} C3D_LightLut; + +typedef float (* C3D_LightLutFunc)(float x, float param); + +void LightLut_FromArray(C3D_LightLut* lut, float* data); +void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative); + +#define LightLut_Phong(lut, shininess) LightLut_FromFunc((lut), powf, (shininess), false) diff --git a/source/light.c b/source/light.c index 73ce9d9..99252c5 100644 --- a/source/light.c +++ b/source/light.c @@ -120,6 +120,15 @@ void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z) light->flags |= C3DF_Light_Dirty; } +void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut) +{ + bool hasLut = lut != NULL; + C3Di_EnableCommon(light, hasLut, GPU_LC1_SPOTBIT(light->id)); + light->lut_SP = lut; + if (hasLut) + light->flags |= C3DF_Light_SPDirty; +} + void C3D_LightDistAttnEnable(C3D_Light* light, bool enable) { C3Di_EnableCommon(light, enable, GPU_LC1_ATTNBIT(light->id)); @@ -132,3 +141,12 @@ void C3D_LightDistAttn(C3D_Light* light, float bias, float scale) light->conf.distAttnScale = f32tof20(scale); light->flags |= C3DF_Light_Dirty; } + +void C3D_LightDistAttnLut(C3D_Light* light, C3D_LightLut* lut) +{ + bool hasLut = lut != NULL; + C3Di_EnableCommon(light, hasLut, GPU_LC1_ATTNBIT(light->id)); + light->lut_DA = lut; + if (hasLut) + light->flags |= C3DF_Light_DADirty; +} diff --git a/source/lightenv.c b/source/lightenv.c index 1e2fcc0..273be39 100644 --- a/source/lightenv.c +++ b/source/lightenv.c @@ -16,6 +16,14 @@ static void C3Di_LightEnvMtlBlend(C3D_LightEnv* env) env->conf.ambient = color; } +static void C3Di_LightLutUpload(u32 config, C3D_LightLut* lut) +{ + int i; + GPUCMD_AddWrite(GPUREG_LIGHTING_LUT_INDEX, config); + for (i = 0; i < 256; i += 8) + GPUCMD_AddWrites(GPUREG_LIGHTING_LUT_DATA0, &lut->data[i], 8); +} + static void C3Di_LightEnvUpdate(C3D_LightEnv* env) { int i; @@ -57,8 +65,9 @@ static void C3Di_LightEnvUpdate(C3D_LightEnv* env) { for (i = 0; i < 5; i ++) { + static const u8 lutIds[] = { 0, 1, 3, 4, 5, 6 }; if (!(env->flags & C3DF_LightEnv_LutDirty(i))) continue; - // TODO: Upload LUT + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_COMMON, (u32)lutIds[i], 0), env->luts[i]); } env->flags &= ~C3DF_LightEnv_LutDirtyAll; @@ -84,13 +93,13 @@ static void C3Di_LightEnvUpdate(C3D_LightEnv* env) if (light->flags & C3DF_Light_SPDirty) { - // TODO: Upload LUT + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_SP, i, 0), light->lut_SP); light->flags &= ~C3DF_Light_SPDirty; } if (light->flags & C3DF_Light_DADirty) { - // TODO: Upload LUT + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_DA, i, 0), light->lut_DA); light->flags &= ~C3DF_Light_DADirty; } } @@ -161,3 +170,29 @@ void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b) env->ambient[2] = r; env->flags |= C3DF_LightEnv_MtlDirty; } + +void C3D_LightEnvLut(C3D_LightEnv* env, int lutId, int input, bool abs, C3D_LightLut* lut) +{ + static const s8 ids[] = { 0, 1, -1, 2, 3, 4, 5, -1 }; + int id = ids[lutId]; + if (id >= 0) + { + env->luts[id] = lut; + if (lut) + { + env->conf.config[1] &= ~GPU_LC1_LUTBIT(lutId); + env->flags |= C3DF_LightEnv_LutDirty(id); + } else + env->luts[id] = NULL; + } + + env->conf.lutInput.select &= ~GPU_LIGHTLUTINPUT(lutId, 0xF); + env->conf.lutInput.select |= GPU_LIGHTLUTINPUT(lutId, input); + + u32 absbit = 1 << (lutId*4 + 1); + env->conf.lutInput.abs &= ~absbit; + if (!abs) + env->conf.lutInput.abs |= absbit; + + env->flags |= C3DF_LightEnv_Dirty; +} diff --git a/source/lightlut.c b/source/lightlut.c new file mode 100644 index 0000000..400af86 --- /dev/null +++ b/source/lightlut.c @@ -0,0 +1,46 @@ +#include +#include "context.h" + +void LightLut_FromArray(C3D_LightLut* lut, float* data) +{ + int i; + for (i = 0; i < 256; i ++) + { + float in = data[i], diff = data[i+256]; + + u32 val = 0; + if (in > 0.0) + { + in *= 0x1000; + val = (in < 0x1000) ? (u32)in : 0xFFF; + } + + u32 val2 = 0; + if (diff != 0.0) + { + if (diff < 0) + { + diff = -diff; + val2 = 0x800; + } + diff *= 0x800; + val2 |= (diff < 0x800) ? (u32)diff : 0x7FF; + } + + lut->data[i] = val | (val2 << 12); + } +} + +void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative) +{ + int i; + float data[512]; + memset(data, 0, sizeof(data)); + int start = negative ? (-127) : 0; + for (i = start; i < 128; i ++) + { + data[i & 0xFF] = func((float)i / 127.0f, param); + if (i != start) data[(i & 0xFF) + 255] = data[i & 0xFF] - data[(i-1) & 0xFF]; + } + LightLut_FromArray(lut, data); +}