Begin implementing fragment lighting functionality

This commit is contained in:
fincs 2015-09-13 23:27:38 +02:00
parent b2584cb71a
commit 53cf76feac
6 changed files with 438 additions and 1 deletions

118
include/c3d/light.h Normal file
View File

@ -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);

View File

@ -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"

View File

@ -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);
}

View File

@ -6,6 +6,7 @@
#include <c3d/texenv.h>
#include <c3d/effect.h>
#include <c3d/texture.h>
#include <c3d/light.h>
#include <c3d/renderbuffer.h>
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);

134
source/light.c Normal file
View File

@ -0,0 +1,134 @@
#include <string.h>
#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;
}

162
source/lightenv.c Normal file
View File

@ -0,0 +1,162 @@
#include <string.h>
#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;
}