From 53cf76feac0cbddd2f282f4d2ece9e450d4b3e31 Mon Sep 17 00:00:00 2001 From: fincs Date: Sun, 13 Sep 2015 23:27:38 +0200 Subject: [PATCH] Begin implementing fragment lighting functionality --- include/c3d/light.h | 118 ++++++++++++++++++++++++++++++++ include/citro3d.h | 1 + source/base.c | 19 +++++- source/context.h | 5 ++ source/light.c | 134 ++++++++++++++++++++++++++++++++++++ source/lightenv.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 include/c3d/light.h create mode 100644 source/light.c create mode 100644 source/lightenv.c diff --git a/include/c3d/light.h b/include/c3d/light.h new file mode 100644 index 0000000..46488e2 --- /dev/null +++ b/include/c3d/light.h @@ -0,0 +1,118 @@ +#pragma once +#include "types.h" + +//----------------------------------------------------------------------------- +// Material +//----------------------------------------------------------------------------- + +typedef struct +{ + float ambient[3]; + float diffuse[3]; + float specular0[3]; + float specular1[3]; + float emission[3]; +} C3D_Material; + +//----------------------------------------------------------------------------- +// Light environment +//----------------------------------------------------------------------------- + +// Forward declarations +typedef struct C3D_Light_t C3D_Light; +typedef struct C3D_LightEnv_t C3D_LightEnv; + +typedef struct +{ + u32 abs, select, scale; +} C3D_LightLutInputConf; + +typedef struct +{ + u32 ambient; + u32 numLights; + u32 config[2]; + C3D_LightLutInputConf lutInput; + u32 permutation; +} C3D_LightEnvConf; + +enum +{ + C3DF_LightEnv_Dirty = BIT(0), + C3DF_LightEnv_MtlDirty = BIT(1), + C3DF_LightEnv_LCDirty = BIT(2), + +#define C3DF_LightEnv_LutDirty(n) BIT(27+(n)) +#define C3DF_LightEnv_LutDirtyAll (0x1F<<27) +}; + +struct C3D_LightEnv_t +{ + void (* Update)(C3D_LightEnv* env); + void (* Dirty)(C3D_LightEnv* env); + u32 flags; + void* luts[5]; + float ambient[3]; + C3D_Light* lights[8]; + C3D_LightEnvConf conf; + C3D_Material material; +}; + +void C3D_LightEnvInit(C3D_LightEnv* env); +void C3D_LightEnvBind(C3D_LightEnv* env); + +void C3D_LightEnvMaterial(C3D_LightEnv* env, C3D_Material* mtl); +void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b); + +//----------------------------------------------------------------------------- +// Light +//----------------------------------------------------------------------------- + +typedef struct +{ + u32 specular0, specular1, diffuse, ambient; +} C3D_LightMatConf; + +typedef struct +{ + C3D_LightMatConf material; + u16 position[3]; u16 padding0; + u16 spotDir[3]; u16 padding1; + u32 padding2; + u32 config; + u32 distAttnBias, distAttnScale; +} C3D_LightConf; + +enum +{ + C3DF_Light_Enabled = BIT(0), + C3DF_Light_Dirty = BIT(1), + C3DF_Light_MatDirty = BIT(2), + //C3DF_Light_Shadow = BIT(3), + //C3DF_Light_Spot = BIT(4), + //C3DF_Light_DistAttn = BIT(5), + + C3DF_Light_SPDirty = BIT(14), + C3DF_Light_DADirty = BIT(15), +}; + +struct C3D_Light_t +{ + u16 flags, id; + C3D_LightEnv* parent; + void *lut_SP, *lut_DA; + float color[3]; + C3D_LightConf conf; +}; + +int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env); +void C3D_LightEnable(C3D_Light* light, bool enable); +void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable); +void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable); +void C3D_LightColor(C3D_Light* light, float r, float g, float b); +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_LightDistAttnEnable(C3D_Light* light, bool enable); +void C3D_LightDistAttn(C3D_Light* light, float bias, float scale); diff --git a/include/citro3d.h b/include/citro3d.h index a5053e1..dce7012 100644 --- a/include/citro3d.h +++ b/include/citro3d.h @@ -20,6 +20,7 @@ extern "C" { #include "c3d/texenv.h" #include "c3d/effect.h" #include "c3d/texture.h" +#include "c3d/light.h" #include "c3d/renderbuffer.h" diff --git a/source/base.c b/source/base.c index 3aa700f..cd0af08 100644 --- a/source/base.c +++ b/source/base.c @@ -49,7 +49,11 @@ static void C3Di_AptEventHook(int hookType, void* param) { ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_RenderBuf | C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program - | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll; + | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv; + + C3D_LightEnv* env = ctx->lightEnv; + if (env) + env->Dirty(env); break; } } @@ -209,6 +213,19 @@ void C3Di_UpdateContext(void) ctx->flags &= ~C3DiF_TexEnvAll; } + C3D_LightEnv* env = ctx->lightEnv; + + if (ctx->flags & C3DiF_LightEnv) + { + u32 enable = env != NULL; + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE0, enable); + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE1, !enable); + ctx->flags &= ~C3DiF_LightEnv; + } + + if (env) + env->Update(env); + C3D_UpdateUniforms(GPU_VERTEX_SHADER); C3D_UpdateUniforms(GPU_GEOMETRY_SHADER); } diff --git a/source/context.h b/source/context.h index 323d53c..70de827 100644 --- a/source/context.h +++ b/source/context.h @@ -6,6 +6,7 @@ #include #include #include +#include #include typedef struct @@ -32,6 +33,7 @@ typedef struct C3D_AttrInfo attrInfo; C3D_BufInfo bufInfo; C3D_Effect effect; + C3D_LightEnv* lightEnv; C3D_Tex* tex[3]; C3D_TexEnv texEnv[6]; @@ -56,6 +58,7 @@ enum C3DiF_Scissor = BIT(7), C3DiF_Program = BIT(8), C3DiF_TexEnvBuf = BIT(9), + C3DiF_LightEnv = BIT(10), #define C3DiF_Tex(n) BIT(23+(n)) C3DiF_TexAll = 7 << 23, @@ -75,3 +78,5 @@ void C3Di_BufInfoBind(C3D_BufInfo* info); void C3Di_TexEnvBind(int id, C3D_TexEnv* env); void C3Di_EffectBind(C3D_Effect* effect); void C3Di_RenderBufBind(C3D_RenderBuf* rb); + +void C3Di_LightMtlBlend(C3D_Light* light); diff --git a/source/light.c b/source/light.c new file mode 100644 index 0000000..72f9082 --- /dev/null +++ b/source/light.c @@ -0,0 +1,134 @@ +#include +#include "context.h" + +void C3Di_LightMtlBlend(C3D_Light* light) +{ + int i; + C3D_Material* mtl = &light->parent->material; + C3D_LightMatConf* conf = &light->conf.material; + memset(conf, 0, sizeof(*conf)); + + for (i = 0; i < 3; i ++) + { + conf->specular0 |= ((u32)(255*(mtl->specular0[i]*light->color[i]))) << (i*10); + conf->specular1 |= ((u32)(255*(mtl->specular1[i]*light->color[i]))) << (i*10); + conf->diffuse |= ((u32)(255*(mtl->diffuse[i] *light->color[i]))) << (i*10); + conf->ambient |= ((u32)(255*(mtl->ambient[i] *light->color[i]))) << (i*10); + } +} + +int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env) +{ + int i; + memset(light, 0, sizeof(*light)); + + for (i = 0; i < 8; i ++) + if (!env->lights[i]) + break; + if (i == 8) return -1; + + light->flags = C3DF_Light_Enabled | C3DF_Light_Dirty | C3DF_Light_MatDirty; + light->id = i; + light->parent = env; + light->conf.config = BIT(0); + + env->flags |= C3DF_LightEnv_LCDirty; + return i; +} + +void C3D_LightEnable(C3D_Light* light, bool enable) +{ + if ((light->flags & C3DF_Light_Enabled) == (enable?C3DF_Light_Enabled:0)) + return; + + if (enable) + light->flags |= C3DF_Light_Enabled; + else + light->flags &= ~C3DF_Light_Enabled; + + light->parent->flags |= C3DF_LightEnv_LCDirty; +} + +void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable) +{ + if (enable) + light->conf.config |= BIT(1); + else + light->conf.config &= ~BIT(1); + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable) +{ + id = 2 + (id&1); + if (enable) + light->conf.config |= BIT(id); + else + light->conf.config &= ~BIT(id); + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightColor(C3D_Light* light, float r, float g, float b) +{ + light->color[0] = b; + light->color[1] = g; + light->color[2] = r; + light->flags |= C3DF_Light_MatDirty; +} + +void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos) +{ + // Enable/disable positional light depending on W coordinate + light->conf.config &= ~BIT(0); + light->conf.config |= (pos->w != 0.0); + light->conf.position[0] = f32tof16(pos->x); + light->conf.position[1] = f32tof16(pos->y); + light->conf.position[2] = f32tof16(pos->z); + light->flags |= C3DF_Light_Dirty; +} + +static void C3Di_EnableCommon(C3D_Light* light, bool enable, u32 bit) +{ + C3D_LightEnv* env = light->parent; + u32* var = &env->conf.config[1]; + + if ((*var & bit) == bit) + return; + + if (enable) + *var |= bit; + else + *var &= ~bit; + + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightShadowEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_SHADOWBIT(light->id)); +} + +void C3D_LightSpotEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_SPOTBIT(light->id)); +} + +void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z) +{ + C3Di_EnableCommon(light, true, GPU_LC1_SPOTBIT(light->id)); + // TODO + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightDistAttnEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_ATTNBIT(light->id)); +} + +void C3D_LightDistAttn(C3D_Light* light, float bias, float scale) +{ + C3Di_EnableCommon(light, true, GPU_LC1_ATTNBIT(light->id)); + light->conf.distAttnBias = f32tof20(bias); + light->conf.distAttnScale = f32tof20(scale); + light->flags |= C3DF_Light_Dirty; +} diff --git a/source/lightenv.c b/source/lightenv.c new file mode 100644 index 0000000..b2086e3 --- /dev/null +++ b/source/lightenv.c @@ -0,0 +1,162 @@ +#include +#include "context.h" + +static void C3Di_LightEnvMtlBlend(C3D_LightEnv* env) +{ + int i; + C3D_Material* mtl = &env->material; + u32 color = 0; + for (i = 0; i < 3; i ++) + { + int v = 255*(mtl->emission[i] + mtl->ambient[i]*env->ambient[i]); + if (v < 0) v = 0; + else if (v > 255) v = 255; + color |= v << (i*10); + } + env->conf.ambient = color; +} + +static void C3Di_LightEnvUpdate(C3D_LightEnv* env) +{ + int i; + C3D_LightEnvConf* conf = &env->conf; + + if (env->flags & C3DF_LightEnv_LCDirty) + { + conf->numLights = 0; + conf->permutation = 0; + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + if (!(light->flags & C3DF_Light_Enabled)) continue; + conf->permutation |= GPU_LIGHTPERM(conf->numLights++, i); + } + env->flags &= ~C3DF_LightEnv_LCDirty; + env->flags |= C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_Light_MatDirty) + { + C3Di_LightEnvMtlBlend(env); + env->flags &= ~C3DF_Light_MatDirty; + env->flags |= C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_LightEnv_Dirty) + { + GPUCMD_AddWrite(GPUREG_LIGHTING_AMBIENT, conf->ambient); + GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_NUM_LIGHTS, (u32*)&conf->numLights, 3); + GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_LUTINPUT_ABS, (u32*)&conf->lutInput, 3); + GPUCMD_AddWrite(GPUREG_LIGHTING_LIGHT_PERMUTATION, conf->permutation); + env->flags &= ~C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_LightEnv_LutDirtyAll) + { + for (i = 0; i < 5; i ++) + { + if (!(env->flags & C3DF_LightEnv_LutDirty(i))) continue; + // TODO: Upload LUT + } + + env->flags &= ~C3DF_LightEnv_LutDirtyAll; + } + + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + + if (light->flags & C3DF_Light_MatDirty) + { + C3Di_LightMtlBlend(light); + light->flags &= ~C3DF_Light_MatDirty; + light->flags |= C3DF_Light_Dirty; + } + + if (light->flags & C3DF_Light_Dirty) + { + GPUCMD_AddIncrementalWrites(GPUREG_LIGHT0_SPECULAR0 + i*0x10, (u32*)&light->conf, 12); + light->flags &= ~C3DF_Light_Dirty; + } + + if (light->flags & C3DF_Light_SPDirty) + { + // TODO: Upload LUT + light->flags &= ~C3DF_Light_SPDirty; + } + + if (light->flags & C3DF_Light_DADirty) + { + // TODO: Upload LUT + light->flags &= ~C3DF_Light_DADirty; + } + } +} + +static void C3Di_LightEnvDirty(C3D_LightEnv* env) +{ + env->flags |= C3DF_LightEnv_Dirty; + int i; + for (i = 0; i < 5; i ++) + if (env->luts[i]) + env->flags |= C3DF_LightEnv_LutDirty(i); + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + + light->flags |= C3DF_Light_Dirty; + if (light->lut_SP) + light->flags |= C3DF_Light_SPDirty; + if (light->lut_DA) + light->flags |= C3DF_Light_DADirty; + } +} + +void C3D_LightEnvInit(C3D_LightEnv* env) +{ + memset(env, 0, sizeof(*env)); + env->Update = C3Di_LightEnvUpdate; + env->Dirty = C3Di_LightEnvDirty; + + env->flags = C3DF_LightEnv_Dirty; + env->conf.config[0] = (4<<8) | BIT(27) | BIT(31); + env->conf.config[1] = ~0; + env->conf.lutInput.abs = 0xFF; +} + +void C3D_LightEnvBind(C3D_LightEnv* env) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (ctx->lightEnv == env) + return; + + ctx->flags |= C3DiF_LightEnv; + ctx->lightEnv = env; +} + +void C3D_LightEnvMaterial(C3D_LightEnv* env, C3D_Material* mtl) +{ + int i; + memcpy(&env->material, mtl, sizeof(C3D_Material)); + env->flags |= C3DF_LightEnv_MtlDirty; + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (light) light->flags |= C3DF_Light_MatDirty; + } +} + +void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b) +{ + env->ambient[0] = b; + env->ambient[1] = g; + env->ambient[2] = r; + env->flags |= C3DF_LightEnv_MtlDirty; +}