From 1739f88e400d466048cc89e48bec5cfe209d6b0d Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 15 Mar 2017 16:49:06 +0100 Subject: [PATCH] Add support for procedural textures (Texture Unit 3) --- include/c3d/proctex.h | 125 +++++++++++++++++++++++++++ include/citro3d.h | 1 + source/base.c | 14 ++++ source/internal.h | 9 ++ source/proctex.c | 191 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 340 insertions(+) create mode 100644 include/c3d/proctex.h create mode 100644 source/proctex.c diff --git a/include/c3d/proctex.h b/include/c3d/proctex.h new file mode 100644 index 0000000..3abf7ab --- /dev/null +++ b/include/c3d/proctex.h @@ -0,0 +1,125 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 color[256]; + u32 diff[256]; +} C3D_ProcTexColorLut; + +typedef struct +{ + union + { + u32 proctex0; + struct + { + u32 uClamp : 3; + u32 vClamp : 3; + u32 rgbFunc : 4; + u32 alphaFunc : 4; + bool alphaSeparate : 1; + bool enableNoise : 1; + u32 uShift : 2; + u32 vShift : 2; + u32 lodBiasLow : 8; + }; + }; + union + { + u32 proctex1; + struct + { + u16 uNoiseAmpl; + u16 uNoisePhase; + }; + }; + union + { + u32 proctex2; + struct + { + u16 vNoiseAmpl; + u16 vNoisePhase; + }; + }; + union + { + u32 proctex3; + struct + { + u16 uNoiseFreq; + u16 vNoiseFreq; + }; + }; + union + { + u32 proctex4; + struct + { + u32 minFilter : 3; + u32 unknown1 : 8; + u32 width : 8; + u32 lodBiasHigh : 8; + }; + }; + union + { + u32 proctex5; + struct + { + u32 offset : 8; + u32 unknown2 : 24; + }; + }; +} C3D_ProcTex; + +enum +{ + C3D_ProcTex_U = BIT(0), + C3D_ProcTex_V = BIT(1), + C3D_ProcTex_UV = C3D_ProcTex_U | C3D_ProcTex_V, +}; + +void C3D_ProcTexInit(C3D_ProcTex* pt, int offset, int length); +void C3D_ProcTexNoiseCoefs(C3D_ProcTex* pt, int mode, float amplitude, float frequency, float phase); +void C3D_ProcTexLodBias(C3D_ProcTex* pt, float bias); +void C3D_ProcTexBind(int texCoordId, C3D_ProcTex* pt); + +// GPU_LUT_NOISE, GPU_LUT_RGBMAP, GPU_LUT_ALPHAMAP +typedef u32 C3D_ProcTexLut[128]; +void C3D_ProcTexLutBind(GPU_PROCTEX_LUTID id, C3D_ProcTexLut* lut); +void ProcTexLut_FromArray(C3D_ProcTexLut* lut, const float in[129]); + +void C3D_ProcTexColorLutBind(C3D_ProcTexColorLut* lut); +void ProcTexColorLut_Write(C3D_ProcTexColorLut* out, const u32* in, int offset, int length); + +static inline void C3D_ProcTexClamp(C3D_ProcTex* pt, GPU_PROCTEX_CLAMP u, GPU_PROCTEX_CLAMP v) +{ + pt->uClamp = u; + pt->vClamp = v; +} + +static inline void C3D_ProcTexCombiner(C3D_ProcTex* pt, bool separate, GPU_PROCTEX_MAPFUNC rgb, GPU_PROCTEX_MAPFUNC alpha) +{ + pt->alphaSeparate = separate; + pt->rgbFunc = rgb; + if (separate) + pt->alphaFunc = alpha; +} + +static inline void C3D_ProcTexNoiseEnable(C3D_ProcTex* pt, bool enable) +{ + pt->enableNoise = enable; +} + +static inline void C3D_ProcTexShift(C3D_ProcTex* pt, GPU_PROCTEX_SHIFT u, GPU_PROCTEX_SHIFT v) +{ + pt->uShift = u; + pt->vShift = v; +} + +static inline void C3D_ProcTexFilter(C3D_ProcTex* pt, GPU_PROCTEX_FILTER min) +{ + pt->minFilter = min; +} diff --git a/include/citro3d.h b/include/citro3d.h index cbce14f..227bf21 100644 --- a/include/citro3d.h +++ b/include/citro3d.h @@ -21,6 +21,7 @@ extern "C" { #include "c3d/texenv.h" #include "c3d/effect.h" #include "c3d/texture.h" +#include "c3d/proctex.h" #include "c3d/light.h" #include "c3d/lightlut.h" diff --git a/source/base.c b/source/base.c index 64d3bed..2b8080e 100644 --- a/source/base.c +++ b/source/base.c @@ -25,6 +25,16 @@ __attribute__((weak)) void C3Di_LightEnvDirty(C3D_LightEnv* env) (void)env; } +__attribute__((weak)) void C3Di_ProcTexUpdate(C3D_Context* ctx) +{ + (void)ctx; +} + +__attribute__((weak)) void C3Di_ProcTexDirty(C3D_Context* ctx) +{ + (void)ctx; +} + static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) { C3D_Context* ctx = C3Di_GetContext(); @@ -50,6 +60,7 @@ static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) C3D_LightEnv* env = ctx->lightEnv; if (env) C3Di_LightEnvDirty(env); + C3Di_ProcTexDirty(ctx); break; } default: @@ -209,6 +220,9 @@ void C3Di_UpdateContext(void) ctx->texConfig &= ~BIT(16); // Remove clear-texture-cache flag } + if (ctx->flags & (C3DiF_ProcTex | C3DiF_ProcTexColorLut | C3DiF_ProcTexLutAll)) + C3Di_ProcTexUpdate(ctx); + if (ctx->flags & C3DiF_TexEnvBuf) { ctx->flags &= ~C3DiF_TexEnvBuf; diff --git a/source/internal.h b/source/internal.h index ca68063..aa5e71d 100644 --- a/source/internal.h +++ b/source/internal.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -45,6 +46,10 @@ typedef struct u32 texEnvBuf, texEnvBufClr; + C3D_ProcTex* procTex; + C3D_ProcTexLut* procTexLut[3]; + C3D_ProcTexColorLut* procTexColorLut; + C3D_FrameBuf fb; u32 viewport[5]; u32 scissor[3]; @@ -70,7 +75,11 @@ enum C3DiF_GshCode = BIT(12), C3DiF_CmdBuffer = BIT(13), C3DiF_TexStatus = BIT(14), + C3DiF_ProcTex = BIT(15), + C3DiF_ProcTexColorLut = BIT(16), +#define C3DiF_ProcTexLut(n) BIT(20+(n)) + C3DiF_ProcTexLutAll = 7 << 20, #define C3DiF_Tex(n) BIT(23+(n)) C3DiF_TexAll = 7 << 23, #define C3DiF_TexEnv(n) BIT(26+(n)) diff --git a/source/proctex.c b/source/proctex.c new file mode 100644 index 0000000..ef1f73d --- /dev/null +++ b/source/proctex.c @@ -0,0 +1,191 @@ +#include "internal.h" + +void C3D_ProcTexInit(C3D_ProcTex* pt, int offset, int width) +{ + memset(pt, 0, sizeof(*pt)); + pt->offset = offset; + pt->width = width; + pt->unknown1 = 0x60; + pt->unknown2 = 0xE0C080; +} + +void C3D_ProcTexNoiseCoefs(C3D_ProcTex* pt, int mode, float amplitude, float frequency, float phase) +{ + u16 f16_ampl = (s32)(amplitude*0x1000); + u16 f16_freq = f32tof16(frequency); + u16 f16_phase = f32tof16(phase); + pt->enableNoise = true; + if (mode & C3D_ProcTex_U) + { + pt->uNoiseAmpl = f16_ampl; + pt->uNoiseFreq = f16_freq; + pt->uNoisePhase = f16_phase; + } + if (mode & C3D_ProcTex_V) + { + pt->vNoiseAmpl = f16_ampl; + pt->vNoiseFreq = f16_freq; + pt->vNoisePhase = f16_phase; + } +} + +void C3D_ProcTexLodBias(C3D_ProcTex* pt, float bias) +{ + u32 f16_bias = f32tof16(bias); + pt->lodBiasLow = f16_bias; + pt->lodBiasHigh = f16_bias>>8; +} + +void C3D_ProcTexBind(int texCoordId, C3D_ProcTex* pt) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->flags |= C3DiF_TexStatus; + ctx->texConfig &= ~(7<<8); + ctx->procTex = pt; + if (pt) + { + ctx->flags |= C3DiF_ProcTex; + ctx->texConfig |= BIT(10) | ((texCoordId&3)<<8); + } else + ctx->flags &= ~C3DiF_ProcTex; +} + +static inline int lutid2idx(GPU_PROCTEX_LUTID id) +{ + switch (id) + { + case GPU_LUT_NOISE: return 0; + case GPU_LUT_RGBMAP: return 1; + case GPU_LUT_ALPHAMAP: return 2; + default: return -1; + } +} + +void C3D_ProcTexLutBind(GPU_PROCTEX_LUTID id, C3D_ProcTexLut* lut) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + int idx = lutid2idx(id); + if (idx < 0) + return; + + ctx->procTexLut[idx] = lut; + if (lut) + ctx->flags |= C3DiF_ProcTexLut(idx); + else + ctx->flags &= ~C3DiF_ProcTexLut(idx); +} + +static inline float clampLut(float val) +{ + if (val < 0.0f) return 0.0f; + if (val > 1.0f) return 1.0f; + return val; +} + +void ProcTexLut_FromArray(C3D_ProcTexLut* lut, const float in[129]) +{ + int i; + for (i = 0; i < 128; i ++) + { + u32 cur = 0xFFF*clampLut(in[i]); + u32 next = 0xFFF*clampLut(in[i+1]); + u32 diff = (next-cur)&0xFFF; + (*lut)[i] = cur | (diff<<12); + } +} + +void C3D_ProcTexColorLutBind(C3D_ProcTexColorLut* lut) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->procTexColorLut = lut; + if (lut) + ctx->flags |= C3DiF_ProcTexColorLut; + else + ctx->flags &= ~C3DiF_ProcTexColorLut; +} + +static inline u32 calc_diff(u32 cur, u32 next, int pos) +{ + cur = (cur>>pos)&0xFF; + next = (next>>pos)&0xFF; + u32 diff = (((s32)next-(s32)cur)>>1)&0xFF; + return diff<color[offset], in, 4*width); + for (i = 0; i < (width-1); i ++) + { + u32 cur = in[i]; + u32 next = in[i+1]; + out->diff[offset+i] = + calc_diff(cur,next,0) | + calc_diff(cur,next,8) | + calc_diff(cur,next,16) | + calc_diff(cur,next,24); + } + out->diff[offset+width-1] = 0; +} + +void C3Di_ProcTexUpdate(C3D_Context* ctx) +{ + if (ctx->flags & C3DiF_ProcTex) + { + ctx->flags &= ~C3DiF_ProcTex; + if (ctx->procTex) + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT3_PROCTEX0, (u32*)ctx->procTex, 6); + } + if (ctx->flags & C3DiF_ProcTexLutAll) + { + int i; + for (i = 0; i < 3; i ++) + { + int j = i ? (i+1) : 0; + if (!(ctx->flags & C3DiF_ProcTexLut(i)) || !ctx->procTexLut[i]) + continue; + + GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, j<<8); + GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, *ctx->procTexLut[i], 128); + } + ctx->flags &= ~C3DiF_ProcTexLutAll; + } + if (ctx->flags & C3DiF_ProcTexColorLut) + { + ctx->flags &= ~C3DiF_ProcTexColorLut; + if (ctx->procTexColorLut) + { + GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, GPU_LUT_COLOR<<8); + GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, ctx->procTexColorLut->color, 256); + GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, GPU_LUT_COLORDIF<<8); + GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, ctx->procTexColorLut->diff, 256); + } + } +} + +void C3Di_ProcTexDirty(C3D_Context* ctx) +{ + int i; + if (!ctx->procTex) + return; + + ctx->flags |= C3DiF_ProcTex; + if (ctx->procTexColorLut) + ctx->flags |= C3DiF_ProcTexColorLut; + for (i = 0; i < 3; i ++) + if (ctx->procTexLut[i]) + ctx->flags |= C3DiF_ProcTexLut(i); +}