├── source ├── maths │ ├── .gitignore │ ├── mtx_scale.c │ ├── quat_fromaxisangle.c │ ├── mtx_transpose.c │ ├── mtx_multiplyfvec3.c │ ├── quat_crossfvec3.c │ ├── quat_rotatex.c │ ├── quat_rotatey.c │ ├── quat_rotatez.c │ ├── mtx_multiplyfvec4.c │ ├── quat_multiply.c │ ├── quat_rotate.c │ ├── mtx_translate.c │ ├── mtx_multiply.c │ ├── mtx_perspstereo.c │ ├── mtx_rotatex.c │ ├── mtx_rotatey.c │ ├── mtx_rotatez.c │ ├── mtx_persp.c │ ├── mtx_fromquat.c │ ├── mtx_ortho.c │ ├── quat_lookat.c │ ├── quat_pow.c │ ├── quat_frompitchyawroll.c │ ├── mtx_perspstereotilt.c │ ├── mtx_orthotilt.c │ ├── mtx_lookat.c │ ├── mtx_persptilt.c │ ├── mtx_rotate.c │ ├── quat_frommtx.c │ └── mtx_inverse.c ├── mtxstack.c ├── drawArrays.c ├── buffers.c ├── lightlut.c ├── texenv.c ├── drawElements.c ├── fog.c ├── immediate.c ├── attribs.c ├── framebuffer.c ├── effect.c ├── uniforms.c ├── internal.h ├── gas.c ├── proctex.c ├── light.c ├── tex3ds.c ├── texture.c ├── lightenv.c ├── base.c └── renderqueue.c ├── test ├── pc │ ├── .gitignore │ └── Makefile └── 3ds │ ├── logo48.png │ ├── logo64.png │ ├── romfs │ └── logo.bin │ ├── source │ └── vshader.v.pica │ └── Makefile ├── citro3d_logo.png ├── .gitignore ├── include ├── c3d │ ├── attribs.h │ ├── buffers.h │ ├── mtxstack.h │ ├── effect.h │ ├── fog.h │ ├── lightlut.h │ ├── base.h │ ├── types.h │ ├── framebuffer.h │ ├── uniforms.h │ ├── renderqueue.h │ ├── texenv.h │ ├── proctex.h │ ├── light.h │ ├── texture.h │ └── maths.h ├── citro3d.h └── tex3ds.h ├── LICENSE ├── README.md ├── Makefile └── Doxyfile /source/maths/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.o 3 | -------------------------------------------------------------------------------- /test/pc/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.o 3 | test 4 | coverage.info 5 | lcov/ 6 | -------------------------------------------------------------------------------- /citro3d_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devkitPro/citro3d/HEAD/citro3d_logo.png -------------------------------------------------------------------------------- /test/3ds/logo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devkitPro/citro3d/HEAD/test/3ds/logo48.png -------------------------------------------------------------------------------- /test/3ds/logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devkitPro/citro3d/HEAD/test/3ds/logo64.png -------------------------------------------------------------------------------- /test/3ds/romfs/logo.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devkitPro/citro3d/HEAD/test/3ds/romfs/logo.bin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*/ 2 | *~ 3 | *.3dsx 4 | *.elf 5 | *.exe 6 | *.smdh 7 | *.tar.bz2 8 | Thumbs.db 9 | build/ 10 | deps/ 11 | release/ 12 | debug/ 13 | lib/ 14 | bin/ 15 | doc/ 16 | -------------------------------------------------------------------------------- /source/maths/mtx_scale.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z) 4 | { 5 | int i; 6 | for (i = 0; i < 4; ++i) 7 | { 8 | mtx->r[i].x *= x; 9 | mtx->r[i].y *= y; 10 | mtx->r[i].z *= z; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /source/maths/quat_fromaxisangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_FromAxisAngle(C3D_FVec axis, float angle) 4 | { 5 | float halfAngle = angle / 2.0f; 6 | float scale = sinf(halfAngle); 7 | axis = Quat_Normalize(axis); 8 | return Quat_New(axis.x * scale, axis.y * scale, axis.z * scale, cosf(halfAngle)); 9 | } 10 | -------------------------------------------------------------------------------- /source/maths/mtx_transpose.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Transpose(C3D_Mtx* out) 4 | { 5 | float swap; 6 | for (int i = 0; i <= 2; i++) 7 | { 8 | for (int j = 2-i; j >= 0; j--) 9 | { 10 | swap = out->r[i].c[j]; 11 | out->r[i].c[j] = out->r[3-j].c[3-i]; 12 | out->r[3-j].c[3-i] = swap; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source/maths/mtx_multiplyfvec3.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FVec Mtx_MultiplyFVec3(const C3D_Mtx* mtx, C3D_FVec v) 4 | { 5 | // http://www.wolframalpha.com/input/?i={{a,b,c},{d,e,f},{g,h,i}}{x,y,z} 6 | float x = FVec3_Dot(mtx->r[0], v); 7 | float y = FVec3_Dot(mtx->r[1], v); 8 | float z = FVec3_Dot(mtx->r[2], v); 9 | 10 | return FVec3_New(x, y, z); 11 | } 12 | -------------------------------------------------------------------------------- /source/maths/quat_crossfvec3.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FVec Quat_CrossFVec3(C3D_FQuat q, C3D_FVec v) 4 | { 5 | C3D_FVec qv = FVec3_New(q.i, q.j, q.k); 6 | C3D_FVec uv = FVec3_Cross(qv, v); 7 | C3D_FVec uuv = FVec3_Cross(qv, uv); 8 | 9 | uv = FVec3_Scale(uv, 2.0f * q.r); 10 | uuv = FVec3_Scale(uuv, 2.0f); 11 | 12 | return FVec3_Add(v, FVec3_Add(uv, uuv)); 13 | } 14 | -------------------------------------------------------------------------------- /source/maths/quat_rotatex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_RotateX(C3D_FQuat q, float r, bool bRightSide) 4 | { 5 | float c = cosf(r/2.0f); 6 | float s = sinf(r/2.0f); 7 | 8 | if (bRightSide) 9 | return Quat_New(q.r*s + q.i*c, q.j*c - q.k*s, q.k*c + q.j*s, q.r*c - q.i*s); 10 | else 11 | return Quat_New(q.r*s + q.i*c, q.j*c + q.k*s, q.k*c - q.j*s, q.r*c - q.i*s); 12 | } 13 | -------------------------------------------------------------------------------- /source/maths/quat_rotatey.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_RotateY(C3D_FQuat q, float r, bool bRightSide) 4 | { 5 | float c = cosf(r/2.0f); 6 | float s = sinf(r/2.0f); 7 | 8 | if (bRightSide) 9 | return Quat_New(q.i*c + q.k*s, q.r*s + q.j*c, q.k*c - q.i*s, q.r*c - q.j*s); 10 | else 11 | return Quat_New(q.i*c - q.k*s, q.r*s + q.j*c, q.k*c + q.i*s, q.r*c - q.j*s); 12 | } 13 | -------------------------------------------------------------------------------- /source/maths/quat_rotatez.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_RotateZ(C3D_FQuat q, float r, bool bRightSide) 4 | { 5 | float c = cosf(r/2.0f); 6 | float s = sinf(r/2.0f); 7 | 8 | if (bRightSide) 9 | return Quat_New(q.i*c - q.j*s, q.j*c + q.i*s, q.r*s + q.k*c, q.r*c - q.k*s); 10 | else 11 | return Quat_New(q.i*c + q.j*s, q.j*c - q.i*s, q.r*s + q.k*c, q.r*c - q.k*s); 12 | } 13 | -------------------------------------------------------------------------------- /source/maths/mtx_multiplyfvec4.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FVec Mtx_MultiplyFVec4(const C3D_Mtx* mtx, C3D_FVec v) 4 | { 5 | // http://www.wolframalpha.com/input/?i={{a,b,c,d},{e,f,g,h},{i,j,k,l},{m,n,o,p}}{x,y,z,w} 6 | float x = FVec4_Dot(mtx->r[0], v); 7 | float y = FVec4_Dot(mtx->r[1], v); 8 | float z = FVec4_Dot(mtx->r[2], v); 9 | float w = FVec4_Dot(mtx->r[3], v); 10 | 11 | return FVec4_New(x, y, z, w); 12 | } 13 | -------------------------------------------------------------------------------- /source/maths/quat_multiply.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_Multiply(C3D_FQuat lhs, C3D_FQuat rhs) 4 | { 5 | float i = lhs.r*rhs.i + lhs.i*rhs.r + lhs.j*rhs.k - lhs.k*rhs.j; 6 | float j = lhs.r*rhs.j + lhs.j*rhs.r + lhs.k*rhs.i - lhs.i*rhs.k; 7 | float k = lhs.r*rhs.k + lhs.k*rhs.r + lhs.i*rhs.j - lhs.j*rhs.i; 8 | float r = lhs.r*rhs.r - lhs.i*rhs.i - lhs.j*rhs.j - lhs.k*rhs.k; 9 | 10 | return Quat_New(i, j, k, r); 11 | } 12 | -------------------------------------------------------------------------------- /source/maths/quat_rotate.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_Rotate(C3D_FQuat q, C3D_FVec axis, float r, bool bRightSide) 4 | { 5 | float halfAngle = r/2.0f; 6 | float s = sinf(halfAngle); 7 | 8 | axis = FVec3_Normalize(axis); 9 | 10 | C3D_FQuat tmp = Quat_New(axis.x*s, axis.y*s, axis.z*s, cosf(halfAngle)); 11 | 12 | if (bRightSide) 13 | return Quat_Multiply(tmp, q); 14 | else 15 | return Quat_Multiply(q, tmp); 16 | } 17 | -------------------------------------------------------------------------------- /include/c3d/attribs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct 5 | { 6 | u32 flags[2]; 7 | u64 permutation; 8 | int attrCount; 9 | } C3D_AttrInfo; 10 | 11 | void AttrInfo_Init(C3D_AttrInfo* info); 12 | int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count); 13 | int AttrInfo_AddFixed(C3D_AttrInfo* info, int regId); 14 | 15 | C3D_AttrInfo* C3D_GetAttrInfo(void); 16 | void C3D_SetAttrInfo(C3D_AttrInfo* info); 17 | -------------------------------------------------------------------------------- /source/maths/mtx_translate.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z, bool bRightSide) 4 | { 5 | 6 | C3D_FVec v = FVec4_New(x, y, z, 1.0f); 7 | int i, j; 8 | 9 | if (bRightSide) 10 | { 11 | for (i = 0; i < 4; ++i) 12 | mtx->r[i].w = FVec4_Dot(mtx->r[i], v); 13 | } 14 | else 15 | { 16 | for (j = 0; j < 3; ++j) 17 | for (i = 0; i < 4; ++i) 18 | mtx->r[j].c[i] += mtx->r[3].c[i] * v.c[3-j]; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /include/c3d/buffers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct 5 | { 6 | u32 offset; 7 | u32 flags[2]; 8 | } C3D_BufCfg; 9 | 10 | typedef struct 11 | { 12 | u32 base_paddr; 13 | int bufCount; 14 | C3D_BufCfg buffers[12]; 15 | } C3D_BufInfo; 16 | 17 | void BufInfo_Init(C3D_BufInfo* info); 18 | int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation); 19 | 20 | C3D_BufInfo* C3D_GetBufInfo(void); 21 | void C3D_SetBufInfo(C3D_BufInfo* info); 22 | -------------------------------------------------------------------------------- /include/c3d/mtxstack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "maths.h" 3 | 4 | #define C3D_MTXSTACK_SIZE 8 5 | 6 | typedef struct 7 | { 8 | C3D_Mtx m[C3D_MTXSTACK_SIZE]; 9 | int pos; 10 | u8 unifType, unifPos, unifLen; 11 | bool isDirty; 12 | } C3D_MtxStack; 13 | 14 | static inline C3D_Mtx* MtxStack_Cur(C3D_MtxStack* stk) 15 | { 16 | stk->isDirty = true; 17 | return &stk->m[stk->pos]; 18 | } 19 | 20 | void MtxStack_Init(C3D_MtxStack* stk); 21 | void MtxStack_Bind(C3D_MtxStack* stk, GPU_SHADER_TYPE unifType, int unifPos, int unifLen); 22 | C3D_Mtx* MtxStack_Push(C3D_MtxStack* stk); 23 | C3D_Mtx* MtxStack_Pop(C3D_MtxStack* stk); 24 | void MtxStack_Update(C3D_MtxStack* stk); 25 | -------------------------------------------------------------------------------- /source/maths/mtx_multiply.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b) 4 | { 5 | // if out is a or b, then we need to avoid overwriting them 6 | if(out == a || out == b) 7 | { 8 | C3D_Mtx tmp; 9 | Mtx_Multiply(&tmp, a, b); 10 | Mtx_Copy(out, &tmp); 11 | return; 12 | } 13 | 14 | // http://www.wolframalpha.com/input/?i={{a,b,c,d},{e,f,g,h},{i,j,k,l},{m,n,o,p}}{{α,β,γ,δ},{ε,θ,ι,κ},{λ,μ,ν,ξ},{ο,π,ρ,σ}} 15 | int i, j; 16 | for (j = 0; j < 4; ++j) 17 | for (i = 0; i < 4; ++i) 18 | out->r[j].c[i] = a->r[j].x*b->r[0].c[i] + a->r[j].y*b->r[1].c[i] + a->r[j].z*b->r[2].c[i] + a->r[j].w*b->r[3].c[i]; 19 | } 20 | -------------------------------------------------------------------------------- /include/citro3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CITRO3D_BUILD 4 | #error "This header file is only for external users of citro3d." 5 | #endif 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include "c3d/types.h" 12 | 13 | #include "c3d/maths.h" 14 | #include "c3d/mtxstack.h" 15 | 16 | #include "c3d/uniforms.h" 17 | #include "c3d/attribs.h" 18 | #include "c3d/buffers.h" 19 | #include "c3d/base.h" 20 | 21 | #include "c3d/texenv.h" 22 | #include "c3d/effect.h" 23 | #include "c3d/texture.h" 24 | #include "c3d/proctex.h" 25 | #include "c3d/light.h" 26 | #include "c3d/lightlut.h" 27 | #include "c3d/fog.h" 28 | 29 | #include "c3d/framebuffer.h" 30 | #include "c3d/renderqueue.h" 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /source/maths/mtx_perspstereo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_PerspStereo(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded) 4 | { 5 | float fovy_tan = tanf(fovy/2.0f); 6 | float fovy_tan_aspect = fovy_tan*aspect; 7 | float shift = iod / (2.0f*screen); // 'near' not in the numerator because it cancels out in mp.r[1].z 8 | 9 | Mtx_Zeros(mtx); 10 | 11 | mtx->r[0].x = 1.0f / fovy_tan_aspect; 12 | mtx->r[0].w = -iod / 2.0f; 13 | mtx->r[1].y = 1.0f / fovy_tan; 14 | mtx->r[2].w = near * far / (near - far); 15 | mtx->r[3].z = isLeftHanded ? 1.0f : -1.0f; 16 | mtx->r[0].z = mtx->r[3].z * shift / fovy_tan_aspect; 17 | mtx->r[2].z = -mtx->r[3].z * near / (near - far); 18 | } 19 | -------------------------------------------------------------------------------- /source/maths/mtx_rotatex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide) 4 | { 5 | float a, b; 6 | float cosAngle = cosf(angle); 7 | float sinAngle = sinf(angle); 8 | size_t i; 9 | 10 | if (bRightSide) 11 | { 12 | for (i = 0; i < 4; ++i) 13 | { 14 | a = mtx->r[i].y*cosAngle + mtx->r[i].z*sinAngle; 15 | b = mtx->r[i].z*cosAngle - mtx->r[i].y*sinAngle; 16 | mtx->r[i].y = a; 17 | mtx->r[i].z = b; 18 | } 19 | } 20 | else 21 | { 22 | for (i = 0; i < 4; ++i) 23 | { 24 | a = mtx->r[1].c[i]*cosAngle - mtx->r[2].c[i]*sinAngle; 25 | b = mtx->r[2].c[i]*cosAngle + mtx->r[1].c[i]*sinAngle; 26 | mtx->r[1].c[i] = a; 27 | mtx->r[2].c[i] = b; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/maths/mtx_rotatey.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide) 4 | { 5 | float a, b; 6 | float cosAngle = cosf(angle); 7 | float sinAngle = sinf(angle); 8 | size_t i; 9 | 10 | if (bRightSide) 11 | { 12 | for (i = 0; i < 4; ++i) 13 | { 14 | a = mtx->r[i].x*cosAngle - mtx->r[i].z*sinAngle; 15 | b = mtx->r[i].z*cosAngle + mtx->r[i].x*sinAngle; 16 | mtx->r[i].x = a; 17 | mtx->r[i].z = b; 18 | } 19 | } 20 | else 21 | { 22 | for (i = 0; i < 4; ++i) 23 | { 24 | a = mtx->r[0].c[i]*cosAngle + mtx->r[2].c[i]*sinAngle; 25 | b = mtx->r[2].c[i]*cosAngle - mtx->r[0].c[i]*sinAngle; 26 | mtx->r[0].c[i] = a; 27 | mtx->r[2].c[i] = b; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/maths/mtx_rotatez.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide) 4 | { 5 | float a, b; 6 | float cosAngle = cosf(angle); 7 | float sinAngle = sinf(angle); 8 | size_t i; 9 | 10 | if (bRightSide) 11 | { 12 | for (i = 0; i < 4; ++i) 13 | { 14 | a = mtx->r[i].x*cosAngle + mtx->r[i].y*sinAngle; 15 | b = mtx->r[i].y*cosAngle - mtx->r[i].x*sinAngle; 16 | mtx->r[i].x = a; 17 | mtx->r[i].y = b; 18 | } 19 | } 20 | else 21 | { 22 | for (i = 0; i < 4; ++i) 23 | { 24 | a = mtx->r[0].c[i]*cosAngle - mtx->r[1].c[i]*sinAngle; 25 | b = mtx->r[1].c[i]*cosAngle + mtx->r[0].c[i]*sinAngle; 26 | mtx->r[0].c[i] = a; 27 | mtx->r[1].c[i] = b; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/maths/mtx_persp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Persp(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded) 4 | { 5 | float fovy_tan = tanf(fovy/2.0f); 6 | 7 | Mtx_Zeros(mtx); 8 | 9 | // Standard perspective projection matrix, with a fixed depth range of [-1,0] (required by PICA) 10 | // http://www.wolframalpha.com/input/?i=%7B%7B1,0,0,0%7D,%7B0,1,0,0%7D,%7B0,0,0.5,-0.5%7D,%7B0,0,0,1%7D%7D%7B%7B1%2F(a*tan(v)),0,0,0%7D,%7B0,1%2Ftan(v),0,0%7D,%7B0,0,(n%2Bf)%2F(n-f),(2fn)%2F(n-f)%7D,%7B0,0,-1,0%7D%7D 11 | 12 | mtx->r[0].x = 1.0f / (aspect * fovy_tan); 13 | mtx->r[1].y = 1.0f / fovy_tan; 14 | mtx->r[2].w = far*near / (near - far); 15 | mtx->r[3].z = isLeftHanded ? 1.0f : -1.0f; 16 | mtx->r[2].z = -mtx->r[3].z * near / (near - far); 17 | } 18 | -------------------------------------------------------------------------------- /source/maths/mtx_fromquat.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_FromQuat(C3D_Mtx* m, C3D_FQuat q) 4 | { 5 | float ii = q.i*q.i; 6 | float ij = q.i*q.j; 7 | float ik = q.i*q.k; 8 | float jj = q.j*q.j; 9 | float jk = q.j*q.k; 10 | float kk = q.k*q.k; 11 | float ri = q.r*q.i; 12 | float rj = q.r*q.j; 13 | float rk = q.r*q.k; 14 | 15 | m->r[0].x = 1.0f - (2.0f * (jj + kk)); 16 | m->r[1].x = 2.0f * (ij + rk); 17 | m->r[2].x = 2.0f * (ik - rj); 18 | m->r[3].x = 0.0f; 19 | 20 | m->r[0].y = 2.0f * (ij - rk); 21 | m->r[1].y = 1.0f - (2.0f * (ii + kk)); 22 | m->r[2].y = 2.0f * (jk + ri); 23 | m->r[3].y = 0.0f; 24 | 25 | m->r[0].z = 2.0f * (ik + rj); 26 | m->r[1].z = 2.0f * (jk - ri); 27 | m->r[2].z = 1.0f - (2.0f * (ii + jj)); 28 | m->r[3].z = 0.0f; 29 | 30 | m->r[0].w = 0.0f; 31 | m->r[1].w = 0.0f; 32 | m->r[2].w = 0.0f; 33 | m->r[3].w = 1.0f; 34 | } 35 | -------------------------------------------------------------------------------- /source/maths/mtx_ortho.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Ortho(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded) 4 | { 5 | Mtx_Zeros(mtx); 6 | 7 | // Standard orthogonal projection matrix, with a fixed depth range of [-1,0] (required by PICA) 8 | // http://www.wolframalpha.com/input/?i={{1,0,0,0},{0,1,0,0},{0,0,0.5,-0.5},{0,0,0,1}}{{2/(r-l),0,0,(l%2Br)/(l-r)},{0,2/(t-b),0,(b%2Bt)/(b-t)},{0,0,2/(n-f),(n%2Bf)/(n-f)},{0,0,0,1}} 9 | 10 | mtx->r[0].x = 2.0f / (right - left); 11 | mtx->r[0].w = (left + right) / (left - right); 12 | mtx->r[1].y = 2.0f / (top - bottom); 13 | mtx->r[1].w = (bottom + top) / (bottom - top); 14 | if (isLeftHanded) 15 | mtx->r[2].z = 1.0f / (far - near); 16 | else 17 | mtx->r[2].z = 1.0f / (near - far); 18 | mtx->r[2].w = 0.5f*(near + far) / (near - far) - 0.5f; 19 | mtx->r[3].w = 1.0f; 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014-2018 fincs 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any 5 | damages arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any 8 | purpose, including commercial applications, and to alter it and 9 | redistribute it freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you 12 | must not claim that you wrote the original software. If you use 13 | this software in a product, an acknowledgment in the product 14 | documentation would be appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and 16 | must not be misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source 18 | distribution. 19 | -------------------------------------------------------------------------------- /include/c3d/effect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | void C3D_DepthMap(bool bIsZBuffer, float zScale, float zOffset); 5 | void C3D_CullFace(GPU_CULLMODE mode); 6 | void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int inputMask, int writeMask); 7 | void C3D_StencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass); 8 | void C3D_BlendingColor(u32 color); 9 | void C3D_EarlyDepthTest(bool enable, GPU_EARLYDEPTHFUNC function, u32 ref); 10 | void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask); 11 | void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref); 12 | void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha); 13 | void C3D_ColorLogicOp(GPU_LOGICOP op); 14 | void C3D_FragOpMode(GPU_FRAGOPMODE mode); 15 | void C3D_FragOpShadow(float scale, float bias); 16 | -------------------------------------------------------------------------------- /source/maths/quat_lookat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | C3D_FQuat Quat_LookAt(C3D_FVec source, C3D_FVec target, C3D_FVec forwardVector, C3D_FVec upVector) 5 | { 6 | C3D_FVec forward = FVec3_Normalize(FVec3_Subtract(target, source)); 7 | float dot = FVec3_Dot(forwardVector, forward); 8 | 9 | //Instead of checking at very high precision, this check includes margin of rounding errors for floats. 10 | if (dot + 1.0f < 0.0001f) //If dot product is -1.0f, the resulting quaternion is rotated 180 degrees on the Up axis. 11 | return Quat_FromAxisAngle(upVector, M_TAU/2.0f); 12 | if (dot - 1.0f > -0.0001f) //If dot product is 1.0f, the resulting quaternion is an identity quaternion; there's no rotation. 13 | return Quat_Identity(); 14 | 15 | float rotationAngle = acosf(dot); 16 | C3D_FVec rotationAxis = FVec3_Cross(forwardVector, forward); 17 | return Quat_FromAxisAngle(rotationAxis, rotationAngle); 18 | } 19 | -------------------------------------------------------------------------------- /source/maths/quat_pow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | C3D_FQuat Quat_Pow(C3D_FQuat q, float p) 5 | { 6 | // if the power is very near to zero, return the identity quaternion to avoid blowing up with division 7 | if (p > -FLT_EPSILON && p < FLT_EPSILON) 8 | return Quat_Identity(); 9 | 10 | float mag = FVec4_Magnitude(q); 11 | 12 | // if the magnitude is very near to one, this is equivalent to raising the real component by the power 13 | // also, acosf(1) == 0 and sinf(0) == 0 so you would get a divide-by-zero anyway 14 | if (fabsf(q.r / mag) > 1.0f - FLT_EPSILON && fabsf(q.r / mag) < 1.0f + FLT_EPSILON) 15 | return Quat_New(0.0f, 0.0f, 0.0f, powf(q.r, p)); 16 | 17 | float angle = acosf(q.r / mag); 18 | float newAngle = angle * p; 19 | float div = sinf(newAngle) / sinf(angle); 20 | float Mag = powf(mag, p - 1.0f); 21 | 22 | return Quat_New(q.i*div*Mag, q.j*div*Mag, q.k*div*Mag, cosf(newAngle)*mag*Mag); 23 | } 24 | -------------------------------------------------------------------------------- /source/maths/quat_frompitchyawroll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_FromPitchYawRoll(float pitch, float yaw, float roll, bool bRightSide) 4 | { 5 | float pitch_c = cosf(pitch / 2.0f); 6 | float pitch_s = sinf(pitch / 2.0f); 7 | float yaw_c = cosf(yaw / 2.0f); 8 | float yaw_s = sinf(yaw / 2.0f); 9 | float roll_c = cosf(roll / 2.0f); 10 | float roll_s = sinf(roll / 2.0f); 11 | 12 | if (bRightSide) 13 | { 14 | return Quat_New( 15 | pitch_s * yaw_c * roll_c - pitch_c * yaw_s * roll_s, 16 | pitch_c * yaw_s * roll_c + pitch_s * yaw_c * roll_s, 17 | pitch_c * yaw_c * roll_s - pitch_s * yaw_s * roll_c, 18 | pitch_c * yaw_c * roll_c + pitch_s * yaw_s * roll_s); 19 | } 20 | else 21 | { 22 | return Quat_New( 23 | pitch_s * yaw_c * roll_c + pitch_c * yaw_s * roll_s, 24 | pitch_c * yaw_s * roll_c - pitch_s * yaw_c * roll_s, 25 | pitch_c * yaw_c * roll_s + pitch_s * yaw_s * roll_c, 26 | pitch_c * yaw_c * roll_c - pitch_s * yaw_s * roll_s); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/maths/mtx_perspstereotilt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_PerspStereoTilt(C3D_Mtx* mtx, float fovx, float invaspect, float near, float far, float iod, float screen, bool isLeftHanded) 4 | { 5 | // Notes: 6 | // Once again, we are passed "fovy" and the "aspect ratio"; however the 3DS screens are sideways, 7 | // and the formula had to be tweaked. With stereo, left/right separation becomes top/bottom separation. 8 | // The detailed mathematical explanation is in mtx_persptilt.c. 9 | 10 | float fovx_tan = tanf(fovx/2.0f); 11 | float fovx_tan_invaspect = fovx_tan*invaspect; 12 | float shift = iod / (2.0f*screen); // 'near' not in the numerator because it cancels out in mp.r[1].z 13 | 14 | Mtx_Zeros(mtx); 15 | 16 | mtx->r[0].y = 1.0f / fovx_tan; 17 | mtx->r[1].x = -1.0f / fovx_tan_invaspect; 18 | mtx->r[1].w = iod / 2.0f; 19 | mtx->r[2].w = near * far / (near - far); 20 | mtx->r[3].z = isLeftHanded ? 1.0f : -1.0f; 21 | mtx->r[1].z = -mtx->r[3].z * shift / fovx_tan_invaspect; 22 | mtx->r[2].z = -mtx->r[3].z * near / (near - far); 23 | } 24 | -------------------------------------------------------------------------------- /source/mtxstack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void MtxStack_Init(C3D_MtxStack* stk) 5 | { 6 | stk->pos = 0; 7 | stk->unifPos = 0xFF; 8 | stk->isDirty = true; 9 | Mtx_Identity(&stk->m[0]); 10 | } 11 | 12 | void MtxStack_Bind(C3D_MtxStack* stk, GPU_SHADER_TYPE unifType, int unifPos, int unifLen) 13 | { 14 | stk->unifType = unifType; 15 | stk->unifPos = unifPos; 16 | stk->unifLen = unifLen; 17 | stk->isDirty = true; 18 | } 19 | 20 | C3D_Mtx* MtxStack_Push(C3D_MtxStack* stk) 21 | { 22 | if (stk->pos == (C3D_MTXSTACK_SIZE-1)) return NULL; 23 | stk->pos ++; 24 | Mtx_Copy(&stk->m[stk->pos], &stk->m[stk->pos-1]); 25 | return MtxStack_Cur(stk); 26 | } 27 | 28 | C3D_Mtx* MtxStack_Pop(C3D_MtxStack* stk) 29 | { 30 | if (stk->pos == 0) return NULL; 31 | stk->pos --; 32 | return MtxStack_Cur(stk); 33 | } 34 | 35 | void MtxStack_Update(C3D_MtxStack* stk) 36 | { 37 | if (!stk->isDirty) return; 38 | 39 | if (stk->unifPos != 0xFF) 40 | C3D_FVUnifMtxNx4(stk->unifType, stk->unifPos, &stk->m[stk->pos], stk->unifLen); 41 | 42 | stk->isDirty = false; 43 | } 44 | -------------------------------------------------------------------------------- /source/maths/mtx_orthotilt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_OrthoTilt(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded) 4 | { 5 | Mtx_Zeros(mtx); 6 | 7 | // Standard orthogonal projection matrix, with a fixed depth range of [-1,0] (required by PICA) and rotated τ/4 radians counterclockwise around the Z axis (due to 3DS screen orientation) 8 | // http://www.wolframalpha.com/input/?i={{0,1,0,0},{-1,0,0,0},{0,0,1,0},{0,0,0,1}}{{1,0,0,0},{0,1,0,0},{0,0,0.5,-0.5},{0,0,0,1}} 9 | // http://www.wolframalpha.com/input/?i={{0,1,0,0},{-1,0,0,0},{0,0,0.5,-0.5},{0,0,0,1}}{{2/(r-l),0,0,(l%2Br)/(l-r)},{0,2/(t-b),0,(b%2Bt)/(b-t)},{0,0,2/(n-f),(n%2Bf)/(n-f)},{0,0,0,1}} 10 | 11 | mtx->r[0].y = 2.0f / (top - bottom); 12 | mtx->r[0].w = (bottom + top) / (bottom - top); 13 | mtx->r[1].x = 2.0f / (left - right); 14 | mtx->r[1].w = (left + right) / (right - left); 15 | if (isLeftHanded) 16 | mtx->r[2].z = 1.0f / (far - near); 17 | else 18 | mtx->r[2].z = 1.0f / (near - far); 19 | mtx->r[2].w = 0.5f*(near + far) / (near - far) - 0.5f; 20 | mtx->r[3].w = 1.0f; 21 | } 22 | -------------------------------------------------------------------------------- /include/c3d/fog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | #include 4 | 5 | typedef struct 6 | { 7 | u32 data[128]; 8 | } C3D_FogLut; 9 | 10 | typedef struct 11 | { 12 | u32 diff[8]; 13 | u32 color[8]; 14 | } C3D_GasLut; 15 | 16 | static inline float FogLut_CalcZ(float depth, float near, float far) 17 | { 18 | return far*near/(depth*(far-near)+near); 19 | } 20 | 21 | void FogLut_FromArray(C3D_FogLut* lut, const float data[256]); 22 | void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, float far); 23 | 24 | void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip); 25 | void C3D_FogColor(u32 color); 26 | void C3D_FogLutBind(C3D_FogLut* lut); 27 | 28 | void GasLut_FromArray(C3D_GasLut* lut, const u32 data[9]); 29 | 30 | void C3D_GasBeginAcc(void); 31 | void C3D_GasDeltaZ(float value); 32 | 33 | void C3D_GasAccMax(float value); 34 | void C3D_GasAttn(float value); 35 | void C3D_GasLightPlanar(float min, float max, float attn); 36 | void C3D_GasLightView(float min, float max, float attn); 37 | void C3D_GasLightDirection(float dotp); 38 | void C3D_GasLutInput(GPU_GASLUTINPUT input); 39 | void C3D_GasLutBind(C3D_GasLut* lut); 40 | -------------------------------------------------------------------------------- /source/drawArrays.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void C3D_DrawArrays(GPU_Primitive_t primitive, int first, int size) 4 | { 5 | C3Di_UpdateContext(); 6 | 7 | // Set primitive type 8 | GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); 9 | // Start a new primitive (breaks off a triangle strip/fan) 10 | GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); 11 | // The index buffer is not used, but this command is still required 12 | GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); 13 | // Number of vertices 14 | GPUCMD_AddWrite(GPUREG_NUMVERTICES, size); 15 | // First vertex 16 | GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, first); 17 | // Enable array drawing mode 18 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 1); 19 | // Enable drawing mode 20 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); 21 | // Trigger array drawing 22 | GPUCMD_AddWrite(GPUREG_DRAWARRAYS, 1); 23 | // Go back to configuration mode 24 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); 25 | // Disable array drawing mode 26 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 0); 27 | // Clear the post-vertex cache 28 | GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); 29 | 30 | C3Di_GetContext()->flags |= C3DiF_DrawUsed; 31 | } 32 | -------------------------------------------------------------------------------- /include/c3d/lightlut.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | #include 4 | 5 | typedef struct 6 | { 7 | u32 data[256]; 8 | } C3D_LightLut; 9 | 10 | typedef struct 11 | { 12 | C3D_LightLut lut; 13 | float bias, scale; 14 | } C3D_LightLutDA; 15 | 16 | typedef float (* C3D_LightLutFunc)(float x, float param); 17 | typedef float (* C3D_LightLutFuncDA)(float dist, float arg0, float arg1); 18 | 19 | static inline float quadratic_dist_attn(float dist, float linear, float quad) 20 | { 21 | return 1.0f / (1.0f + linear*dist + quad*dist*dist); 22 | } 23 | 24 | static inline float spot_step(float angle, float cutoff) 25 | { 26 | return angle >= cutoff ? 1.0f : 0.0f; 27 | } 28 | 29 | void LightLut_FromArray(C3D_LightLut* lut, float* data); 30 | void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative); 31 | void LightLutDA_Create(C3D_LightLutDA* lut, C3D_LightLutFuncDA func, float from, float to, float arg0, float arg1); 32 | 33 | #define LightLut_Phong(lut, shininess) LightLut_FromFunc((lut), powf, (shininess), false) 34 | #define LightLut_Spotlight(lut, angle) LightLut_FromFunc((lut), spot_step, cosf(angle), true) 35 | #define LightLutDA_Quadratic(lut, from, to, linear, quad) LightLutDA_Create((lut), quadratic_dist_attn, (from), (to), (linear), (quad)) 36 | -------------------------------------------------------------------------------- /test/pc/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := test 2 | 3 | CFILES := $(wildcard *.c) $(wildcard ../../source/maths/*.c) 4 | CXXFILES := $(wildcard *.cpp) 5 | OFILES := $(addprefix build/,$(CXXFILES:.cpp=.o)) \ 6 | $(patsubst ../../source/maths/%,build/%,$(CFILES:.c=.o)) 7 | DFILES := $(wildcard build/*.d) 8 | 9 | CFLAGS := -Wall -g -pipe -I../../include --coverage 10 | CXXFLAGS := $(CFLAGS) $(CPPFLAGS) -std=gnu++11 -DGLM_FORCE_RADIANS 11 | LDFLAGS := $(ARCH) -pipe -lm --coverage 12 | 13 | .PHONY: all clean lcov 14 | 15 | all: $(TARGET) 16 | 17 | $(TARGET): $(OFILES) 18 | @echo "Linking $@" 19 | $(CXX) -o $@ $^ $(LDFLAGS) 20 | 21 | lcov: all 22 | @./$(TARGET) 23 | @lcov --capture --no-external --directory ../../include --directory ../../source --directory ../../test/pc --output-file coverage.info 24 | @genhtml coverage.info --output-directory lcov 25 | 26 | $(OFILES): | build 27 | 28 | build: 29 | @[ -d build ] || mkdir build 30 | 31 | build/%.o : %.cpp $(wildcard *.h) 32 | @echo "Compiling $@" 33 | @$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF build/$*.d 34 | 35 | build/%.o : ../../source/maths/%.c $(wildcard *.h) 36 | @echo "Compiling $@" 37 | @$(CC) -o $@ -c $< $(CFLAGS) -MMD -MP -MF build/$*.d 38 | 39 | clean: 40 | $(RM) -r $(TARGET) build/ coverage.info lcov/ 41 | 42 | -include $(DFILES) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # citro3d 2 | 3 | citro3d is a library that provides an easy to use stateful interface to the PICA200 GPU of the Nintendo 3DS. It tries to expose hardware functionality in the way that is most natural and convenient to the GPU and the user, therefore deviating from openGL. 4 | 5 | # Setup 6 | 7 | citro3d can be built and installed using the following command: 8 | 9 | make install 10 | 11 | # License 12 | 13 | This software is provided 'as-is', without any express or implied 14 | warranty. In no event will the authors be held liable for any 15 | damages arising from the use of this software. 16 | 17 | Permission is granted to anyone to use this software for any 18 | purpose, including commercial applications, and to alter it and 19 | redistribute it freely, subject to the following restrictions: 20 | 21 | 1. The origin of this software must not be misrepresented; you 22 | must not claim that you wrote the original software. If you use 23 | this software in a product, an acknowledgment in the product 24 | documentation would be appreciated but is not required. 25 | 2. Altered source versions must be plainly marked as such, and 26 | must not be misrepresented as being the original software. 27 | 3. This notice may not be removed or altered from any source 28 | distribution. 29 | -------------------------------------------------------------------------------- /include/c3d/base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "buffers.h" 3 | #include "maths.h" 4 | 5 | #define C3D_DEFAULT_CMDBUF_SIZE 0x40000 6 | 7 | enum 8 | { 9 | C3D_UNSIGNED_BYTE = 0, 10 | C3D_UNSIGNED_SHORT = 1, 11 | }; 12 | 13 | bool C3D_Init(size_t cmdBufSize); 14 | void C3D_Fini(void); 15 | 16 | float C3D_GetCmdBufUsage(void); 17 | 18 | void C3D_BindProgram(shaderProgram_s* program); 19 | 20 | void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h); 21 | void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom); 22 | 23 | void C3D_DrawArrays(GPU_Primitive_t primitive, int first, int size); 24 | void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices); 25 | 26 | // Immediate-mode vertex submission 27 | void C3D_ImmDrawBegin(GPU_Primitive_t primitive); 28 | void C3D_ImmSendAttrib(float x, float y, float z, float w); 29 | void C3D_ImmDrawEnd(void); 30 | 31 | static inline void C3D_ImmDrawRestartPrim(void) 32 | { 33 | GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); 34 | } 35 | 36 | // Fixed vertex attributes 37 | C3D_FVec* C3D_FixedAttribGetWritePtr(int id); 38 | 39 | static inline void C3D_FixedAttribSet(int id, float x, float y, float z, float w) 40 | { 41 | C3D_FVec* ptr = C3D_FixedAttribGetWritePtr(id); 42 | ptr->x = x; 43 | ptr->y = y; 44 | ptr->z = z; 45 | ptr->w = w; 46 | } 47 | -------------------------------------------------------------------------------- /source/maths/mtx_lookat.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_LookAt(C3D_Mtx* out, C3D_FVec cameraPosition, C3D_FVec cameraTarget, C3D_FVec cameraUpVector, bool isLeftHanded) 4 | { 5 | //Left-handed and Right-handed Look-At matrix functions are DirectX implementations. 6 | //Left-handed: https://msdn.microsoft.com/en-us/library/windows/desktop/bb281710 7 | //Right-handed: https://msdn.microsoft.com/en-us/library/windows/desktop/bb281711 8 | C3D_FVec xaxis, yaxis, zaxis; 9 | 10 | //Order of operations is crucial. 11 | if (isLeftHanded) 12 | zaxis = FVec3_Normalize(FVec3_Subtract(cameraTarget, cameraPosition)); 13 | else 14 | zaxis = FVec3_Normalize(FVec3_Subtract(cameraPosition, cameraTarget)); 15 | xaxis = FVec3_Normalize(FVec3_Cross(cameraUpVector, zaxis)); 16 | yaxis = FVec3_Cross(zaxis, xaxis); 17 | 18 | out->r[0].x = xaxis.x; 19 | out->r[0].y = xaxis.y; 20 | out->r[0].z = xaxis.z; 21 | out->r[0].w = -FVec3_Dot(xaxis, cameraPosition); 22 | 23 | out->r[1].x = yaxis.x; 24 | out->r[1].y = yaxis.y; 25 | out->r[1].z = yaxis.z; 26 | out->r[1].w = -FVec3_Dot(yaxis, cameraPosition); 27 | 28 | out->r[2].x = zaxis.x; 29 | out->r[2].y = zaxis.y; 30 | out->r[2].z = zaxis.z; 31 | out->r[2].w = -FVec3_Dot(zaxis, cameraPosition); 32 | 33 | out->r[3].x = 0.0f; 34 | out->r[3].y = 0.0f; 35 | out->r[3].z = 0.0f; 36 | out->r[3].w = 1.0f; 37 | } 38 | -------------------------------------------------------------------------------- /source/maths/mtx_persptilt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_PerspTilt(C3D_Mtx* mtx, float fovx, float invaspect, float near, float far, bool isLeftHanded) 4 | { 5 | // Notes: 6 | // We are passed "fovy" and the "aspect ratio". However, the 3DS screens are sideways, 7 | // and so are these parameters -- in fact, they are actually the fovx and the inverse 8 | // of the aspect ratio. Therefore the formula for the perspective projection matrix 9 | // had to be modified to be expressed in these terms instead. 10 | 11 | // Notes: 12 | // Includes adjusting depth range from [-1,1] to [-1,0] 13 | // Includes rotation of the matrix one quarter of a turn clockwise in order to fix the 3DS screens' orientation 14 | 15 | // Notes: 16 | // fovx = 2 atan(tan(fovy/2)*w/h) 17 | // fovy = 2 atan(tan(fovx/2)*h/w) 18 | // invaspect = h/w 19 | 20 | // a0,0 = h / (w*tan(fovy/2)) = 21 | // = h / (w*tan(2 atan(tan(fovx/2)*h/w) / 2)) = 22 | // = h / (w*tan( atan(tan(fovx/2)*h/w) )) = 23 | // = h / (w * tan(fovx/2)*h/w) = 24 | // = 1 / tan(fovx/2) 25 | 26 | // a1,1 = 1 / tan(fovy/2) = (...) = w / (h*tan(fovx/2)) 27 | 28 | float fovx_tan = tanf(fovx/2.0f); 29 | 30 | Mtx_Zeros(mtx); 31 | 32 | mtx->r[0].y = 1.0f / fovx_tan; 33 | mtx->r[1].x = -1.0f / (fovx_tan*invaspect); 34 | mtx->r[2].w = far*near / (near - far); 35 | mtx->r[3].z = isLeftHanded ? 1.0f : -1.0f; 36 | mtx->r[2].z = -mtx->r[3].z * near / (near - far); 37 | } 38 | -------------------------------------------------------------------------------- /source/buffers.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | #define BUFFER_BASE_PADDR 0x18000000 4 | 5 | void BufInfo_Init(C3D_BufInfo* info) 6 | { 7 | memset(info, 0, sizeof(*info)); 8 | info->base_paddr = BUFFER_BASE_PADDR; 9 | } 10 | 11 | int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation) 12 | { 13 | if (info->bufCount == 12) return -1; 14 | int id = info->bufCount++; 15 | 16 | u32 pa = osConvertVirtToPhys(data); 17 | if (pa < info->base_paddr) return -2; 18 | 19 | C3D_BufCfg* buf = &info->buffers[id]; 20 | buf->offset = pa - info->base_paddr; 21 | buf->flags[0] = permutation & 0xFFFFFFFF; 22 | buf->flags[1] = (permutation >> 32) | (stride << 16) | (attribCount << 28); 23 | return id; 24 | } 25 | 26 | C3D_BufInfo* C3D_GetBufInfo(void) 27 | { 28 | C3D_Context* ctx = C3Di_GetContext(); 29 | 30 | if (!(ctx->flags & C3DiF_Active)) 31 | return NULL; 32 | 33 | ctx->flags |= C3DiF_BufInfo; 34 | return &ctx->bufInfo; 35 | } 36 | 37 | void C3D_SetBufInfo(C3D_BufInfo* info) 38 | { 39 | C3D_Context* ctx = C3Di_GetContext(); 40 | 41 | if (!(ctx->flags & C3DiF_Active)) 42 | return; 43 | 44 | if (info != &ctx->bufInfo) 45 | memcpy(&ctx->bufInfo, info, sizeof(*info)); 46 | ctx->flags |= C3DiF_BufInfo; 47 | } 48 | 49 | void C3Di_BufInfoBind(C3D_BufInfo* info) 50 | { 51 | GPUCMD_AddWrite(GPUREG_ATTRIBBUFFERS_LOC, info->base_paddr >> 3); 52 | GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFER0_OFFSET, (u32*)info->buffers, sizeof(info->buffers)/sizeof(u32)); 53 | } 54 | -------------------------------------------------------------------------------- /include/c3d/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if defined(__3DS__) || defined(_3DS) 3 | #include <3ds.h> 4 | #else 5 | #include 6 | #include 7 | typedef uint8_t u8; 8 | typedef uint32_t u32; 9 | #endif 10 | 11 | #ifndef CITRO3D_NO_DEPRECATION 12 | #define C3D_DEPRECATED __attribute__ ((deprecated)) 13 | #else 14 | #define C3D_DEPRECATED 15 | #endif 16 | 17 | typedef u32 C3D_IVec; 18 | 19 | static inline C3D_IVec IVec_Pack(u8 x, u8 y, u8 z, u8 w) 20 | { 21 | return (u32)x | ((u32)y << 8) | ((u32)z << 16) | ((u32)w << 24); 22 | } 23 | 24 | /** 25 | * @defgroup math_support Math Support Library 26 | * @brief Implementations of matrix, vector, and quaternion operations. 27 | * @{ 28 | */ 29 | 30 | /** 31 | * @struct C3D_FVec 32 | * @brief Float vector 33 | * 34 | * Matches PICA layout 35 | */ 36 | typedef union 37 | { 38 | /** 39 | * @brief Vector access 40 | */ 41 | struct 42 | { 43 | float w; ///< W-component 44 | float z; ///< Z-component 45 | float y; ///< Y-component 46 | float x; ///< X-component 47 | }; 48 | 49 | /** 50 | * @brief Quaternion access 51 | */ 52 | struct 53 | { 54 | float r; ///< Real component 55 | float k; ///< K-component 56 | float j; ///< J-component 57 | float i; ///< I-component 58 | }; 59 | 60 | /** 61 | * @brief Raw access 62 | */ 63 | float c[4]; 64 | } C3D_FVec; 65 | 66 | /** 67 | * @struct C3D_FQuat 68 | * @brief Float quaternion. See @ref C3D_FVec. 69 | */ 70 | typedef C3D_FVec C3D_FQuat; 71 | 72 | /** 73 | * @struct C3D_Mtx 74 | * @brief Row-major 4x4 matrix 75 | */ 76 | typedef union 77 | { 78 | C3D_FVec r[4]; ///< Rows are vectors 79 | float m[4*4]; ///< Raw access 80 | } C3D_Mtx; 81 | /** @} */ 82 | -------------------------------------------------------------------------------- /source/lightlut.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void LightLut_FromArray(C3D_LightLut* lut, float* data) 4 | { 5 | int i; 6 | for (i = 0; i < 256; i ++) 7 | { 8 | float in = data[i], diff = data[i+256]; 9 | 10 | u32 val = 0; 11 | if (in > 0.0f) 12 | { 13 | in *= 0x1000; 14 | val = (in < 0x1000) ? (u32)in : 0xFFF; 15 | } 16 | 17 | u32 val2 = 0; 18 | if (diff != 0.0f) 19 | { 20 | if (diff < 0) 21 | { 22 | diff = -diff; 23 | val2 = 0x800; 24 | } 25 | diff *= 0x800; 26 | val2 |= (diff < 0x800) ? (u32)diff : 0x7FF; 27 | } 28 | 29 | lut->data[i] = val | (val2 << 12); 30 | } 31 | } 32 | 33 | void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative) 34 | { 35 | int i; 36 | float data[512]; 37 | memset(data, 0, sizeof(data)); 38 | int min = negative ? (-128) : 0; 39 | int max = negative ? 128 : 256; 40 | for (i = min; i <= max; i ++) 41 | { 42 | float x = (float)i/max; 43 | float val = func(x, param); 44 | int idx = negative ? (i & 0xFF) : i; 45 | if (i < max) 46 | data[idx] = val; 47 | if (i > min) 48 | data[idx+255] = val-data[idx-1]; 49 | } 50 | LightLut_FromArray(lut, data); 51 | } 52 | 53 | void LightLutDA_Create(C3D_LightLutDA* lut, C3D_LightLutFuncDA func, float from, float to, float arg0, float arg1) 54 | { 55 | int i; 56 | float data[512]; 57 | 58 | float range = to-from; 59 | lut->scale = 1.0f / range; 60 | lut->bias = -from*lut->scale; 61 | 62 | for (i = 0; i <= 256; i ++) 63 | { 64 | float x = from + range*i/256.0f; 65 | float val = func(x, arg0, arg1); 66 | if (i < 256) 67 | data[i] = val; 68 | if (i > 0) 69 | data[i+255] = val-data[i-1]; 70 | } 71 | 72 | LightLut_FromArray(&lut->lut, data); 73 | } 74 | -------------------------------------------------------------------------------- /source/texenv.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | C3D_TexEnv* C3D_GetTexEnv(int id) 4 | { 5 | C3D_Context* ctx = C3Di_GetContext(); 6 | 7 | if (!(ctx->flags & C3DiF_Active)) 8 | return NULL; 9 | 10 | ctx->flags |= C3DiF_TexEnv(id); 11 | return &ctx->texEnv[id]; 12 | } 13 | 14 | void C3D_SetTexEnv(int id, C3D_TexEnv* env) 15 | { 16 | C3D_Context* ctx = C3Di_GetContext(); 17 | 18 | if (!(ctx->flags & C3DiF_Active)) 19 | return; 20 | 21 | ctx->flags |= C3DiF_TexEnv(id); 22 | if (env) 23 | memcpy(&ctx->texEnv[id], env, sizeof(*env)); 24 | else 25 | C3D_TexEnvInit(&ctx->texEnv[id]); 26 | } 27 | 28 | void C3D_DirtyTexEnv(C3D_TexEnv* env) 29 | { 30 | C3D_Context* ctx = C3Di_GetContext(); 31 | 32 | if (!(ctx->flags & C3DiF_Active)) 33 | return; 34 | 35 | u32 id = env-ctx->texEnv; 36 | if (id < 6) 37 | ctx->flags |= C3DiF_TexEnv(id); 38 | } 39 | 40 | void C3Di_TexEnvBind(int id, C3D_TexEnv* env) 41 | { 42 | if (id >= 4) id += 2; 43 | GPUCMD_AddIncrementalWrites(GPUREG_TEXENV0_SOURCE + id*8, (u32*)env, sizeof(C3D_TexEnv)/sizeof(u32)); 44 | } 45 | 46 | void C3D_TexEnvBufUpdate(int mode, int mask) 47 | { 48 | C3D_Context* ctx = C3Di_GetContext(); 49 | 50 | if (!(ctx->flags & C3DiF_Active)) 51 | return; 52 | 53 | u32 val = ctx->texEnvBuf; 54 | mask &= 0xF; 55 | 56 | if (mode & C3D_RGB) 57 | { 58 | val &= ~(0xF << 8); 59 | val |= mask << 8; 60 | } 61 | 62 | if (mode & C3D_Alpha) 63 | { 64 | val &= ~(0xF << 12); 65 | val |= mask << 12; 66 | } 67 | 68 | ctx->texEnvBuf = val; 69 | ctx->flags |= C3DiF_TexEnvBuf; 70 | } 71 | 72 | void C3D_TexEnvBufColor(u32 color) 73 | { 74 | C3D_Context* ctx = C3Di_GetContext(); 75 | 76 | if (!(ctx->flags & C3DiF_Active)) 77 | return; 78 | 79 | ctx->texEnvBufClr = color; 80 | ctx->flags |= C3DiF_TexEnvBuf; 81 | } 82 | -------------------------------------------------------------------------------- /source/drawElements.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices) 4 | { 5 | C3D_Context* ctx = C3Di_GetContext(); 6 | u32 pa = osConvertVirtToPhys(indices); 7 | u32 base = ctx->bufInfo.base_paddr; 8 | if (pa < base) return; 9 | 10 | C3Di_UpdateContext(); 11 | 12 | // Set primitive type 13 | GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive != GPU_TRIANGLES ? primitive : GPU_GEOMETRY_PRIM); 14 | // Start a new primitive (breaks off a triangle strip/fan) 15 | GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); 16 | // Configure the index buffer 17 | GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, (pa - base) | (type << 31)); 18 | // Number of vertices 19 | GPUCMD_AddWrite(GPUREG_NUMVERTICES, count); 20 | // First vertex 21 | GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, 0); 22 | // Enable triangle element drawing mode if necessary 23 | if (primitive == GPU_TRIANGLES) 24 | { 25 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0x100); 26 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0x100); 27 | } 28 | // Enable drawing mode 29 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); 30 | // Trigger element drawing 31 | GPUCMD_AddWrite(GPUREG_DRAWELEMENTS, 1); 32 | // Go back to configuration mode 33 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); 34 | // Disable triangle element drawing mode if necessary 35 | if (primitive == GPU_TRIANGLES) 36 | { 37 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0); 38 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0); 39 | } 40 | // Clear the post-vertex cache 41 | GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); 42 | GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x8, 0); 43 | GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x8, 0); 44 | 45 | C3Di_GetContext()->flags |= C3DiF_DrawUsed; 46 | } 47 | -------------------------------------------------------------------------------- /source/maths/mtx_rotate.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Mtx_Rotate(C3D_Mtx* mtx, C3D_FVec axis, float angle, bool bRightSide) 4 | { 5 | size_t i; 6 | C3D_Mtx om; 7 | 8 | float s = sinf(angle); 9 | float c = cosf(angle); 10 | float t = 1.0f - c; 11 | 12 | axis = FVec3_Normalize(axis); 13 | 14 | float x = axis.x; 15 | float y = axis.y; 16 | float z = axis.z; 17 | float w; 18 | 19 | om.r[0].x = t*x*x + c; 20 | om.r[1].x = t*x*y + s*z; 21 | om.r[2].x = t*x*z - s*y; 22 | //om.r[3].x = 0.0f; //optimized out 23 | 24 | om.r[0].y = t*y*x - s*z; 25 | om.r[1].y = t*y*y + c; 26 | om.r[2].y = t*y*z + s*x; 27 | //om.r[3].y = 0.0f; //optimized out 28 | 29 | om.r[0].z = t*z*x + s*y; 30 | om.r[1].z = t*z*y - s*x; 31 | om.r[2].z = t*z*z + c; 32 | //om.r[3].z = 0.0f; //optimized out 33 | 34 | /* optimized out 35 | om.r[0].w = 0.0f; 36 | om.r[1].w = 0.0f; 37 | om.r[2].w = 0.0f; 38 | om.r[3].w = 1.0f; 39 | */ 40 | 41 | if (bRightSide) 42 | { 43 | for (i = 0; i < 4; ++i) 44 | { 45 | x = mtx->r[i].x*om.r[0].x + mtx->r[i].y*om.r[1].x + mtx->r[i].z*om.r[2].x; 46 | y = mtx->r[i].x*om.r[0].y + mtx->r[i].y*om.r[1].y + mtx->r[i].z*om.r[2].y; 47 | z = mtx->r[i].x*om.r[0].z + mtx->r[i].y*om.r[1].z + mtx->r[i].z*om.r[2].z; 48 | 49 | mtx->r[i].x = x; 50 | mtx->r[i].y = y; 51 | mtx->r[i].z = z; 52 | } 53 | } 54 | else 55 | { 56 | for (i = 0; i < 3; ++i) 57 | { 58 | x = mtx->r[0].x*om.r[i].x + mtx->r[1].x*om.r[i].y + mtx->r[2].x*om.r[i].z; 59 | y = mtx->r[0].y*om.r[i].x + mtx->r[1].y*om.r[i].y + mtx->r[2].y*om.r[i].z; 60 | z = mtx->r[0].z*om.r[i].x + mtx->r[1].z*om.r[i].y + mtx->r[2].z*om.r[i].z; 61 | w = mtx->r[0].w*om.r[i].x + mtx->r[1].w*om.r[i].y + mtx->r[2].w*om.r[i].z; 62 | 63 | om.r[i].x = x; 64 | om.r[i].y = y; 65 | om.r[i].z = z; 66 | om.r[i].w = w; 67 | } 68 | 69 | for (i = 0; i < 3; ++i) 70 | mtx->r[i] = om.r[i]; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/fog.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void FogLut_FromArray(C3D_FogLut* lut, const float data[256]) 4 | { 5 | int i; 6 | for (i = 0; i < 128; i ++) 7 | { 8 | float in = data[i], diff = data[i+128]; 9 | 10 | u32 val = 0; 11 | if (in > 0.0f) 12 | { 13 | in *= 0x800; 14 | val = (in < 0x800) ? (u32)in : 0x7FF; 15 | } 16 | 17 | u32 val2 = 0; 18 | if (diff != 0.0f) 19 | { 20 | diff *= 0x800; 21 | if (diff < -0x1000) diff = -0x1000; 22 | else if (diff > 0xFFF) diff = 0xFFF; 23 | val2 = (s32)diff & 0x1FFF; 24 | } 25 | 26 | lut->data[i] = val2 | (val << 13); 27 | } 28 | } 29 | 30 | void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, float far) 31 | { 32 | int i; 33 | float data[256]; 34 | for (i = 0; i <= 128; i ++) 35 | { 36 | float x = FogLut_CalcZ(i/128.0f, near, far); 37 | float val = expf(-powf(density*x, gradient)); 38 | if (i < 128) 39 | data[i] = val; 40 | if (i > 0) 41 | data[i+127] = val-data[i-1]; 42 | } 43 | FogLut_FromArray(lut, data); 44 | } 45 | 46 | void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip) 47 | { 48 | C3D_Context* ctx = C3Di_GetContext(); 49 | 50 | if (!(ctx->flags & C3DiF_Active)) 51 | return; 52 | 53 | ctx->flags |= C3DiF_TexEnvBuf; 54 | ctx->texEnvBuf &= ~0x100FF; 55 | ctx->texEnvBuf |= (fogMode&7) | ((gasMode&1)<<3) | (zFlip ? BIT(16) : 0); 56 | } 57 | 58 | void C3D_FogColor(u32 color) 59 | { 60 | C3D_Context* ctx = C3Di_GetContext(); 61 | 62 | if (!(ctx->flags & C3DiF_Active)) 63 | return; 64 | 65 | ctx->flags |= C3DiF_TexEnvBuf; 66 | ctx->fogClr = color; 67 | } 68 | 69 | void C3D_FogLutBind(C3D_FogLut* lut) 70 | { 71 | C3D_Context* ctx = C3Di_GetContext(); 72 | 73 | if (!(ctx->flags & C3DiF_Active)) 74 | return; 75 | 76 | if (lut) 77 | { 78 | ctx->flags |= C3DiF_FogLut; 79 | ctx->fogLut = lut; 80 | } else 81 | ctx->flags &= ~C3DiF_FogLut; 82 | } 83 | -------------------------------------------------------------------------------- /source/immediate.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void C3D_ImmDrawBegin(GPU_Primitive_t primitive) 4 | { 5 | C3Di_UpdateContext(); 6 | 7 | // Set primitive type 8 | GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); 9 | // Start a new primitive (breaks off a triangle strip/fan) 10 | GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); 11 | // Not sure if this command is necessary 12 | GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); 13 | // Enable vertex submission mode 14 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 1); 15 | // Enable drawing mode 16 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); 17 | // Begin immediate-mode vertex submission 18 | GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, 0xF); 19 | } 20 | 21 | static inline void write24(u8* p, u32 val) 22 | { 23 | p[0] = val; 24 | p[1] = val>>8; 25 | p[2] = val>>16; 26 | } 27 | 28 | void C3D_ImmSendAttrib(float x, float y, float z, float w) 29 | { 30 | union 31 | { 32 | u32 packed[3]; 33 | struct 34 | { 35 | u8 x[3]; 36 | u8 y[3]; 37 | u8 z[3]; 38 | u8 w[3]; 39 | }; 40 | } param; 41 | 42 | // Convert the values to float24 43 | write24(param.x, f32tof24(x)); 44 | write24(param.y, f32tof24(y)); 45 | write24(param.z, f32tof24(z)); 46 | write24(param.w, f32tof24(w)); 47 | 48 | // Reverse the packed words 49 | u32 p = param.packed[0]; 50 | param.packed[0] = param.packed[2]; 51 | param.packed[2] = p; 52 | 53 | // Send the attribute 54 | GPUCMD_AddIncrementalWrites(GPUREG_FIXEDATTRIB_DATA0, param.packed, 3); 55 | } 56 | 57 | void C3D_ImmDrawEnd(void) 58 | { 59 | // Go back to configuration mode 60 | GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); 61 | // Disable vertex submission mode 62 | GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 0); 63 | // Clear the post-vertex cache 64 | GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); 65 | 66 | C3Di_GetContext()->flags |= C3DiF_DrawUsed; 67 | } 68 | -------------------------------------------------------------------------------- /include/c3d/framebuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "texture.h" 3 | 4 | typedef struct 5 | { 6 | void* colorBuf; 7 | void* depthBuf; 8 | u16 width; 9 | u16 height; 10 | GPU_COLORBUF colorFmt; 11 | GPU_DEPTHBUF depthFmt; 12 | bool block32; 13 | u8 colorMask : 4; 14 | u8 depthMask : 4; 15 | } C3D_FrameBuf; 16 | 17 | // Flags for C3D_FrameBufClear 18 | typedef enum 19 | { 20 | C3D_CLEAR_COLOR = BIT(0), 21 | C3D_CLEAR_DEPTH = BIT(1), 22 | C3D_CLEAR_ALL = C3D_CLEAR_COLOR | C3D_CLEAR_DEPTH, 23 | } C3D_ClearBits; 24 | 25 | u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt); 26 | u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt); 27 | 28 | C3D_FrameBuf* C3D_GetFrameBuf(void); 29 | void C3D_SetFrameBuf(C3D_FrameBuf* fb); 30 | void C3D_FrameBufTex(C3D_FrameBuf* fb, C3D_Tex* tex, GPU_TEXFACE face, int level); 31 | void C3D_FrameBufClear(C3D_FrameBuf* fb, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth); 32 | void C3D_FrameBufTransfer(C3D_FrameBuf* fb, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); 33 | 34 | static inline void C3D_FrameBufAttrib(C3D_FrameBuf* fb, u16 width, u16 height, bool block32) 35 | { 36 | fb->width = width; 37 | fb->height = height; 38 | fb->block32 = block32; 39 | } 40 | 41 | static inline void C3D_FrameBufColor(C3D_FrameBuf* fb, void* buf, GPU_COLORBUF fmt) 42 | { 43 | if (buf) 44 | { 45 | fb->colorBuf = buf; 46 | fb->colorFmt = fmt; 47 | fb->colorMask = 0xF; 48 | } else 49 | { 50 | fb->colorBuf = NULL; 51 | fb->colorFmt = GPU_RB_RGBA8; 52 | fb->colorMask = 0; 53 | } 54 | } 55 | 56 | static inline void C3D_FrameBufDepth(C3D_FrameBuf* fb, void* buf, GPU_DEPTHBUF fmt) 57 | { 58 | if (buf) 59 | { 60 | fb->depthBuf = buf; 61 | fb->depthFmt = fmt; 62 | fb->depthMask = fmt == GPU_RB_DEPTH24_STENCIL8 ? 0x3 : 0x2; 63 | } else 64 | { 65 | fb->depthBuf = NULL; 66 | fb->depthFmt = GPU_RB_DEPTH24; 67 | fb->depthMask = 0; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /source/attribs.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | #include 3 | 4 | void AttrInfo_Init(C3D_AttrInfo* info) 5 | { 6 | memset(info, 0, sizeof(*info)); 7 | info->flags[1] = 0xFFF << 16; 8 | } 9 | 10 | int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count) 11 | { 12 | if (info->attrCount == 12) return -1; 13 | int id = info->attrCount++; 14 | if (regId < 0) regId = id; 15 | if (id < 8) 16 | info->flags[0] |= GPU_ATTRIBFMT(id, count, format); 17 | else 18 | info->flags[1] |= GPU_ATTRIBFMT(id-8, count, format); 19 | 20 | info->flags[1] = (info->flags[1] &~ (0xF0000000 | BIT(id+16))) | (id << 28); 21 | info->permutation |= regId << (id*4); 22 | return id; 23 | } 24 | 25 | int AttrInfo_AddFixed(C3D_AttrInfo* info, int regId) 26 | { 27 | if (info->attrCount == 12) return -1; 28 | int id = info->attrCount++; 29 | if (regId < 0) regId = id; 30 | 31 | info->flags[1] = (info->flags[1] &~ 0xF0000000) | (id << 28); 32 | info->permutation |= regId << (id*4); 33 | return id; 34 | } 35 | 36 | C3D_AttrInfo* C3D_GetAttrInfo(void) 37 | { 38 | C3D_Context* ctx = C3Di_GetContext(); 39 | 40 | if (!(ctx->flags & C3DiF_Active)) 41 | return NULL; 42 | 43 | ctx->flags |= C3DiF_AttrInfo; 44 | return &ctx->attrInfo; 45 | } 46 | 47 | void C3D_SetAttrInfo(C3D_AttrInfo* info) 48 | { 49 | C3D_Context* ctx = C3Di_GetContext(); 50 | 51 | if (!(ctx->flags & C3DiF_Active)) 52 | return; 53 | 54 | if (info != &ctx->attrInfo) 55 | memcpy(&ctx->attrInfo, info, sizeof(*info)); 56 | ctx->flags |= C3DiF_AttrInfo; 57 | } 58 | 59 | void C3Di_AttrInfoBind(C3D_AttrInfo* info) 60 | { 61 | GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFERS_FORMAT_LOW, (u32*)info->flags, sizeof(info->flags)/sizeof(u32)); 62 | GPUCMD_AddMaskedWrite(GPUREG_VSH_INPUTBUFFER_CONFIG, 0xB, 0xA0000000 | (info->attrCount - 1)); 63 | GPUCMD_AddWrite(GPUREG_VSH_NUM_ATTR, info->attrCount - 1); 64 | GPUCMD_AddIncrementalWrites(GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW, (u32*)&info->permutation, 2); 65 | } 66 | -------------------------------------------------------------------------------- /source/maths/quat_frommtx.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | C3D_FQuat Quat_FromMtx(const C3D_Mtx* m) 4 | { 5 | // Original algorithm taken from here (with some optimizations): 6 | // https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf 7 | // Layout of the algorithm: 8 | // First, we select a large (non-zero!) component "P" in the output quaternion (q) 9 | // (we can test this just by looking at the diagonal of the matrix) 10 | // Second, we calculate q' which is our desired output quaternion (q) scaled by 4P 11 | // (this can be done with simple additions; the 4P factor is large and non-zero thanks to above) 12 | // Third, we normalize q' to finally obtain q 13 | // (this will work because normalize(kq) = q for any k scalar and q unit quaternion) 14 | 15 | C3D_FQuat q; 16 | C3D_FVec diagonal = FVec4_New(m->r[0].x, m->r[1].y, m->r[2].z, 1.0f); 17 | 18 | // Check if x^2 + y^2 >= z^2 + w^2 19 | if (diagonal.z <= 0.0f) 20 | { 21 | // Check if |x| >= |y| 22 | if (diagonal.x >= diagonal.y) 23 | { 24 | // X case 25 | q.x = diagonal.w + diagonal.x - diagonal.y - diagonal.z; 26 | q.y = m->r[1].x + m->r[0].y; 27 | q.z = m->r[2].x + m->r[0].z; 28 | q.w = m->r[2].y - m->r[1].z; 29 | } 30 | else 31 | { 32 | // Y case 33 | q.x = m->r[1].x + m->r[0].y; 34 | q.y = diagonal.w - diagonal.x + diagonal.y - diagonal.z; 35 | q.z = m->r[2].y + m->r[1].z; 36 | q.w = m->r[0].z - m->r[2].x; 37 | } 38 | } 39 | else 40 | { 41 | // Check if |z| >= |w| 42 | if (-diagonal.x >= diagonal.y) 43 | { 44 | // Z case 45 | q.x = m->r[2].x + m->r[0].z; 46 | q.y = m->r[2].y + m->r[1].z; 47 | q.z = diagonal.w - diagonal.x - diagonal.y + diagonal.z; 48 | q.w = m->r[1].x - m->r[0].y; 49 | } 50 | else 51 | { 52 | // W case 53 | q.x = m->r[2].y - m->r[1].z; 54 | q.y = m->r[0].z - m->r[2].x; 55 | q.z = m->r[1].x - m->r[0].y; 56 | q.w = diagonal.w + diagonal.x + diagonal.y + diagonal.z; 57 | } 58 | } 59 | 60 | // Normalize the quaternion 61 | return Quat_Normalize(q); 62 | } 63 | -------------------------------------------------------------------------------- /include/c3d/uniforms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "maths.h" 3 | 4 | #define C3D_FVUNIF_COUNT 96 5 | #define C3D_IVUNIF_COUNT 4 6 | 7 | extern C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; 8 | extern C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; 9 | extern u16 C3D_BoolUnifs[2]; 10 | 11 | extern bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; 12 | extern bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; 13 | extern bool C3D_BoolUnifsDirty[2]; 14 | 15 | static inline C3D_FVec* C3D_FVUnifWritePtr(GPU_SHADER_TYPE type, int id, int size) 16 | { 17 | int i; 18 | for (i = 0; i < size; i ++) 19 | C3D_FVUnifDirty[type][id+i] = true; 20 | return &C3D_FVUnif[type][id]; 21 | } 22 | 23 | static inline C3D_IVec* C3D_IVUnifWritePtr(GPU_SHADER_TYPE type, int id) 24 | { 25 | id -= 0x60; 26 | C3D_IVUnifDirty[type][id] = true; 27 | return &C3D_IVUnif[type][id]; 28 | } 29 | 30 | static inline void C3D_FVUnifMtxNx4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx, int num) 31 | { 32 | int i; 33 | C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, num); 34 | for (i = 0; i < num; i ++) 35 | ptr[i] = mtx->r[i]; // Struct copy. 36 | } 37 | 38 | static inline void C3D_FVUnifMtx4x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) 39 | { 40 | C3D_FVUnifMtxNx4(type, id, mtx, 4); 41 | } 42 | 43 | static inline void C3D_FVUnifMtx3x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) 44 | { 45 | C3D_FVUnifMtxNx4(type, id, mtx, 3); 46 | } 47 | 48 | static inline void C3D_FVUnifMtx2x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) 49 | { 50 | C3D_FVUnifMtxNx4(type, id, mtx, 2); 51 | } 52 | 53 | static inline void C3D_FVUnifSet(GPU_SHADER_TYPE type, int id, float x, float y, float z, float w) 54 | { 55 | C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, 1); 56 | ptr->x = x; 57 | ptr->y = y; 58 | ptr->z = z; 59 | ptr->w = w; 60 | } 61 | 62 | static inline void C3D_IVUnifSet(GPU_SHADER_TYPE type, int id, int x, int y, int z, int w) 63 | { 64 | C3D_IVec* ptr = C3D_IVUnifWritePtr(type, id); 65 | *ptr = IVec_Pack(x, y, z, w); 66 | } 67 | 68 | static inline void C3D_BoolUnifSet(GPU_SHADER_TYPE type, int id, bool value) 69 | { 70 | id -= 0x68; 71 | C3D_BoolUnifsDirty[type] = true; 72 | if (value) 73 | C3D_BoolUnifs[type] |= BIT(id); 74 | else 75 | C3D_BoolUnifs[type] &= ~BIT(id); 76 | } 77 | 78 | void C3D_UpdateUniforms(GPU_SHADER_TYPE type); 79 | -------------------------------------------------------------------------------- /test/3ds/source/vshader.v.pica: -------------------------------------------------------------------------------- 1 | ; Example PICA200 vertex shader 2 | 3 | ; Uniforms 4 | .fvec projection[4], modelView[4], texView[2] 5 | .fvec lightVec, lightHalfVec, lightClr, material[4] 6 | .alias mat_amb material[0] 7 | .alias mat_dif material[1] 8 | .alias mat_spe material[2] 9 | .alias mat_emi material[3] 10 | 11 | ; Constants 12 | .constf myconst(0.0, 1.0, -1.0, -0.5) 13 | .alias zeros myconst.xxxx ; Vector full of zeros 14 | .alias ones myconst.yyyy ; Vector full of ones 15 | 16 | ; Outputs 17 | .out outpos position 18 | .out outtc0 texcoord0 19 | .out outclr color 20 | 21 | ; Inputs (defined as aliases for convenience) 22 | .alias inpos v0 23 | .alias intex v1 24 | .alias innrm v2 25 | 26 | .proc main 27 | ; Force the w component of inpos to be 1.0 28 | mov r0.xyz, inpos 29 | mov r0.w, ones 30 | 31 | ; r1 = modelView * inpos 32 | dp4 r1.x, modelView[0], r0 33 | dp4 r1.y, modelView[1], r0 34 | dp4 r1.z, modelView[2], r0 35 | dp4 r1.w, modelView[3], r0 36 | 37 | ; outpos = projection * r1 38 | dp4 outpos.x, projection[0], r1 39 | dp4 outpos.y, projection[1], r1 40 | dp4 outpos.z, projection[2], r1 41 | dp4 outpos.w, projection[3], r1 42 | 43 | ; outtex = intex 44 | dp4 outtc0.x, texView[0], intex 45 | dp4 outtc0.y, texView[1], intex 46 | mov outtc0.zw, myconst.xy 47 | 48 | ; Transform the normal vector with the modelView matrix 49 | ; r1 = normalize(modelView * innrm) 50 | mov r0.xyz, innrm 51 | mov r0.w, zeros 52 | dp4 r1.x, modelView[0], r0 53 | dp4 r1.y, modelView[1], r0 54 | dp4 r1.z, modelView[2], r0 55 | mov r1.w, zeros 56 | dp3 r2, r1, r1 ; r2 = x^2+y^2+z^2 for each component 57 | rsq r2, r2 ; r2 = 1/sqrt(r2) '' 58 | mul r1, r2, r1 ; r1 = r1*r2 59 | 60 | ; Calculate the diffuse level (r0.x) and the shininess level (r0.y) 61 | ; r0.x = max(0, -(lightVec * r1)) 62 | ; r0.y = max(0, (-lightHalfVec[i]) * r1) ^ 2 63 | dp3 r0.x, lightVec, r1 64 | add r0.x, zeros, -r0 65 | dp3 r0.y, -lightHalfVec, r1 66 | max r0, zeros, r0 67 | mul r0.y, r0, r0 68 | 69 | ; Accumulate the vertex color in r1, initializing it to the emission color 70 | mov r1, mat_emi 71 | 72 | ; r1 += specularColor * lightClr * shininessLevel 73 | mul r2, lightClr, r0.yyyy 74 | mad r1, r2, mat_spe, r1 75 | 76 | ; r1 += diffuseColor * lightClr * diffuseLevel 77 | mul r2, lightClr, r0.xxxx 78 | mad r1, r2, mat_dif, r1 79 | 80 | ; r1 += ambientColor * lightClr 81 | mov r2, lightClr 82 | mad r1, r2, mat_amb, r1 83 | 84 | ; outclr = clamp r1 to [0,1] 85 | min outclr, ones, r1 86 | 87 | ; We're finished 88 | end 89 | .end 90 | -------------------------------------------------------------------------------- /include/c3d/renderqueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "framebuffer.h" 3 | 4 | typedef struct C3D_RenderTarget_tag C3D_RenderTarget; 5 | 6 | struct C3D_RenderTarget_tag 7 | { 8 | C3D_RenderTarget *next, *prev; 9 | C3D_FrameBuf frameBuf; 10 | 11 | bool used; 12 | bool ownsColor, ownsDepth; 13 | 14 | bool linked; 15 | gfxScreen_t screen; 16 | gfx3dSide_t side; 17 | u32 transferFlags; 18 | }; 19 | 20 | // Flags for C3D_FrameBegin 21 | enum 22 | { 23 | C3D_FRAME_SYNCDRAW = BIT(0), // Perform C3D_FrameSync before checking the GPU status 24 | C3D_FRAME_NONBLOCK = BIT(1), // Return false instead of waiting if the GPU is busy 25 | }; 26 | 27 | float C3D_FrameRate(float fps); 28 | void C3D_FrameSync(void); 29 | u32 C3D_FrameCounter(int id); 30 | 31 | bool C3D_FrameBegin(u8 flags); 32 | bool C3D_FrameDrawOn(C3D_RenderTarget* target); 33 | void C3D_FrameSplit(u8 flags); 34 | void C3D_FrameEnd(u8 flags); 35 | 36 | void C3D_FrameEndHook(void (* hook)(void*), void* param); 37 | 38 | float C3D_GetDrawingTime(void); 39 | float C3D_GetProcessingTime(void); 40 | 41 | #if defined(__GNUC__) && !defined(__cplusplus) 42 | typedef union __attribute__((__transparent_union__)) 43 | { 44 | int __i; 45 | GPU_DEPTHBUF __e; 46 | } C3D_DEPTHTYPE; 47 | #else 48 | union C3D_DEPTHTYPE 49 | { 50 | private: 51 | int __i; 52 | GPU_DEPTHBUF __e; 53 | public: 54 | C3D_DEPTHTYPE(GPU_DEPTHBUF e) : __e(e) {} 55 | C3D_DEPTHTYPE(int i) : __i(-1) { (void)i; } 56 | }; 57 | #endif 58 | 59 | #define C3D_DEPTHTYPE_OK(_x) ((_x).__i >= 0) 60 | #define C3D_DEPTHTYPE_VAL(_x) ((_x).__e) 61 | 62 | C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt); 63 | C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt); 64 | void C3D_RenderTargetDelete(C3D_RenderTarget* target); 65 | void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); 66 | 67 | static inline void C3D_RenderTargetDetachOutput(C3D_RenderTarget* target) 68 | { 69 | C3D_RenderTargetSetOutput(NULL, target->screen, target->side, 0); 70 | } 71 | 72 | static inline void C3D_RenderTargetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth) 73 | { 74 | C3D_FrameBufClear(&target->frameBuf, clearBits, clearColor, clearDepth); 75 | } 76 | 77 | void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags); 78 | void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags); 79 | void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1); 80 | -------------------------------------------------------------------------------- /include/c3d/texenv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct 5 | { 6 | u16 srcRgb, srcAlpha; 7 | union 8 | { 9 | u32 opAll; 10 | struct { u32 opRgb:12, opAlpha:12; }; 11 | }; 12 | u16 funcRgb, funcAlpha; 13 | u32 color; 14 | u16 scaleRgb, scaleAlpha; 15 | } C3D_TexEnv; 16 | 17 | typedef enum 18 | { 19 | C3D_RGB = BIT(0), 20 | C3D_Alpha = BIT(1), 21 | C3D_Both = C3D_RGB | C3D_Alpha, 22 | } C3D_TexEnvMode; 23 | 24 | C3D_TexEnv* C3D_GetTexEnv(int id); 25 | void C3D_SetTexEnv(int id, C3D_TexEnv* env); 26 | void C3D_DirtyTexEnv(C3D_TexEnv* env); 27 | 28 | void C3D_TexEnvBufUpdate(int mode, int mask); 29 | void C3D_TexEnvBufColor(u32 color); 30 | 31 | static inline void C3D_TexEnvInit(C3D_TexEnv* env) 32 | { 33 | env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0); 34 | env->srcAlpha = env->srcRgb; 35 | env->opAll = 0; 36 | env->funcRgb = GPU_REPLACE; 37 | env->funcAlpha = env->funcRgb; 38 | env->color = 0xFFFFFFFF; 39 | env->scaleRgb = GPU_TEVSCALE_1; 40 | env->scaleAlpha = GPU_TEVSCALE_1; 41 | } 42 | 43 | #ifdef __cplusplus 44 | #define _C3D_DEFAULT(x) = x 45 | #else 46 | #define _C3D_DEFAULT(x) 47 | #endif 48 | 49 | static inline void C3D_TexEnvSrc(C3D_TexEnv* env, C3D_TexEnvMode mode, 50 | GPU_TEVSRC s1, 51 | GPU_TEVSRC s2 _C3D_DEFAULT(GPU_PRIMARY_COLOR), 52 | GPU_TEVSRC s3 _C3D_DEFAULT(GPU_PRIMARY_COLOR)) 53 | { 54 | int param = GPU_TEVSOURCES((int)s1, (int)s2, (int)s3); 55 | if ((int)mode & C3D_RGB) 56 | env->srcRgb = param; 57 | if ((int)mode & C3D_Alpha) 58 | env->srcAlpha = param; 59 | } 60 | 61 | static inline void C3D_TexEnvOpRgb(C3D_TexEnv* env, 62 | GPU_TEVOP_RGB o1, 63 | GPU_TEVOP_RGB o2 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR), 64 | GPU_TEVOP_RGB o3 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR)) 65 | { 66 | env->opRgb = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3); 67 | } 68 | 69 | static inline void C3D_TexEnvOpAlpha(C3D_TexEnv* env, 70 | GPU_TEVOP_A o1, 71 | GPU_TEVOP_A o2 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA), 72 | GPU_TEVOP_A o3 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA)) 73 | { 74 | env->opAlpha = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3); 75 | } 76 | 77 | static inline void C3D_TexEnvFunc(C3D_TexEnv* env, C3D_TexEnvMode mode, GPU_COMBINEFUNC param) 78 | { 79 | if ((int)mode & C3D_RGB) 80 | env->funcRgb = param; 81 | if ((int)mode & C3D_Alpha) 82 | env->funcAlpha = param; 83 | } 84 | 85 | static inline void C3D_TexEnvColor(C3D_TexEnv* env, u32 color) 86 | { 87 | env->color = color; 88 | } 89 | 90 | static inline void C3D_TexEnvScale(C3D_TexEnv* env, int mode, GPU_TEVSCALE param) 91 | { 92 | if (mode & C3D_RGB) 93 | env->scaleRgb = param; 94 | if (mode & C3D_Alpha) 95 | env->scaleAlpha = param; 96 | } 97 | 98 | #undef _C3D_DEFAULT 99 | -------------------------------------------------------------------------------- /include/c3d/proctex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct 5 | { 6 | u32 color[256]; 7 | u32 diff[256]; 8 | } C3D_ProcTexColorLut; 9 | 10 | typedef struct 11 | { 12 | union 13 | { 14 | u32 proctex0; 15 | struct 16 | { 17 | u32 uClamp : 3; 18 | u32 vClamp : 3; 19 | u32 rgbFunc : 4; 20 | u32 alphaFunc : 4; 21 | bool alphaSeparate : 1; 22 | bool enableNoise : 1; 23 | u32 uShift : 2; 24 | u32 vShift : 2; 25 | u32 lodBiasLow : 8; 26 | }; 27 | }; 28 | union 29 | { 30 | u32 proctex1; 31 | struct 32 | { 33 | u16 uNoiseAmpl; 34 | u16 uNoisePhase; 35 | }; 36 | }; 37 | union 38 | { 39 | u32 proctex2; 40 | struct 41 | { 42 | u16 vNoiseAmpl; 43 | u16 vNoisePhase; 44 | }; 45 | }; 46 | union 47 | { 48 | u32 proctex3; 49 | struct 50 | { 51 | u16 uNoiseFreq; 52 | u16 vNoiseFreq; 53 | }; 54 | }; 55 | union 56 | { 57 | u32 proctex4; 58 | struct 59 | { 60 | u32 minFilter : 3; 61 | u32 unknown1 : 8; 62 | u32 width : 8; 63 | u32 lodBiasHigh : 8; 64 | }; 65 | }; 66 | union 67 | { 68 | u32 proctex5; 69 | struct 70 | { 71 | u32 offset : 8; 72 | u32 unknown2 : 24; 73 | }; 74 | }; 75 | } C3D_ProcTex; 76 | 77 | enum 78 | { 79 | C3D_ProcTex_U = BIT(0), 80 | C3D_ProcTex_V = BIT(1), 81 | C3D_ProcTex_UV = C3D_ProcTex_U | C3D_ProcTex_V, 82 | }; 83 | 84 | void C3D_ProcTexInit(C3D_ProcTex* pt, int offset, int length); 85 | void C3D_ProcTexNoiseCoefs(C3D_ProcTex* pt, int mode, float amplitude, float frequency, float phase); 86 | void C3D_ProcTexLodBias(C3D_ProcTex* pt, float bias); 87 | void C3D_ProcTexBind(int texCoordId, C3D_ProcTex* pt); 88 | 89 | // GPU_LUT_NOISE, GPU_LUT_RGBMAP, GPU_LUT_ALPHAMAP 90 | typedef u32 C3D_ProcTexLut[128]; 91 | void C3D_ProcTexLutBind(GPU_PROCTEX_LUTID id, C3D_ProcTexLut* lut); 92 | void ProcTexLut_FromArray(C3D_ProcTexLut* lut, const float in[129]); 93 | 94 | void C3D_ProcTexColorLutBind(C3D_ProcTexColorLut* lut); 95 | void ProcTexColorLut_Write(C3D_ProcTexColorLut* out, const u32* in, int offset, int length); 96 | 97 | static inline void C3D_ProcTexClamp(C3D_ProcTex* pt, GPU_PROCTEX_CLAMP u, GPU_PROCTEX_CLAMP v) 98 | { 99 | pt->uClamp = u; 100 | pt->vClamp = v; 101 | } 102 | 103 | static inline void C3D_ProcTexCombiner(C3D_ProcTex* pt, bool separate, GPU_PROCTEX_MAPFUNC rgb, GPU_PROCTEX_MAPFUNC alpha) 104 | { 105 | pt->alphaSeparate = separate; 106 | pt->rgbFunc = rgb; 107 | if (separate) 108 | pt->alphaFunc = alpha; 109 | } 110 | 111 | static inline void C3D_ProcTexNoiseEnable(C3D_ProcTex* pt, bool enable) 112 | { 113 | pt->enableNoise = enable; 114 | } 115 | 116 | static inline void C3D_ProcTexShift(C3D_ProcTex* pt, GPU_PROCTEX_SHIFT u, GPU_PROCTEX_SHIFT v) 117 | { 118 | pt->uShift = u; 119 | pt->vShift = v; 120 | } 121 | 122 | static inline void C3D_ProcTexFilter(C3D_ProcTex* pt, GPU_PROCTEX_FILTER min) 123 | { 124 | pt->minFilter = min; 125 | } 126 | -------------------------------------------------------------------------------- /source/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | static const u8 colorFmtSizes[] = {2,1,0,0,0}; 4 | static const u8 depthFmtSizes[] = {0,0,1,2}; 5 | 6 | u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt) 7 | { 8 | u32 size = width*height; 9 | return size*(2+colorFmtSizes[fmt]); 10 | } 11 | 12 | u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt) 13 | { 14 | u32 size = width*height; 15 | return size*(2+depthFmtSizes[fmt]); 16 | } 17 | 18 | C3D_FrameBuf* C3D_GetFrameBuf(void) 19 | { 20 | C3D_Context* ctx = C3Di_GetContext(); 21 | 22 | if (!(ctx->flags & C3DiF_Active)) 23 | return NULL; 24 | 25 | ctx->flags |= C3DiF_FrameBuf; 26 | return &ctx->fb; 27 | } 28 | 29 | void C3D_SetFrameBuf(C3D_FrameBuf* fb) 30 | { 31 | C3D_Context* ctx = C3Di_GetContext(); 32 | 33 | if (!(ctx->flags & C3DiF_Active)) 34 | return; 35 | 36 | if (fb != &ctx->fb) 37 | memcpy(&ctx->fb, fb, sizeof(*fb)); 38 | ctx->flags |= C3DiF_FrameBuf; 39 | } 40 | 41 | void C3D_FrameBufTex(C3D_FrameBuf* fb, C3D_Tex* tex, GPU_TEXFACE face, int level) 42 | { 43 | C3D_FrameBufAttrib(fb, tex->width, tex->height, false); 44 | C3D_FrameBufColor(fb, C3D_TexGetImagePtr(tex, 45 | C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face], level, NULL), 46 | (GPU_COLORBUF)tex->fmt); 47 | } 48 | 49 | void C3Di_FrameBufBind(C3D_FrameBuf* fb) 50 | { 51 | u32 param[4] = { 0, 0, 0, 0 }; 52 | 53 | GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); 54 | 55 | param[0] = osConvertVirtToPhys(fb->depthBuf) >> 3; 56 | param[1] = osConvertVirtToPhys(fb->colorBuf) >> 3; 57 | param[2] = 0x01000000 | (((u32)(fb->height-1) & 0xFFF) << 12) | (fb->width & 0xFFF); 58 | GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, param, 3); 59 | 60 | GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, param[2]); 61 | GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, fb->depthFmt); 62 | GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, colorFmtSizes[fb->colorFmt] | ((u32)fb->colorFmt << 16)); 63 | GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, fb->block32 ? 1 : 0); 64 | 65 | // Enable or disable color/depth buffers 66 | param[0] = param[1] = fb->colorBuf ? fb->colorMask : 0; 67 | param[2] = param[3] = fb->depthBuf ? fb->depthMask : 0; 68 | GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, param, 4); 69 | } 70 | 71 | void C3D_FrameBufClear(C3D_FrameBuf* frameBuf, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth) 72 | { 73 | u32 size = (u32)frameBuf->width * frameBuf->height; 74 | u32 cfs = colorFmtSizes[frameBuf->colorFmt]; 75 | u32 dfs = depthFmtSizes[frameBuf->depthFmt]; 76 | void* colorBufEnd = (u8*)frameBuf->colorBuf + size*(2+cfs); 77 | void* depthBufEnd = (u8*)frameBuf->depthBuf + size*(2+dfs); 78 | 79 | if (clearBits & C3D_CLEAR_COLOR) 80 | { 81 | if (clearBits & C3D_CLEAR_DEPTH) 82 | GX_MemoryFill( 83 | (u32*)frameBuf->colorBuf, clearColor, (u32*)colorBufEnd, BIT(0) | (cfs << 8), 84 | (u32*)frameBuf->depthBuf, clearDepth, (u32*)depthBufEnd, BIT(0) | (dfs << 8)); 85 | else 86 | GX_MemoryFill( 87 | (u32*)frameBuf->colorBuf, clearColor, (u32*)colorBufEnd, BIT(0) | (cfs << 8), 88 | NULL, 0, NULL, 0); 89 | } else 90 | GX_MemoryFill( 91 | (u32*)frameBuf->depthBuf, clearDepth, (u32*)depthBufEnd, BIT(0) | (dfs << 8), 92 | NULL, 0, NULL, 0); 93 | } 94 | 95 | void C3D_FrameBufTransfer(C3D_FrameBuf* frameBuf, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags) 96 | { 97 | u32* outputFrameBuf = (u32*)gfxGetFramebuffer(screen, side, NULL, NULL); 98 | u32 dim = GX_BUFFER_DIM((u32)frameBuf->width, (u32)frameBuf->height); 99 | GX_DisplayTransfer((u32*)frameBuf->colorBuf, dim, outputFrameBuf, dim, transferFlags); 100 | } 101 | -------------------------------------------------------------------------------- /source/effect.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | static inline C3D_Effect* getEffect() 4 | { 5 | C3D_Context* ctx = C3Di_GetContext(); 6 | ctx->flags |= C3DiF_Effect; 7 | return &ctx->effect; 8 | } 9 | 10 | void C3D_DepthMap(bool bIsZBuffer, float zScale, float zOffset) 11 | { 12 | C3D_Effect* e = getEffect(); 13 | e->zBuffer = bIsZBuffer; 14 | e->zScale = f32tof24(zScale); 15 | e->zOffset = f32tof24(zOffset); 16 | } 17 | 18 | void C3D_CullFace(GPU_CULLMODE mode) 19 | { 20 | C3D_Effect* e = getEffect(); 21 | e->cullMode = mode; 22 | } 23 | 24 | void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int inputMask, int writeMask) 25 | { 26 | C3D_Effect* e = getEffect(); 27 | e->stencilMode = (!!enable) | ((function & 7) << 4) | (writeMask << 8) | (ref << 16) | (inputMask << 24); 28 | } 29 | 30 | void C3D_StencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass) 31 | { 32 | C3D_Effect* e = getEffect(); 33 | e->stencilOp = sfail | (dfail << 4) | (pass << 8); 34 | } 35 | 36 | void C3D_BlendingColor(u32 color) 37 | { 38 | C3D_Effect* e = getEffect(); 39 | e->blendClr = color; 40 | } 41 | 42 | void C3D_EarlyDepthTest(bool enable, GPU_EARLYDEPTHFUNC function, u32 ref) 43 | { 44 | C3D_Effect* e = getEffect(); 45 | e->earlyDepth = enable; 46 | e->earlyDepthFunc = function; 47 | e->earlyDepthRef = ref; 48 | } 49 | 50 | void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask) 51 | { 52 | C3D_Effect* e = getEffect(); 53 | e->depthTest = (!!enable) | ((function & 7) << 4) | (writemask << 8); 54 | } 55 | 56 | void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref) 57 | { 58 | C3D_Effect* e = getEffect(); 59 | e->alphaTest = (!!enable) | ((function & 7) << 4) | (ref << 8); 60 | } 61 | 62 | void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha) 63 | { 64 | C3D_Effect* e = getEffect(); 65 | e->alphaBlend = colorEq | (alphaEq << 8) | (srcClr << 16) | (dstClr << 20) | (srcAlpha << 24) | (dstAlpha << 28); 66 | e->fragOpMode &= ~0xFF00; 67 | e->fragOpMode |= 0x0100; 68 | } 69 | 70 | void C3D_ColorLogicOp(GPU_LOGICOP op) 71 | { 72 | C3D_Effect* e = getEffect(); 73 | e->fragOpMode &= ~0xFF00; 74 | e->clrLogicOp = op; 75 | } 76 | 77 | void C3D_FragOpMode(GPU_FRAGOPMODE mode) 78 | { 79 | C3D_Effect* e = getEffect(); 80 | e->fragOpMode &= ~0xFF00FF; 81 | e->fragOpMode |= 0xE40000 | mode; 82 | } 83 | 84 | void C3D_FragOpShadow(float scale, float bias) 85 | { 86 | C3D_Effect* e = getEffect(); 87 | e->fragOpShadow = f32tof16(scale+bias) | (f32tof16(-scale)<<16); 88 | } 89 | 90 | void C3Di_EffectBind(C3D_Effect* e) 91 | { 92 | GPUCMD_AddWrite(GPUREG_DEPTHMAP_ENABLE, e->zBuffer ? 1 : 0); 93 | GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, e->cullMode & 0x3); 94 | GPUCMD_AddIncrementalWrites(GPUREG_DEPTHMAP_SCALE, (u32*)&e->zScale, 2); 95 | GPUCMD_AddIncrementalWrites(GPUREG_FRAGOP_ALPHA_TEST, (u32*)&e->alphaTest, 4); 96 | GPUCMD_AddMaskedWrite(GPUREG_GAS_DELTAZ_DEPTH, 0x8, (u32)GPU_MAKEGASDEPTHFUNC((e->depthTest>>4)&7) << 24); 97 | GPUCMD_AddWrite(GPUREG_BLEND_COLOR, e->blendClr); 98 | GPUCMD_AddWrite(GPUREG_BLEND_FUNC, e->alphaBlend); 99 | GPUCMD_AddWrite(GPUREG_LOGIC_OP, e->clrLogicOp); 100 | GPUCMD_AddMaskedWrite(GPUREG_COLOR_OPERATION, 7, e->fragOpMode); 101 | GPUCMD_AddWrite(GPUREG_FRAGOP_SHADOW, e->fragOpShadow); 102 | GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 1, e->earlyDepth ? 1 : 0); 103 | GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, e->earlyDepth ? 1 : 0); 104 | GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_FUNC, 1, e->earlyDepthFunc); 105 | GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_DATA, 0x7, e->earlyDepthRef); 106 | } 107 | -------------------------------------------------------------------------------- /source/uniforms.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | #include 3 | 4 | C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; 5 | C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; 6 | u16 C3D_BoolUnifs[2]; 7 | 8 | bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; 9 | bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; 10 | bool C3D_BoolUnifsDirty[2]; 11 | 12 | static struct 13 | { 14 | bool dirty; 15 | int count; 16 | float24Uniform_s* data; 17 | } C3Di_ShaderFVecData[2]; 18 | 19 | static bool C3Di_FVUnifEverDirty[2][C3D_FVUNIF_COUNT]; 20 | static bool C3Di_IVUnifEverDirty[2][C3D_IVUNIF_COUNT]; 21 | 22 | void C3D_UpdateUniforms(GPU_SHADER_TYPE type) 23 | { 24 | int offset = type == GPU_GEOMETRY_SHADER ? (GPUREG_GSH_BOOLUNIFORM-GPUREG_VSH_BOOLUNIFORM) : 0; 25 | int i = 0; 26 | 27 | // Update FVec uniforms that come from shader constants 28 | if (C3Di_ShaderFVecData[type].dirty) 29 | { 30 | while (i < C3Di_ShaderFVecData[type].count) 31 | { 32 | float24Uniform_s* u = &C3Di_ShaderFVecData[type].data[i++]; 33 | GPUCMD_AddIncrementalWrites(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, (u32*)u, 4); 34 | C3D_FVUnifDirty[type][u->id] = false; 35 | } 36 | C3Di_ShaderFVecData[type].dirty = false; 37 | i = 0; 38 | } 39 | 40 | // Update FVec uniforms 41 | while (i < C3D_FVUNIF_COUNT) 42 | { 43 | if (!C3D_FVUnifDirty[type][i]) 44 | { 45 | i ++; 46 | continue; 47 | } 48 | 49 | // Find the number of consecutive dirty uniforms 50 | int j; 51 | for (j = i; j < C3D_FVUNIF_COUNT && C3D_FVUnifDirty[type][j]; j ++); 52 | 53 | // Upload the uniforms 54 | GPUCMD_AddWrite(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, 0x80000000|i); 55 | GPUCMD_AddWrites(GPUREG_VSH_FLOATUNIFORM_DATA+offset, (u32*)&C3D_FVUnif[type][i], (j-i)*4); 56 | 57 | // Clear the dirty flag 58 | int k; 59 | for (k = i; k < j; k ++) 60 | { 61 | C3D_FVUnifDirty[type][k] = false; 62 | C3Di_FVUnifEverDirty[type][k] = true; 63 | } 64 | 65 | // Advance 66 | i = j; 67 | } 68 | 69 | // Update IVec uniforms 70 | for (i = 0; i < C3D_IVUNIF_COUNT; i ++) 71 | { 72 | if (!C3D_IVUnifDirty[type][i]) continue; 73 | 74 | GPUCMD_AddWrite(GPUREG_VSH_INTUNIFORM_I0+offset+i, C3D_IVUnif[type][i]); 75 | C3D_IVUnifDirty[type][i] = false; 76 | C3Di_IVUnifEverDirty[type][i] = false; 77 | } 78 | 79 | // Update bool uniforms 80 | if (C3D_BoolUnifsDirty[type]) 81 | { 82 | GPUCMD_AddWrite(GPUREG_VSH_BOOLUNIFORM+offset, 0x7FFF0000 | C3D_BoolUnifs[type]); 83 | C3D_BoolUnifsDirty[type] = false; 84 | } 85 | } 86 | 87 | void C3Di_DirtyUniforms(GPU_SHADER_TYPE type) 88 | { 89 | int i; 90 | C3D_BoolUnifsDirty[type] = true; 91 | if (C3Di_ShaderFVecData[type].count) 92 | C3Di_ShaderFVecData[type].dirty = true; 93 | for (i = 0; i < C3D_FVUNIF_COUNT; i ++) 94 | C3D_FVUnifDirty[type][i] = C3D_FVUnifDirty[type][i] || C3Di_FVUnifEverDirty[type][i]; 95 | for (i = 0; i < C3D_IVUNIF_COUNT; i ++) 96 | C3D_IVUnifDirty[type][i] = C3D_IVUnifDirty[type][i] || C3Di_IVUnifEverDirty[type][i]; 97 | } 98 | 99 | void C3Di_LoadShaderUniforms(shaderInstance_s* si) 100 | { 101 | GPU_SHADER_TYPE type = si->dvle->type; 102 | if (si->boolUniformMask) 103 | { 104 | C3D_BoolUnifs[type] &= ~si->boolUniformMask; 105 | C3D_BoolUnifs[type] |= si->boolUniforms; 106 | } 107 | 108 | if (type == GPU_GEOMETRY_SHADER) 109 | C3D_BoolUnifs[type] &= ~BIT(15); 110 | C3D_BoolUnifsDirty[type] = true; 111 | 112 | if (si->intUniformMask) 113 | { 114 | int i; 115 | for (i = 0; i < 4; i ++) 116 | { 117 | if (si->intUniformMask & BIT(i)) 118 | { 119 | C3D_IVUnif[type][i] = si->intUniforms[i]; 120 | C3D_IVUnifDirty[type][i] = true; 121 | } 122 | } 123 | } 124 | C3Di_ShaderFVecData[type].dirty = true; 125 | C3Di_ShaderFVecData[type].count = si->numFloat24Uniforms; 126 | C3Di_ShaderFVecData[type].data = si->float24Uniforms; 127 | } 128 | 129 | void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type) 130 | { 131 | C3Di_ShaderFVecData[type].dirty = false; 132 | C3Di_ShaderFVecData[type].count = 0; 133 | C3Di_ShaderFVecData[type].data = NULL; 134 | } 135 | -------------------------------------------------------------------------------- /source/internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define C3D_UNUSED __attribute__((unused)) 11 | 12 | typedef struct 13 | { 14 | u32 fragOpMode; 15 | u32 fragOpShadow; 16 | u32 zScale, zOffset; 17 | GPU_CULLMODE cullMode; 18 | bool zBuffer, earlyDepth; 19 | GPU_EARLYDEPTHFUNC earlyDepthFunc; 20 | u32 earlyDepthRef; 21 | 22 | u32 alphaTest; 23 | u32 stencilMode, stencilOp; 24 | u32 depthTest; 25 | 26 | u32 blendClr; 27 | u32 alphaBlend; 28 | GPU_LOGICOP clrLogicOp; 29 | } C3D_Effect; 30 | 31 | typedef struct 32 | { 33 | gxCmdQueue_s gxQueue; 34 | u32* cmdBuf; 35 | size_t cmdBufSize; 36 | float cmdBufUsage; 37 | 38 | u32 flags; 39 | shaderProgram_s* program; 40 | 41 | C3D_AttrInfo attrInfo; 42 | C3D_BufInfo bufInfo; 43 | C3D_Effect effect; 44 | C3D_LightEnv* lightEnv; 45 | 46 | u32 texConfig; 47 | u32 texShadow; 48 | C3D_Tex* tex[3]; 49 | C3D_TexEnv texEnv[6]; 50 | 51 | u32 texEnvBuf, texEnvBufClr; 52 | u32 fogClr; 53 | C3D_FogLut* fogLut; 54 | 55 | u16 gasAttn, gasAccMax; 56 | u32 gasLightXY, gasLightZ, gasLightZColor; 57 | u32 gasDeltaZ : 24; 58 | u32 gasFlags : 8; 59 | C3D_GasLut* gasLut; 60 | 61 | C3D_ProcTex* procTex; 62 | C3D_ProcTexLut* procTexLut[3]; 63 | C3D_ProcTexColorLut* procTexColorLut; 64 | 65 | C3D_FrameBuf fb; 66 | u32 viewport[5]; 67 | u32 scissor[3]; 68 | 69 | u16 fixedAttribDirty, fixedAttribEverDirty; 70 | C3D_FVec fixedAttribs[12]; 71 | } C3D_Context; 72 | 73 | enum 74 | { 75 | C3DiF_Active = BIT(0), 76 | C3DiF_DrawUsed = BIT(1), 77 | C3DiF_AttrInfo = BIT(2), 78 | C3DiF_BufInfo = BIT(3), 79 | C3DiF_Effect = BIT(4), 80 | C3DiF_FrameBuf = BIT(5), 81 | C3DiF_Viewport = BIT(6), 82 | C3DiF_Scissor = BIT(7), 83 | C3DiF_Program = BIT(8), 84 | C3DiF_TexEnvBuf = BIT(9), 85 | C3DiF_LightEnv = BIT(10), 86 | C3DiF_VshCode = BIT(11), 87 | C3DiF_GshCode = BIT(12), 88 | C3DiF_TexStatus = BIT(14), 89 | C3DiF_ProcTex = BIT(15), 90 | C3DiF_ProcTexColorLut = BIT(16), 91 | C3DiF_FogLut = BIT(17), 92 | C3DiF_Gas = BIT(18), 93 | C3DiF_GasLut = BIT(19), 94 | 95 | #define C3DiF_ProcTexLut(n) BIT(20+(n)) 96 | C3DiF_ProcTexLutAll = 7 << 20, 97 | #define C3DiF_Tex(n) BIT(23+(n)) 98 | C3DiF_TexAll = 7 << 23, 99 | #define C3DiF_TexEnv(n) BIT(26+(n)) 100 | C3DiF_TexEnvAll = 0x3F << 26, 101 | }; 102 | 103 | enum 104 | { 105 | C3DiG_BeginAcc = BIT(0), 106 | C3DiG_AccStage = BIT(1), 107 | C3DiG_SetAccMax = BIT(2), 108 | C3DiG_RenderStage = BIT(3), 109 | }; 110 | 111 | static inline C3D_Context* C3Di_GetContext(void) 112 | { 113 | extern C3D_Context __C3D_Context; 114 | return &__C3D_Context; 115 | } 116 | 117 | static inline bool typeIsCube(GPU_TEXTURE_MODE_PARAM type) 118 | { 119 | return type == GPU_TEX_CUBE_MAP || type == GPU_TEX_SHADOW_CUBE; 120 | } 121 | 122 | static inline bool C3Di_TexIs2D(C3D_Tex* tex) 123 | { 124 | return !typeIsCube(C3D_TexGetType(tex)); 125 | } 126 | 127 | static inline bool addrIsVRAM(const void* addr) 128 | { 129 | u32 vaddr = (u32)addr; 130 | return vaddr >= OS_VRAM_VADDR && vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE; 131 | } 132 | 133 | static inline vramAllocPos addrGetVRAMBank(const void* addr) 134 | { 135 | u32 vaddr = (u32)addr; 136 | return vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE/2 ? VRAM_ALLOC_A : VRAM_ALLOC_B; 137 | } 138 | 139 | void C3Di_UpdateContext(void); 140 | void C3Di_AttrInfoBind(C3D_AttrInfo* info); 141 | void C3Di_BufInfoBind(C3D_BufInfo* info); 142 | void C3Di_FrameBufBind(C3D_FrameBuf* fb); 143 | void C3Di_TexEnvBind(int id, C3D_TexEnv* env); 144 | void C3Di_SetTex(int unit, C3D_Tex* tex); 145 | void C3Di_EffectBind(C3D_Effect* effect); 146 | void C3Di_GasUpdate(C3D_Context* ctx); 147 | 148 | void C3Di_LightMtlBlend(C3D_Light* light); 149 | 150 | void C3Di_DirtyUniforms(GPU_SHADER_TYPE type); 151 | void C3Di_LoadShaderUniforms(shaderInstance_s* si); 152 | void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type); 153 | 154 | bool C3Di_SplitFrame(u32** pBuf, u32* pSize); 155 | 156 | void C3Di_RenderQueueInit(void); 157 | void C3Di_RenderQueueExit(void); 158 | void C3Di_RenderQueueWaitDone(void); 159 | void C3Di_RenderQueueEnableVBlank(void); 160 | void C3Di_RenderQueueDisableVBlank(void); 161 | -------------------------------------------------------------------------------- /source/gas.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | static inline u32 calc_diff(u32 a, u32 b, int pos) 4 | { 5 | float fa = ((a>>pos)&0xFF)/255.0f; 6 | float fb = ((b>>pos)&0xFF)/255.0f; 7 | float x = fb-fa; 8 | u32 diff = 0; 9 | if (x < 0) 10 | { 11 | diff = 0x80; 12 | x = -x; 13 | } 14 | diff |= (u32)(x*0x7F); 15 | return diff< 1.0f) x = 1.0f; 22 | return ((u32)x*255)<color[i] = data[i]; 37 | if (i > 0) 38 | lut->diff[i-1] = color_diff(data[i-1], data[i]); 39 | } 40 | } 41 | 42 | void C3D_GasBeginAcc(void) 43 | { 44 | C3D_Context* ctx = C3Di_GetContext(); 45 | 46 | if (!(ctx->flags & C3DiF_Active)) 47 | return; 48 | 49 | ctx->gasFlags |= C3DiG_BeginAcc; 50 | } 51 | 52 | void C3D_GasDeltaZ(float value) 53 | { 54 | C3D_Context* ctx = C3Di_GetContext(); 55 | 56 | if (!(ctx->flags & C3DiF_Active)) 57 | return; 58 | 59 | ctx->flags |= C3DiF_Gas; 60 | ctx->gasDeltaZ = (u32)(value*0x100); 61 | ctx->gasFlags |= C3DiG_AccStage; 62 | } 63 | 64 | void C3D_GasAccMax(float value) 65 | { 66 | C3D_Context* ctx = C3Di_GetContext(); 67 | 68 | if (!(ctx->flags & C3DiF_Active)) 69 | return; 70 | 71 | ctx->flags |= C3DiF_Gas; 72 | ctx->gasAccMax = f32tof16(1.0f / value); 73 | ctx->gasFlags |= C3DiG_SetAccMax; 74 | } 75 | 76 | void C3D_GasAttn(float value) 77 | { 78 | C3D_Context* ctx = C3Di_GetContext(); 79 | 80 | if (!(ctx->flags & C3DiF_Active)) 81 | return; 82 | 83 | ctx->flags |= C3DiF_Gas; 84 | ctx->gasAttn = f32tof16(value); 85 | ctx->gasFlags |= C3DiG_RenderStage; 86 | } 87 | 88 | void C3D_GasLightPlanar(float min, float max, float attn) 89 | { 90 | C3D_Context* ctx = C3Di_GetContext(); 91 | 92 | if (!(ctx->flags & C3DiF_Active)) 93 | return; 94 | 95 | ctx->flags |= C3DiF_Gas; 96 | ctx->gasLightXY = conv_u8(min,0) | conv_u8(max,8) | conv_u8(attn,16); 97 | ctx->gasFlags |= C3DiG_RenderStage; 98 | } 99 | 100 | void C3D_GasLightView(float min, float max, float attn) 101 | { 102 | C3D_Context* ctx = C3Di_GetContext(); 103 | 104 | if (!(ctx->flags & C3DiF_Active)) 105 | return; 106 | 107 | ctx->flags |= C3DiF_Gas; 108 | ctx->gasLightZ = conv_u8(min,0) | conv_u8(max,8) | conv_u8(attn,16); 109 | ctx->gasFlags |= C3DiG_RenderStage; 110 | } 111 | 112 | void C3D_GasLightDirection(float dotp) 113 | { 114 | C3D_Context* ctx = C3Di_GetContext(); 115 | 116 | if (!(ctx->flags & C3DiF_Active)) 117 | return; 118 | 119 | ctx->flags |= C3DiF_Gas; 120 | ctx->gasLightZColor &= ~0xFF; 121 | ctx->gasLightZColor |= conv_u8(dotp,0); 122 | ctx->gasFlags |= C3DiG_RenderStage; 123 | } 124 | 125 | void C3D_GasLutInput(GPU_GASLUTINPUT input) 126 | { 127 | C3D_Context* ctx = C3Di_GetContext(); 128 | 129 | if (!(ctx->flags & C3DiF_Active)) 130 | return; 131 | 132 | ctx->flags |= C3DiF_Gas; 133 | ctx->gasLightZColor &= ~0x100; 134 | ctx->gasLightZColor |= (input&1)<<8; 135 | ctx->gasFlags |= C3DiG_RenderStage; 136 | } 137 | 138 | void C3D_GasLutBind(C3D_GasLut* lut) 139 | { 140 | C3D_Context* ctx = C3Di_GetContext(); 141 | 142 | if (!(ctx->flags & C3DiF_Active)) 143 | return; 144 | 145 | if (lut) 146 | { 147 | ctx->flags |= C3DiF_GasLut; 148 | ctx->gasLut = lut; 149 | } else 150 | ctx->flags &= ~C3DiF_GasLut; 151 | } 152 | 153 | void C3Di_GasUpdate(C3D_Context* ctx) 154 | { 155 | if (ctx->flags & C3DiF_Gas) 156 | { 157 | ctx->flags &= ~C3DiF_Gas; 158 | u32 gasFlags = ctx->gasFlags; 159 | ctx->gasFlags = 0; 160 | 161 | if (gasFlags & C3DiG_BeginAcc) 162 | GPUCMD_AddMaskedWrite(GPUREG_GAS_ACCMAX_FEEDBACK, 0x3, 0); 163 | if (gasFlags & C3DiG_AccStage) 164 | GPUCMD_AddMaskedWrite(GPUREG_GAS_DELTAZ_DEPTH, 0x7, ctx->gasDeltaZ); 165 | if (gasFlags & C3DiG_SetAccMax) 166 | GPUCMD_AddWrite(GPUREG_GAS_ACCMAX, ctx->gasAccMax); 167 | if (gasFlags & C3DiG_RenderStage) 168 | { 169 | GPUCMD_AddWrite(GPUREG_GAS_ATTENUATION, ctx->gasAttn); 170 | GPUCMD_AddWrite(GPUREG_GAS_LIGHT_XY, ctx->gasLightXY); 171 | GPUCMD_AddWrite(GPUREG_GAS_LIGHT_Z, ctx->gasLightZ); 172 | GPUCMD_AddWrite(GPUREG_GAS_LIGHT_Z_COLOR, ctx->gasLightZColor); 173 | } 174 | } 175 | if (ctx->flags & C3DiF_GasLut) 176 | { 177 | ctx->flags &= ~C3DiF_GasLut; 178 | if (ctx->gasLut) 179 | { 180 | GPUCMD_AddWrite(GPUREG_GAS_LUT_INDEX, 0); 181 | GPUCMD_AddWrites(GPUREG_GAS_LUT_DATA, (u32*)ctx->gasLut, 16); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /source/proctex.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void C3D_ProcTexInit(C3D_ProcTex* pt, int offset, int width) 4 | { 5 | memset(pt, 0, sizeof(*pt)); 6 | pt->offset = offset; 7 | pt->width = width; 8 | pt->unknown1 = 0x60; 9 | pt->unknown2 = 0xE0C080; 10 | } 11 | 12 | void C3D_ProcTexNoiseCoefs(C3D_ProcTex* pt, int mode, float amplitude, float frequency, float phase) 13 | { 14 | u16 f16_ampl = (s32)(amplitude*0x1000); 15 | u16 f16_freq = f32tof16(frequency); 16 | u16 f16_phase = f32tof16(phase); 17 | pt->enableNoise = true; 18 | if (mode & C3D_ProcTex_U) 19 | { 20 | pt->uNoiseAmpl = f16_ampl; 21 | pt->uNoiseFreq = f16_freq; 22 | pt->uNoisePhase = f16_phase; 23 | } 24 | if (mode & C3D_ProcTex_V) 25 | { 26 | pt->vNoiseAmpl = f16_ampl; 27 | pt->vNoiseFreq = f16_freq; 28 | pt->vNoisePhase = f16_phase; 29 | } 30 | } 31 | 32 | void C3D_ProcTexLodBias(C3D_ProcTex* pt, float bias) 33 | { 34 | u32 f16_bias = f32tof16(bias); 35 | pt->lodBiasLow = f16_bias; 36 | pt->lodBiasHigh = f16_bias>>8; 37 | } 38 | 39 | void C3D_ProcTexBind(int texCoordId, C3D_ProcTex* pt) 40 | { 41 | C3D_Context* ctx = C3Di_GetContext(); 42 | 43 | if (!(ctx->flags & C3DiF_Active)) 44 | return; 45 | 46 | ctx->flags |= C3DiF_TexStatus; 47 | ctx->texConfig &= ~(7<<8); 48 | ctx->procTex = pt; 49 | if (pt) 50 | { 51 | ctx->flags |= C3DiF_ProcTex; 52 | ctx->texConfig |= BIT(10) | ((texCoordId&3)<<8); 53 | } else 54 | ctx->flags &= ~C3DiF_ProcTex; 55 | } 56 | 57 | static inline int lutid2idx(GPU_PROCTEX_LUTID id) 58 | { 59 | switch (id) 60 | { 61 | case GPU_LUT_NOISE: return 0; 62 | case GPU_LUT_RGBMAP: return 1; 63 | case GPU_LUT_ALPHAMAP: return 2; 64 | default: return -1; 65 | } 66 | } 67 | 68 | void C3D_ProcTexLutBind(GPU_PROCTEX_LUTID id, C3D_ProcTexLut* lut) 69 | { 70 | C3D_Context* ctx = C3Di_GetContext(); 71 | 72 | if (!(ctx->flags & C3DiF_Active)) 73 | return; 74 | 75 | int idx = lutid2idx(id); 76 | if (idx < 0) 77 | return; 78 | 79 | ctx->procTexLut[idx] = lut; 80 | if (lut) 81 | ctx->flags |= C3DiF_ProcTexLut(idx); 82 | else 83 | ctx->flags &= ~C3DiF_ProcTexLut(idx); 84 | } 85 | 86 | static inline float clampLut(float val) 87 | { 88 | if (val < 0.0f) return 0.0f; 89 | if (val > 1.0f) return 1.0f; 90 | return val; 91 | } 92 | 93 | void ProcTexLut_FromArray(C3D_ProcTexLut* lut, const float in[129]) 94 | { 95 | int i; 96 | for (i = 0; i < 128; i ++) 97 | { 98 | u32 cur = 0xFFF*clampLut(in[i]); 99 | u32 next = 0xFFF*clampLut(in[i+1]); 100 | u32 diff = (next-cur)&0xFFF; 101 | (*lut)[i] = cur | (diff<<12); 102 | } 103 | } 104 | 105 | void C3D_ProcTexColorLutBind(C3D_ProcTexColorLut* lut) 106 | { 107 | C3D_Context* ctx = C3Di_GetContext(); 108 | 109 | if (!(ctx->flags & C3DiF_Active)) 110 | return; 111 | 112 | ctx->procTexColorLut = lut; 113 | if (lut) 114 | ctx->flags |= C3DiF_ProcTexColorLut; 115 | else 116 | ctx->flags &= ~C3DiF_ProcTexColorLut; 117 | } 118 | 119 | static inline u32 calc_diff(u32 cur, u32 next, int pos) 120 | { 121 | cur = (cur>>pos)&0xFF; 122 | next = (next>>pos)&0xFF; 123 | u32 diff = (((s32)next-(s32)cur)>>1)&0xFF; 124 | return diff<color[offset], in, 4*width); 131 | for (i = 0; i < (width-1); i ++) 132 | { 133 | u32 cur = in[i]; 134 | u32 next = in[i+1]; 135 | out->diff[offset+i] = 136 | calc_diff(cur,next,0) | 137 | calc_diff(cur,next,8) | 138 | calc_diff(cur,next,16) | 139 | calc_diff(cur,next,24); 140 | } 141 | out->diff[offset+width-1] = 0; 142 | } 143 | 144 | void C3Di_ProcTexUpdate(C3D_Context* ctx) 145 | { 146 | if (!(ctx->texConfig & BIT(10))) 147 | return; 148 | 149 | if (ctx->flags & C3DiF_ProcTex) 150 | { 151 | ctx->flags &= ~C3DiF_ProcTex; 152 | if (ctx->procTex) 153 | GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT3_PROCTEX0, (u32*)ctx->procTex, 6); 154 | } 155 | if (ctx->flags & C3DiF_ProcTexLutAll) 156 | { 157 | int i; 158 | for (i = 0; i < 3; i ++) 159 | { 160 | int j = i ? (i+1) : 0; 161 | if (!(ctx->flags & C3DiF_ProcTexLut(i)) || !ctx->procTexLut[i]) 162 | continue; 163 | 164 | GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, j<<8); 165 | GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, *ctx->procTexLut[i], 128); 166 | } 167 | ctx->flags &= ~C3DiF_ProcTexLutAll; 168 | } 169 | if (ctx->flags & C3DiF_ProcTexColorLut) 170 | { 171 | ctx->flags &= ~C3DiF_ProcTexColorLut; 172 | if (ctx->procTexColorLut) 173 | { 174 | GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, GPU_LUT_COLOR<<8); 175 | GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, ctx->procTexColorLut->color, 256); 176 | GPUCMD_AddWrite(GPUREG_PROCTEX_LUT, GPU_LUT_COLORDIF<<8); 177 | GPUCMD_AddWrites(GPUREG_PROCTEX_LUT_DATA0, ctx->procTexColorLut->diff, 256); 178 | } 179 | } 180 | } 181 | 182 | void C3Di_ProcTexDirty(C3D_Context* ctx) 183 | { 184 | int i; 185 | if (!ctx->procTex) 186 | return; 187 | 188 | ctx->flags |= C3DiF_ProcTex; 189 | if (ctx->procTexColorLut) 190 | ctx->flags |= C3DiF_ProcTexColorLut; 191 | for (i = 0; i < 3; i ++) 192 | if (ctx->procTexLut[i]) 193 | ctx->flags |= C3DiF_ProcTexLut(i); 194 | } 195 | -------------------------------------------------------------------------------- /include/c3d/light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "lightlut.h" 3 | #include "maths.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | // Material 7 | //----------------------------------------------------------------------------- 8 | 9 | typedef struct 10 | { 11 | float ambient[3]; 12 | float diffuse[3]; 13 | float specular0[3]; 14 | float specular1[3]; 15 | float emission[3]; 16 | } C3D_Material; 17 | 18 | //----------------------------------------------------------------------------- 19 | // Light environment 20 | //----------------------------------------------------------------------------- 21 | 22 | // Forward declarations 23 | typedef struct C3D_Light_t C3D_Light; 24 | typedef struct C3D_LightEnv_t C3D_LightEnv; 25 | 26 | typedef struct 27 | { 28 | u32 abs, select, scale; 29 | } C3D_LightLutInputConf; 30 | 31 | typedef struct 32 | { 33 | u32 ambient; 34 | u32 numLights; 35 | u32 config[2]; 36 | C3D_LightLutInputConf lutInput; 37 | u32 permutation; 38 | } C3D_LightEnvConf; 39 | 40 | enum 41 | { 42 | C3DF_LightEnv_Dirty = BIT(0), 43 | C3DF_LightEnv_MtlDirty = BIT(1), 44 | C3DF_LightEnv_LCDirty = BIT(2), 45 | 46 | #define C3DF_LightEnv_IsCP(n) BIT(18+(n)) 47 | #define C3DF_LightEnv_IsCP_Any (0xFF<<18) 48 | #define C3DF_LightEnv_LutDirty(n) BIT(26+(n)) 49 | #define C3DF_LightEnv_LutDirtyAll (0x3F<<26) 50 | }; 51 | 52 | struct C3D_LightEnv_t 53 | { 54 | u32 flags; 55 | C3D_LightLut* luts[6]; 56 | float ambient[3]; 57 | C3D_Light* lights[8]; 58 | C3D_LightEnvConf conf; 59 | C3D_Material material; 60 | }; 61 | 62 | void C3D_LightEnvInit(C3D_LightEnv* env); 63 | void C3D_LightEnvBind(C3D_LightEnv* env); 64 | 65 | void C3D_LightEnvMaterial(C3D_LightEnv* env, const C3D_Material* mtl); 66 | void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b); 67 | void C3D_LightEnvLut(C3D_LightEnv* env, GPU_LIGHTLUTID lutId, GPU_LIGHTLUTINPUT input, bool negative, C3D_LightLut* lut); 68 | 69 | enum 70 | { 71 | GPU_SHADOW_PRIMARY = BIT(16), 72 | GPU_SHADOW_SECONDARY = BIT(17), 73 | GPU_INVERT_SHADOW = BIT(18), 74 | GPU_SHADOW_ALPHA = BIT(19), 75 | }; 76 | 77 | void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector); 78 | void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode); 79 | void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit); 80 | 81 | /** 82 | * @brief Configures whether to use the z component of the normal map. 83 | * @param[out] env Pointer to light environment structure. 84 | * @param[in] enable false if the z component is reconstructed from the xy components 85 | * of the normal map, true if the z component is taken from the normal map. 86 | */ 87 | void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool enable); 88 | void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode); 89 | void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit); 90 | void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp); 91 | 92 | //----------------------------------------------------------------------------- 93 | // Light 94 | //----------------------------------------------------------------------------- 95 | 96 | typedef struct 97 | { 98 | u32 specular0, specular1, diffuse, ambient; 99 | } C3D_LightMatConf; 100 | 101 | typedef struct 102 | { 103 | C3D_LightMatConf material; 104 | u16 position[3]; u16 padding0; 105 | u16 spotDir[3]; u16 padding1; 106 | u32 padding2; 107 | u32 config; 108 | u32 distAttnBias, distAttnScale; 109 | } C3D_LightConf; 110 | 111 | enum 112 | { 113 | C3DF_Light_Enabled = BIT(0), 114 | C3DF_Light_Dirty = BIT(1), 115 | C3DF_Light_MatDirty = BIT(2), 116 | //C3DF_Light_Shadow = BIT(3), 117 | //C3DF_Light_Spot = BIT(4), 118 | //C3DF_Light_DistAttn = BIT(5), 119 | 120 | C3DF_Light_SPDirty = BIT(14), 121 | C3DF_Light_DADirty = BIT(15), 122 | }; 123 | 124 | struct C3D_Light_t 125 | { 126 | u16 flags, id; 127 | C3D_LightEnv* parent; 128 | C3D_LightLut *lut_SP, *lut_DA; 129 | float ambient[3]; 130 | float diffuse[3]; 131 | float specular0[3]; 132 | float specular1[3]; 133 | C3D_LightConf conf; 134 | }; 135 | 136 | int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env); 137 | void C3D_LightEnable(C3D_Light* light, bool enable); 138 | void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable); 139 | void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable); 140 | void C3D_LightAmbient(C3D_Light* light, float r, float g, float b); 141 | void C3D_LightDiffuse(C3D_Light* light, float r, float g, float b); 142 | void C3D_LightSpecular0(C3D_Light* light, float r, float g, float b); 143 | void C3D_LightSpecular1(C3D_Light* light, float r, float g, float b); 144 | void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos); 145 | void C3D_LightShadowEnable(C3D_Light* light, bool enable); 146 | void C3D_LightSpotEnable(C3D_Light* light, bool enable); 147 | void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z); 148 | void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut); 149 | void C3D_LightDistAttnEnable(C3D_Light* light, bool enable); 150 | void C3D_LightDistAttn(C3D_Light* light, C3D_LightLutDA* lut); 151 | 152 | static inline void C3D_LightColor(C3D_Light* light, float r, float g, float b) 153 | { 154 | C3D_LightDiffuse(light, r, g, b); 155 | C3D_LightSpecular0(light, r, g, b); 156 | C3D_LightSpecular1(light, r, g, b); 157 | } 158 | -------------------------------------------------------------------------------- /source/light.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | void C3Di_LightMtlBlend(C3D_Light* light) 4 | { 5 | int i; 6 | C3D_Material* mtl = &light->parent->material; 7 | C3D_LightMatConf* conf = &light->conf.material; 8 | memset(conf, 0, sizeof(*conf)); 9 | 10 | for (i = 0; i < 3; i ++) 11 | { 12 | conf->specular0 |= ((u32)(255*(mtl->specular0[i]*light->specular0[i]))) << (i*10); 13 | conf->specular1 |= ((u32)(255*(mtl->specular1[i]*light->specular1[i]))) << (i*10); 14 | conf->diffuse |= ((u32)(255*(mtl->diffuse[i] *light->diffuse[i]))) << (i*10); 15 | conf->ambient |= ((u32)(255*(mtl->ambient[i] *light->ambient[i]))) << (i*10); 16 | } 17 | } 18 | 19 | int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env) 20 | { 21 | int i; 22 | memset(light, 0, sizeof(*light)); 23 | 24 | for (i = 0; i < 8; i ++) 25 | if (!env->lights[i]) 26 | break; 27 | if (i == 8) return -1; 28 | 29 | env->lights[i] = light; 30 | light->flags = C3DF_Light_Enabled | C3DF_Light_Dirty | C3DF_Light_MatDirty; 31 | light->id = i; 32 | light->parent = env; 33 | light->diffuse[0] = light->diffuse[1] = light->diffuse[2] = 1.0f; 34 | light->specular0[0] = light->specular0[1] = light->specular0[2] = 1.0f; 35 | light->specular1[0] = light->specular1[1] = light->specular1[2] = 1.0f; 36 | 37 | env->flags |= C3DF_LightEnv_LCDirty; 38 | return i; 39 | } 40 | 41 | void C3D_LightEnable(C3D_Light* light, bool enable) 42 | { 43 | if ((light->flags & C3DF_Light_Enabled) == (enable?C3DF_Light_Enabled:0)) 44 | return; 45 | 46 | if (enable) 47 | light->flags |= C3DF_Light_Enabled; 48 | else 49 | light->flags &= ~C3DF_Light_Enabled; 50 | 51 | light->parent->flags |= C3DF_LightEnv_LCDirty; 52 | } 53 | 54 | void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable) 55 | { 56 | if (enable) 57 | light->conf.config |= BIT(1); 58 | else 59 | light->conf.config &= ~BIT(1); 60 | light->flags |= C3DF_Light_Dirty; 61 | } 62 | 63 | void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable) 64 | { 65 | id = 2 + (id&1); 66 | if (enable) 67 | light->conf.config |= BIT(id); 68 | else 69 | light->conf.config &= ~BIT(id); 70 | light->flags |= C3DF_Light_Dirty; 71 | } 72 | 73 | void C3D_LightAmbient(C3D_Light* light, float r, float g, float b) 74 | { 75 | light->ambient[0] = b; 76 | light->ambient[1] = g; 77 | light->ambient[2] = r; 78 | light->flags |= C3DF_Light_MatDirty; 79 | } 80 | 81 | void C3D_LightDiffuse(C3D_Light* light, float r, float g, float b) 82 | { 83 | light->diffuse[0] = b; 84 | light->diffuse[1] = g; 85 | light->diffuse[2] = r; 86 | light->flags |= C3DF_Light_MatDirty; 87 | } 88 | 89 | void C3D_LightSpecular0(C3D_Light* light, float r, float g, float b) 90 | { 91 | light->specular0[0] = b; 92 | light->specular0[1] = g; 93 | light->specular0[2] = r; 94 | light->flags |= C3DF_Light_MatDirty; 95 | } 96 | 97 | void C3D_LightSpecular1(C3D_Light* light, float r, float g, float b) 98 | { 99 | light->specular1[0] = b; 100 | light->specular1[1] = g; 101 | light->specular1[2] = r; 102 | light->flags |= C3DF_Light_MatDirty; 103 | } 104 | 105 | void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos) 106 | { 107 | // Enable/disable positional light depending on W coordinate 108 | light->conf.config &= ~BIT(0); 109 | light->conf.config |= (pos->w == 0.0f); 110 | light->conf.position[0] = f32tof16(pos->x); 111 | light->conf.position[1] = f32tof16(pos->y); 112 | light->conf.position[2] = f32tof16(pos->z); 113 | light->flags |= C3DF_Light_Dirty; 114 | } 115 | 116 | static void C3Di_EnableCommon(C3D_Light* light, bool enable, u32 bit) 117 | { 118 | C3D_LightEnv* env = light->parent; 119 | u32* var = &env->conf.config[1]; 120 | 121 | if (enable == !(*var & bit)) 122 | return; 123 | 124 | if (!enable) 125 | *var |= bit; 126 | else 127 | *var &= ~bit; 128 | 129 | env->flags |= C3DF_LightEnv_Dirty; 130 | } 131 | 132 | void C3D_LightShadowEnable(C3D_Light* light, bool enable) 133 | { 134 | C3Di_EnableCommon(light, enable, GPU_LC1_SHADOWBIT(light->id)); 135 | } 136 | 137 | void C3D_LightSpotEnable(C3D_Light* light, bool enable) 138 | { 139 | C3Di_EnableCommon(light, enable, GPU_LC1_SPOTBIT(light->id)); 140 | } 141 | 142 | static inline u16 floattofix2_11(float x) 143 | { 144 | return (u16)((s32)(x * (1U<<11)) & 0x1FFF); 145 | } 146 | 147 | void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z) 148 | { 149 | C3Di_EnableCommon(light, true, GPU_LC1_SPOTBIT(light->id)); 150 | C3D_FVec vec = FVec3_New(-x, -y, -z); 151 | vec = FVec3_Normalize(vec); 152 | light->conf.spotDir[0] = floattofix2_11(vec.x); 153 | light->conf.spotDir[1] = floattofix2_11(vec.y); 154 | light->conf.spotDir[2] = floattofix2_11(vec.z); 155 | light->flags |= C3DF_Light_Dirty; 156 | } 157 | 158 | void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut) 159 | { 160 | bool hasLut = lut != NULL; 161 | C3Di_EnableCommon(light, hasLut, GPU_LC1_SPOTBIT(light->id)); 162 | light->lut_SP = lut; 163 | if (hasLut) 164 | light->flags |= C3DF_Light_SPDirty; 165 | } 166 | 167 | void C3D_LightDistAttnEnable(C3D_Light* light, bool enable) 168 | { 169 | C3Di_EnableCommon(light, enable, GPU_LC1_ATTNBIT(light->id)); 170 | } 171 | 172 | void C3D_LightDistAttn(C3D_Light* light, C3D_LightLutDA* lut) 173 | { 174 | bool hasLut = lut != NULL; 175 | C3Di_EnableCommon(light, hasLut, GPU_LC1_ATTNBIT(light->id)); 176 | if (!hasLut) return; 177 | 178 | light->conf.distAttnBias = f32tof20(lut->bias); 179 | light->conf.distAttnScale = f32tof20(lut->scale); 180 | light->lut_DA = &lut->lut; 181 | light->flags |= C3DF_Light_Dirty | C3DF_Light_DADirty; 182 | } 183 | -------------------------------------------------------------------------------- /include/c3d/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct 5 | { 6 | void* data[6]; 7 | } C3D_TexCube; 8 | 9 | typedef struct 10 | { 11 | union 12 | { 13 | void* data; 14 | C3D_TexCube* cube; 15 | }; 16 | 17 | GPU_TEXCOLOR fmt : 4; 18 | size_t size : 28; 19 | 20 | union 21 | { 22 | u32 dim; 23 | struct 24 | { 25 | u16 height; 26 | u16 width; 27 | }; 28 | }; 29 | 30 | u32 param; 31 | u32 border; 32 | union 33 | { 34 | u32 lodParam; 35 | struct 36 | { 37 | u16 lodBias; 38 | u8 maxLevel; 39 | u8 minLevel; 40 | }; 41 | }; 42 | } C3D_Tex; 43 | 44 | typedef struct CTR_ALIGN(8) 45 | { 46 | u16 width; 47 | u16 height; 48 | u8 maxLevel : 4; 49 | GPU_TEXCOLOR format : 4; 50 | GPU_TEXTURE_MODE_PARAM type : 3; 51 | bool onVram : 1; 52 | } C3D_TexInitParams; 53 | 54 | bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p); 55 | void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level); 56 | void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face); 57 | void C3D_TexBind(int unitId, C3D_Tex* tex); 58 | void C3D_TexFlush(C3D_Tex* tex); 59 | void C3D_TexDelete(C3D_Tex* tex); 60 | 61 | void C3D_TexShadowParams(bool perspective, float bias); 62 | 63 | static inline int C3D_TexCalcMaxLevel(u32 width, u32 height) 64 | { 65 | return (31-__builtin_clz(width < height ? width : height)) - 3; // avoid sizes smaller than 8 66 | } 67 | 68 | static inline u32 C3D_TexCalcLevelSize(u32 size, int level) 69 | { 70 | return size >> (2*level); 71 | } 72 | 73 | static inline u32 C3D_TexCalcTotalSize(u32 size, int maxLevel) 74 | { 75 | /* 76 | S = s + sr + sr^2 + sr^3 + ... + sr^n 77 | Sr = sr + sr^2 + sr^3 + ... + sr^(n+1) 78 | S-Sr = s - sr^(n+1) 79 | S(1-r) = s(1 - r^(n+1)) 80 | S = s (1 - r^(n+1)) / (1-r) 81 | 82 | r = 1/4 83 | 1-r = 3/4 84 | 85 | S = 4s (1 - (1/4)^(n+1)) / 3 86 | S = 4s (1 - 1/4^(n+1)) / 3 87 | S = (4/3) (s - s/4^(n+1)) 88 | S = (4/3) (s - s/(1<<(2n+2))) 89 | S = (4/3) (s - s>>(2n+2)) 90 | */ 91 | return (size - C3D_TexCalcLevelSize(size,maxLevel+1)) * 4 / 3; 92 | } 93 | 94 | static inline bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) 95 | { 96 | return C3D_TexInitWithParams(tex, NULL, 97 | (C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, false }); 98 | } 99 | 100 | static inline bool C3D_TexInitMipmap(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) 101 | { 102 | return C3D_TexInitWithParams(tex, NULL, 103 | (C3D_TexInitParams){ width, height, (u8)C3D_TexCalcMaxLevel(width, height), format, GPU_TEX_2D, false }); 104 | } 105 | 106 | static inline bool C3D_TexInitCube(C3D_Tex* tex, C3D_TexCube* cube, u16 width, u16 height, GPU_TEXCOLOR format) 107 | { 108 | return C3D_TexInitWithParams(tex, cube, 109 | (C3D_TexInitParams){ width, height, 0, format, GPU_TEX_CUBE_MAP, false }); 110 | } 111 | 112 | static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) 113 | { 114 | return C3D_TexInitWithParams(tex, NULL, 115 | (C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, true }); 116 | } 117 | 118 | static inline bool C3D_TexInitShadow(C3D_Tex* tex, u16 width, u16 height) 119 | { 120 | return C3D_TexInitWithParams(tex, NULL, 121 | (C3D_TexInitParams){ width, height, 0, GPU_RGBA8, GPU_TEX_SHADOW_2D, true }); 122 | } 123 | 124 | static inline bool C3D_TexInitShadowCube(C3D_Tex* tex, C3D_TexCube* cube, u16 width, u16 height) 125 | { 126 | return C3D_TexInitWithParams(tex, cube, 127 | (C3D_TexInitParams){ width, height, 0, GPU_RGBA8, GPU_TEX_SHADOW_CUBE, true }); 128 | } 129 | 130 | static inline GPU_TEXTURE_MODE_PARAM C3D_TexGetType(C3D_Tex* tex) 131 | { 132 | return (GPU_TEXTURE_MODE_PARAM)((tex->param>>28)&0x7); 133 | } 134 | 135 | static inline void* C3D_TexGetImagePtr(C3D_Tex* tex, void* data, int level, u32* size) 136 | { 137 | if (size) *size = level >= 0 ? C3D_TexCalcLevelSize(tex->size, level) : C3D_TexCalcTotalSize(tex->size, tex->maxLevel); 138 | if (!level) return data; 139 | return (u8*)data + (level > 0 ? C3D_TexCalcTotalSize(tex->size, level-1) : 0); 140 | } 141 | 142 | static inline void* C3D_Tex2DGetImagePtr(C3D_Tex* tex, int level, u32* size) 143 | { 144 | return C3D_TexGetImagePtr(tex, tex->data, level, size); 145 | } 146 | 147 | static inline void* C3D_TexCubeGetImagePtr(C3D_Tex* tex, GPU_TEXFACE face, int level, u32* size) 148 | { 149 | return C3D_TexGetImagePtr(tex, tex->cube->data[face], level, size); 150 | } 151 | 152 | static inline void C3D_TexUpload(C3D_Tex* tex, const void* data) 153 | { 154 | C3D_TexLoadImage(tex, data, GPU_TEXFACE_2D, 0); 155 | } 156 | 157 | static inline void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) 158 | { 159 | tex->param &= ~(GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)); 160 | tex->param |= GPU_TEXTURE_MAG_FILTER(magFilter) | GPU_TEXTURE_MIN_FILTER(minFilter); 161 | } 162 | 163 | static inline void C3D_TexSetFilterMipmap(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM filter) 164 | { 165 | tex->param &= ~GPU_TEXTURE_MIP_FILTER(GPU_LINEAR); 166 | tex->param |= GPU_TEXTURE_MIP_FILTER(filter); 167 | } 168 | 169 | static inline void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT) 170 | { 171 | tex->param &= ~(GPU_TEXTURE_WRAP_S(3) | GPU_TEXTURE_WRAP_T(3)); 172 | tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT); 173 | } 174 | 175 | static inline void C3D_TexSetLodBias(C3D_Tex* tex, float lodBias) 176 | { 177 | int iLodBias = (int)(lodBias*0x100); 178 | if (iLodBias > 0xFFF) 179 | iLodBias = 0xFFF; 180 | else if (iLodBias < -0x1000) 181 | iLodBias = -0x1000; 182 | tex->lodBias = iLodBias & 0x1FFF; 183 | } 184 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/3ds_rules 10 | 11 | export CITRO3D_MAJOR := 1 12 | export CITRO3D_MINOR := 7 13 | export CITRO3D_PATCH := 1 14 | 15 | VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH) 16 | 17 | #--------------------------------------------------------------------------------- 18 | # TARGET is the name of the output 19 | # BUILD is the directory where object files & intermediate files will be placed 20 | # SOURCES is a list of directories containing source code 21 | # DATA is a list of directories containing data files 22 | # INCLUDES is a list of directories containing header files 23 | #--------------------------------------------------------------------------------- 24 | TARGET := citro3d 25 | SOURCES := source source/maths 26 | DATA := data 27 | INCLUDES := include 28 | 29 | #--------------------------------------------------------------------------------- 30 | # options for code generation 31 | #--------------------------------------------------------------------------------- 32 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 33 | 34 | CFLAGS := -g -Wall -Wno-sizeof-array-div -Werror -mword-relocations \ 35 | -ffunction-sections -fdata-sections \ 36 | $(ARCH) $(BUILD_CFLAGS) 37 | 38 | CFLAGS += $(INCLUDE) -D__3DS__ -DCITRO3D_BUILD 39 | 40 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 41 | 42 | ASFLAGS := -g $(ARCH) $(DEFINES) 43 | 44 | #--------------------------------------------------------------------------------- 45 | # list of directories containing libraries, this must be the top level containing 46 | # include and lib 47 | #--------------------------------------------------------------------------------- 48 | LIBDIRS := $(CTRULIB) 49 | 50 | #--------------------------------------------------------------------------------- 51 | # no real need to edit anything past this point unless you need to add additional 52 | # rules for different file extensions 53 | #--------------------------------------------------------------------------------- 54 | ifneq ($(BUILD),$(notdir $(CURDIR))) 55 | #--------------------------------------------------------------------------------- 56 | 57 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 58 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 59 | 60 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 61 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 62 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 63 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 64 | 65 | #--------------------------------------------------------------------------------- 66 | # use CXX for linking C++ projects, CC for standard C 67 | #--------------------------------------------------------------------------------- 68 | ifeq ($(strip $(CPPFILES)),) 69 | #--------------------------------------------------------------------------------- 70 | export LD := $(CC) 71 | #--------------------------------------------------------------------------------- 72 | else 73 | #--------------------------------------------------------------------------------- 74 | export LD := $(CXX) 75 | #--------------------------------------------------------------------------------- 76 | endif 77 | #--------------------------------------------------------------------------------- 78 | 79 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 80 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 81 | 82 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 83 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 84 | -I. 85 | 86 | .PHONY: clean all doc 87 | 88 | #--------------------------------------------------------------------------------- 89 | all: lib/libcitro3d.a lib/libcitro3dd.a 90 | 91 | doc: 92 | @doxygen Doxyfile 93 | 94 | dist-bin: all 95 | @tar --exclude=*~ -cjf citro3d-$(VERSION).tar.bz2 include lib 96 | 97 | dist-src: 98 | @tar --exclude=*~ -cjf citro3d-src-$(VERSION).tar.bz2 include source Makefile 99 | 100 | dist: dist-src dist-bin 101 | 102 | install: dist-bin 103 | mkdir -p $(DESTDIR)$(DEVKITPRO)/libctru 104 | bzip2 -cd citro3d-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(DEVKITPRO)/libctru 105 | 106 | lib: 107 | @[ -d $@ ] || mkdir -p $@ 108 | 109 | release: 110 | @[ -d $@ ] || mkdir -p $@ 111 | 112 | debug: 113 | @[ -d $@ ] || mkdir -p $@ 114 | 115 | lib/libcitro3d.a : lib release $(SOURCES) $(INCLUDES) 116 | @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ 117 | BUILD_CFLAGS="-DNDEBUG=1 -O2 -fomit-frame-pointer -fno-math-errno" \ 118 | DEPSDIR=$(CURDIR)/release \ 119 | --no-print-directory -C release \ 120 | -f $(CURDIR)/Makefile 121 | 122 | lib/libcitro3dd.a : lib debug $(SOURCES) $(INCLUDES) 123 | @$(MAKE) BUILD=debug OUTPUT=$(CURDIR)/$@ \ 124 | BUILD_CFLAGS="-DDEBUG=1 -Og" \ 125 | DEPSDIR=$(CURDIR)/debug \ 126 | --no-print-directory -C debug \ 127 | -f $(CURDIR)/Makefile 128 | 129 | #--------------------------------------------------------------------------------- 130 | clean: 131 | @echo clean ... 132 | @rm -fr release debug lib 133 | 134 | #--------------------------------------------------------------------------------- 135 | else 136 | 137 | DEPENDS := $(OFILES:.o=.d) 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | $(OUTPUT) : $(OFILES) 143 | 144 | #--------------------------------------------------------------------------------- 145 | %.bin.o : %.bin 146 | #--------------------------------------------------------------------------------- 147 | @echo $(notdir $<) 148 | @$(bin2o) 149 | 150 | -include $(DEPENDS) 151 | 152 | #--------------------------------------------------------------------------------------- 153 | endif 154 | #--------------------------------------------------------------------------------------- 155 | -------------------------------------------------------------------------------- /source/maths/mtx_inverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | float Mtx_Inverse(C3D_Mtx* out) 5 | { 6 | //Mtx_Inverse can be used to calculate the determinant and the inverse of the matrix. 7 | 8 | C3D_Mtx inv; 9 | float det; 10 | int i; 11 | 12 | inv.r[0].x = out->r[1].y * out->r[2].z * out->r[3].w - 13 | out->r[1].y * out->r[2].w * out->r[3].z - 14 | out->r[2].y * out->r[1].z * out->r[3].w + 15 | out->r[2].y * out->r[1].w * out->r[3].z + 16 | out->r[3].y * out->r[1].z * out->r[2].w - 17 | out->r[3].y * out->r[1].w * out->r[2].z; 18 | 19 | inv.r[1].x = -out->r[1].x * out->r[2].z * out->r[3].w + 20 | out->r[1].x * out->r[2].w * out->r[3].z + 21 | out->r[2].x * out->r[1].z * out->r[3].w - 22 | out->r[2].x * out->r[1].w * out->r[3].z - 23 | out->r[3].x * out->r[1].z * out->r[2].w + 24 | out->r[3].x * out->r[1].w * out->r[2].z; 25 | 26 | inv.r[2].x = out->r[1].x * out->r[2].y * out->r[3].w - 27 | out->r[1].x * out->r[2].w * out->r[3].y - 28 | out->r[2].x * out->r[1].y * out->r[3].w + 29 | out->r[2].x * out->r[1].w * out->r[3].y + 30 | out->r[3].x * out->r[1].y * out->r[2].w - 31 | out->r[3].x * out->r[1].w * out->r[2].y; 32 | 33 | inv.r[3].x = -out->r[1].x * out->r[2].y * out->r[3].z + 34 | out->r[1].x * out->r[2].z * out->r[3].y + 35 | out->r[2].x * out->r[1].y * out->r[3].z - 36 | out->r[2].x * out->r[1].z * out->r[3].y - 37 | out->r[3].x * out->r[1].y * out->r[2].z + 38 | out->r[3].x * out->r[1].z * out->r[2].y; 39 | 40 | det = out->r[0].x * inv.r[0].x + out->r[0].y * inv.r[1].x + 41 | out->r[0].z * inv.r[2].x + out->r[0].w * inv.r[3].x; 42 | 43 | if (fabsf(det) < FLT_EPSILON) 44 | //Returns 0.0f if we find the determinant is less than +/- FLT_EPSILON. 45 | return 0.0f; 46 | 47 | inv.r[0].y = -out->r[0].y * out->r[2].z * out->r[3].w + 48 | out->r[0].y * out->r[2].w * out->r[3].z + 49 | out->r[2].y * out->r[0].z * out->r[3].w - 50 | out->r[2].y * out->r[0].w * out->r[3].z - 51 | out->r[3].y * out->r[0].z * out->r[2].w + 52 | out->r[3].y * out->r[0].w * out->r[2].z; 53 | 54 | inv.r[1].y = out->r[0].x * out->r[2].z * out->r[3].w - 55 | out->r[0].x * out->r[2].w * out->r[3].z - 56 | out->r[2].x * out->r[0].z * out->r[3].w + 57 | out->r[2].x * out->r[0].w * out->r[3].z + 58 | out->r[3].x * out->r[0].z * out->r[2].w - 59 | out->r[3].x * out->r[0].w * out->r[2].z; 60 | 61 | inv.r[2].y = -out->r[0].x * out->r[2].y * out->r[3].w + 62 | out->r[0].x * out->r[2].w * out->r[3].y + 63 | out->r[2].x * out->r[0].y * out->r[3].w - 64 | out->r[2].x * out->r[0].w * out->r[3].y - 65 | out->r[3].x * out->r[0].y * out->r[2].w + 66 | out->r[3].x * out->r[0].w * out->r[2].y; 67 | 68 | inv.r[3].y = out->r[0].x * out->r[2].y * out->r[3].z - 69 | out->r[0].x * out->r[2].z * out->r[3].y - 70 | out->r[2].x * out->r[0].y * out->r[3].z + 71 | out->r[2].x * out->r[0].z * out->r[3].y + 72 | out->r[3].x * out->r[0].y * out->r[2].z - 73 | out->r[3].x * out->r[0].z * out->r[2].y; 74 | 75 | inv.r[0].z = out->r[0].y * out->r[1].z * out->r[3].w - 76 | out->r[0].y * out->r[1].w * out->r[3].z - 77 | out->r[1].y * out->r[0].z * out->r[3].w + 78 | out->r[1].y * out->r[0].w * out->r[3].z + 79 | out->r[3].y * out->r[0].z * out->r[1].w - 80 | out->r[3].y * out->r[0].w * out->r[1].z; 81 | 82 | inv.r[1].z = -out->r[0].x * out->r[1].z * out->r[3].w + 83 | out->r[0].x * out->r[1].w * out->r[3].z + 84 | out->r[1].x * out->r[0].z * out->r[3].w - 85 | out->r[1].x * out->r[0].w * out->r[3].z - 86 | out->r[3].x * out->r[0].z * out->r[1].w + 87 | out->r[3].x * out->r[0].w * out->r[1].z; 88 | 89 | inv.r[2].z = out->r[0].x * out->r[1].y * out->r[3].w - 90 | out->r[0].x * out->r[1].w * out->r[3].y - 91 | out->r[1].x * out->r[0].y * out->r[3].w + 92 | out->r[1].x * out->r[0].w * out->r[3].y + 93 | out->r[3].x * out->r[0].y * out->r[1].w - 94 | out->r[3].x * out->r[0].w * out->r[1].y; 95 | 96 | inv.r[3].z = -out->r[0].x * out->r[1].y * out->r[3].z + 97 | out->r[0].x * out->r[1].z * out->r[3].y + 98 | out->r[1].x * out->r[0].y * out->r[3].z - 99 | out->r[1].x * out->r[0].z * out->r[3].y - 100 | out->r[3].x * out->r[0].y * out->r[1].z + 101 | out->r[3].x * out->r[0].z * out->r[1].y; 102 | 103 | inv.r[0].w = -out->r[0].y * out->r[1].z * out->r[2].w + 104 | out->r[0].y * out->r[1].w * out->r[2].z + 105 | out->r[1].y * out->r[0].z * out->r[2].w - 106 | out->r[1].y * out->r[0].w * out->r[2].z - 107 | out->r[2].y * out->r[0].z * out->r[1].w + 108 | out->r[2].y * out->r[0].w * out->r[1].z; 109 | 110 | inv.r[1].w = out->r[0].x * out->r[1].z * out->r[2].w - 111 | out->r[0].x * out->r[1].w * out->r[2].z - 112 | out->r[1].x * out->r[0].z * out->r[2].w + 113 | out->r[1].x * out->r[0].w * out->r[2].z + 114 | out->r[2].x * out->r[0].z * out->r[1].w - 115 | out->r[2].x * out->r[0].w * out->r[1].z; 116 | 117 | inv.r[2].w = -out->r[0].x * out->r[1].y * out->r[2].w + 118 | out->r[0].x * out->r[1].w * out->r[2].y + 119 | out->r[1].x * out->r[0].y * out->r[2].w - 120 | out->r[1].x * out->r[0].w * out->r[2].y - 121 | out->r[2].x * out->r[0].y * out->r[1].w + 122 | out->r[2].x * out->r[0].w * out->r[1].y; 123 | 124 | inv.r[3].w = out->r[0].x * out->r[1].y * out->r[2].z - 125 | out->r[0].x * out->r[1].z * out->r[2].y - 126 | out->r[1].x * out->r[0].y * out->r[2].z + 127 | out->r[1].x * out->r[0].z * out->r[2].y + 128 | out->r[2].x * out->r[0].y * out->r[1].z - 129 | out->r[2].x * out->r[0].z * out->r[1].y; 130 | 131 | for (i = 0; i < 16; i++) 132 | out->m[i] = inv.m[i] / det; 133 | 134 | return det; 135 | } 136 | -------------------------------------------------------------------------------- /include/tex3ds.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | * Copyright (c) 2017 3 | * Michael Theall (mtheall) 4 | * 5 | * This file is part of citro3d. 6 | * 7 | * This software is provided 'as-is', without any express or implied warranty. 8 | * In no event will the authors be held liable for any damages arising from the 9 | * use of this software. 10 | * 11 | * Permission is granted to anyone to use this software for any purpose, 12 | * including commercial applications, and to alter it and redistribute it 13 | * freely, subject to the following restrictions: 14 | * 15 | * 1. The origin of this software must not be misrepresented; you must not 16 | * claim that you wrote the original software. If you use this software in 17 | * a product, an acknowledgment in the product documentation would be 18 | * appreciated but is not required. 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | *----------------------------------------------------------------------------*/ 23 | /** @file tex3ds.h 24 | * @brief tex3ds support 25 | */ 26 | #pragma once 27 | #ifdef CITRO3D_BUILD 28 | #include "c3d/texture.h" 29 | #else 30 | #include 31 | #endif 32 | 33 | #include 34 | #include 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /** @brief Subtexture 41 | * @note If top < bottom, the subtexture is rotated 1/4 revolution counter-clockwise 42 | */ 43 | typedef struct Tex3DS_SubTexture 44 | { 45 | u16 width; ///< Sub-texture width (pixels) 46 | u16 height; ///< Sub-texture height (pixels) 47 | float left; ///< Left u-coordinate 48 | float top; ///< Top v-coordinate 49 | float right; ///< Right u-coordinate 50 | float bottom; ///< Bottom v-coordinate 51 | } Tex3DS_SubTexture; 52 | 53 | /** @brief Texture */ 54 | typedef struct Tex3DS_Texture_s* Tex3DS_Texture; 55 | 56 | /** @brief Import Tex3DS texture 57 | * @param[in] input Input data 58 | * @param[in] insize Size of the input data 59 | * @param[out] tex citro3d texture 60 | * @param[out] texcube citro3d texcube 61 | * @param[in] vram Whether to store textures in VRAM 62 | * @returns Tex3DS texture 63 | */ 64 | Tex3DS_Texture Tex3DS_TextureImport(const void* input, size_t insize, C3D_Tex* tex, C3D_TexCube* texcube, bool vram); 65 | 66 | /** @brief Import Tex3DS texture 67 | * 68 | * @description 69 | * For example, use this if you want to import from a large file without 70 | * pulling the entire file into memory. 71 | * 72 | * @param[out] tex citro3d texture 73 | * @param[out] texcube citro3d texcube 74 | * @param[in] vram Whether to store textures in VRAM 75 | * @param[in] callback Data callback 76 | * @param[in] userdata User data passed to callback 77 | * @returns Tex3DS texture 78 | */ 79 | Tex3DS_Texture Tex3DS_TextureImportCallback(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata); 80 | 81 | /** @brief Import Tex3DS texture 82 | * 83 | * Starts reading at the current file descriptor's offset. The file 84 | * descriptor's position is left at the end of the decoded data. On error, the 85 | * file descriptor's position is indeterminate. 86 | * 87 | * @param[in] fd Open file descriptor 88 | * @param[out] tex citro3d texture 89 | * @param[out] texcube citro3d texcube 90 | * @param[in] vram Whether to store textures in VRAM 91 | * @returns Tex3DS texture 92 | */ 93 | Tex3DS_Texture Tex3DS_TextureImportFD(int fd, C3D_Tex* tex, C3D_TexCube* texcube, bool vram); 94 | 95 | /** @brief Import Tex3DS texture 96 | * 97 | * Starts reading at the current file stream's offset. The file stream's 98 | * position is left at the end of the decoded data. On error, the file 99 | * stream's position is indeterminate. 100 | * 101 | * @param[in] fp Open file stream 102 | * @param[out] tex citro3d texture 103 | * @param[out] texcube citro3d texcube 104 | * @param[in] vram Whether to store textures in VRAM 105 | * @returns Tex3DS texture 106 | */ 107 | Tex3DS_Texture Tex3DS_TextureImportStdio(FILE* fp, C3D_Tex* tex, C3D_TexCube* texcube, bool vram); 108 | 109 | /** @brief Get number of subtextures 110 | * @param[in] texture Tex3DS texture 111 | * @returns Number of subtextures 112 | */ 113 | size_t Tex3DS_GetNumSubTextures(const Tex3DS_Texture texture); 114 | 115 | /** @brief Get subtexture 116 | * @param[in] texture Tex3DS texture 117 | * @param[in] index Subtexture index 118 | * @returns Subtexture info 119 | */ 120 | const Tex3DS_SubTexture* Tex3DS_GetSubTexture(const Tex3DS_Texture texture, size_t index); 121 | 122 | /** @brief Check if subtexture is rotated 123 | * @param[in] subtex Subtexture to check 124 | * @returns whether subtexture is rotated 125 | */ 126 | static inline bool 127 | Tex3DS_SubTextureRotated(const Tex3DS_SubTexture* subtex) 128 | { 129 | return subtex->top < subtex->bottom; 130 | } 131 | 132 | /** @brief Get bottom-left texcoords 133 | * @param[in] subtex Subtexture 134 | * @param[out] u u-coordinate 135 | * @param[out] v v-coordinate 136 | */ 137 | static inline void 138 | Tex3DS_SubTextureBottomLeft(const Tex3DS_SubTexture* subtex, float* u, float* v) 139 | { 140 | if (!Tex3DS_SubTextureRotated(subtex)) 141 | { 142 | *u = subtex->left; 143 | *v = subtex->bottom; 144 | } else 145 | { 146 | *u = subtex->bottom; 147 | *v = subtex->left; 148 | } 149 | } 150 | 151 | /** @brief Get bottom-right texcoords 152 | * @param[in] subtex Subtexture 153 | * @param[out] u u-coordinate 154 | * @param[out] v v-coordinate 155 | */ 156 | static inline void 157 | Tex3DS_SubTextureBottomRight(const Tex3DS_SubTexture* subtex, float* u, float* v) 158 | { 159 | if (!Tex3DS_SubTextureRotated(subtex)) 160 | { 161 | *u = subtex->right; 162 | *v = subtex->bottom; 163 | } else 164 | { 165 | *u = subtex->bottom; 166 | *v = subtex->right; 167 | } 168 | } 169 | 170 | /** @brief Get top-left texcoords 171 | * @param[in] subtex Subtexture 172 | * @param[out] u u-coordinate 173 | * @param[out] v v-coordinate 174 | */ 175 | static inline void 176 | Tex3DS_SubTextureTopLeft(const Tex3DS_SubTexture* subtex, float* u, float* v) 177 | { 178 | if (!Tex3DS_SubTextureRotated(subtex)) 179 | { 180 | *u = subtex->left; 181 | *v = subtex->top; 182 | } else 183 | { 184 | *u = subtex->top; 185 | *v = subtex->left; 186 | } 187 | } 188 | 189 | /** @brief Get top-right texcoords 190 | * @param[in] subtex Subtexture 191 | * @param[out] u u-coordinate 192 | * @param[out] v v-coordinate 193 | */ 194 | static inline void 195 | Tex3DS_SubTextureTopRight(const Tex3DS_SubTexture* subtex, float* u, float* v) 196 | { 197 | if (!Tex3DS_SubTextureRotated(subtex)) 198 | { 199 | *u = subtex->right; 200 | *v = subtex->top; 201 | } else 202 | { 203 | *u = subtex->top; 204 | *v = subtex->right; 205 | } 206 | } 207 | 208 | /** @brief Free Tex3DS texture 209 | * @param[in] texture Tex3DS texture to free 210 | */ 211 | void Tex3DS_TextureFree(Tex3DS_Texture texture); 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | -------------------------------------------------------------------------------- /source/tex3ds.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | * Copyright (c) 2017 3 | * Michael Theall (mtheall) 4 | * 5 | * This file is part of citro3d. 6 | * 7 | * This software is provided 'as-is', without any express or implied warranty. 8 | * In no event will the authors be held liable for any damages arising from the 9 | * use of this software. 10 | * 11 | * Permission is granted to anyone to use this software for any purpose, 12 | * including commercial applications, and to alter it and redistribute it 13 | * freely, subject to the following restrictions: 14 | * 15 | * 1. The origin of this software must not be misrepresented; you must not 16 | * claim that you wrote the original software. If you use this software in 17 | * a product, an acknowledgment in the product documentation would be 18 | * appreciated but is not required. 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | *----------------------------------------------------------------------------*/ 23 | /** @file tex3ds.c 24 | * @brief Tex3DS routines 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /** @brief Tex3DS texture 33 | */ 34 | struct Tex3DS_Texture_s 35 | { 36 | u16 numSubTextures; ///< Number of subtextures 37 | u16 width; ///< Texture width 38 | u16 height; ///< Texture height 39 | u8 format; ///< Texture format 40 | u8 mipmapLevels; ///< Number of mipmaps 41 | Tex3DS_SubTexture subTextures[]; ///< Subtextures 42 | }; 43 | 44 | typedef struct __attribute__((packed)) 45 | { 46 | u16 numSubTextures; 47 | u8 width_log2 : 3; 48 | u8 height_log2 : 3; 49 | u8 type : 1; 50 | u8 format; 51 | u8 mipmapLevels; 52 | } Tex3DSi_Header; 53 | 54 | typedef struct 55 | { 56 | u16 width, height; 57 | u16 left, top, right, bottom; 58 | } Tex3DSi_SubTexture; 59 | 60 | static inline bool Tex3DSi_ReadData(decompressCallback callback, void** userdata, void* buffer, size_t size, size_t* insize) 61 | { 62 | if (callback) 63 | return callback(*userdata, buffer, size) == size; 64 | if (size > *insize) 65 | return false; 66 | 67 | memcpy(buffer, *userdata, size); 68 | *userdata = (u8*)*userdata + size; 69 | *insize -= size; 70 | return true; 71 | } 72 | 73 | static Tex3DS_Texture 74 | Tex3DSi_ImportCommon(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata, size_t insize) 75 | { 76 | // Read header 77 | Tex3DSi_Header hdr; 78 | if (!Tex3DSi_ReadData(callback, &userdata, &hdr, sizeof(hdr), &insize)) 79 | return NULL; 80 | 81 | // Allocate space for header + subtextures 82 | Tex3DS_Texture texture = (Tex3DS_Texture)malloc(sizeof(struct Tex3DS_Texture_s) + hdr.numSubTextures*sizeof(Tex3DS_SubTexture)); 83 | if (!texture) 84 | return NULL; 85 | 86 | // Fill texture metadata structure 87 | texture->numSubTextures = hdr.numSubTextures; 88 | texture->width = 1 << (hdr.width_log2 + 3); 89 | texture->height = 1 << (hdr.height_log2 + 3); 90 | texture->format = hdr.format; 91 | texture->mipmapLevels = hdr.mipmapLevels; 92 | 93 | // Read subtexture info 94 | for (size_t i = 0; i < hdr.numSubTextures; i ++) 95 | { 96 | Tex3DSi_SubTexture subtex; 97 | if (!Tex3DSi_ReadData(callback, &userdata, &subtex, sizeof(Tex3DSi_SubTexture), &insize)) 98 | { 99 | free(texture); 100 | return NULL; 101 | } 102 | texture->subTextures[i].width = subtex.width; 103 | texture->subTextures[i].height = subtex.height; 104 | texture->subTextures[i].left = subtex.left / 1024.0f; 105 | texture->subTextures[i].top = subtex.top / 1024.0f; 106 | texture->subTextures[i].right = subtex.right / 1024.0f; 107 | texture->subTextures[i].bottom = subtex.bottom / 1024.0f; 108 | } 109 | 110 | // Allocate texture memory 111 | C3D_TexInitParams params; 112 | params.width = texture->width; 113 | params.height = texture->height; 114 | params.maxLevel = texture->mipmapLevels; 115 | params.format = texture->format; 116 | params.type = (GPU_TEXTURE_MODE_PARAM)hdr.type; 117 | params.onVram = vram; 118 | if (!C3D_TexInitWithParams(tex, texcube, params)) 119 | { 120 | free(texture); 121 | return NULL; 122 | } 123 | 124 | // Get texture size, including mipmaps 125 | size_t base_texsize = C3D_TexCalcTotalSize(tex->size, texture->mipmapLevels); 126 | size_t texsize = base_texsize; 127 | 128 | // If this is a cubemap/skybox, there are 6 textures 129 | if (params.type == GPU_TEX_CUBE_MAP) 130 | texsize *= 6; 131 | 132 | if (vram) 133 | { 134 | // Allocate staging buffer in linear memory 135 | void* texdata = linearAlloc(texsize); 136 | if (!texdata) 137 | { 138 | C3D_TexDelete(tex); 139 | free(texture); 140 | return NULL; 141 | } 142 | 143 | // Decompress into staging buffer for VRAM upload 144 | if (!decompress(texdata, texsize, callback, userdata, insize)) 145 | { 146 | linearFree(texdata); 147 | C3D_TexDelete(tex); 148 | free(texture); 149 | return NULL; 150 | } 151 | 152 | // Flush buffer to prepare DMA to VRAM 153 | GSPGPU_FlushDataCache(texdata, texsize); 154 | 155 | size_t texcount = 1; 156 | if (params.type == GPU_TEX_CUBE_MAP) 157 | texcount = 6; 158 | 159 | // Upload texture(s) to VRAM 160 | for (size_t i = 0; i < texcount; ++i) 161 | C3D_TexLoadImage(tex, (u8*)texdata + i * base_texsize, i, -1); 162 | 163 | linearFree(texdata); 164 | } else if (params.type == GPU_TEX_CUBE_MAP) 165 | { 166 | decompressIOVec iov[6]; 167 | 168 | // Setup IO vectors 169 | for (size_t i = 0; i < 6; ++i) 170 | { 171 | u32 size; 172 | iov[i].data = C3D_TexCubeGetImagePtr(tex, i, -1, &size); 173 | iov[i].size = size; 174 | } 175 | 176 | // Decompress into texture memory 177 | if (!decompressV(iov, 6, callback, userdata, insize)) 178 | { 179 | C3D_TexDelete(tex); 180 | free(texture); 181 | return NULL; 182 | } 183 | } else 184 | { 185 | u32 size; 186 | void* data = C3D_Tex2DGetImagePtr(tex, -1, &size); 187 | 188 | // Decompress into texture memory 189 | if (!decompress(data, size, callback, userdata, insize)) 190 | { 191 | C3D_TexDelete(tex); 192 | free(texture); 193 | return NULL; 194 | } 195 | } 196 | 197 | return texture; 198 | } 199 | 200 | Tex3DS_Texture 201 | Tex3DS_TextureImport(const void* input, size_t insize, C3D_Tex* tex, C3D_TexCube* texcube, bool vram) 202 | { 203 | return Tex3DSi_ImportCommon(tex, texcube, vram, NULL, (void*)input, insize); 204 | } 205 | 206 | Tex3DS_Texture 207 | Tex3DS_TextureImportCallback(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata) 208 | { 209 | return Tex3DSi_ImportCommon(tex, texcube, vram, callback, userdata, 0); 210 | } 211 | 212 | Tex3DS_Texture 213 | Tex3DS_TextureImportFD(int fd, C3D_Tex* tex, C3D_TexCube* texcube, bool vram) 214 | { 215 | return Tex3DSi_ImportCommon(tex, texcube, vram, decompressCallback_FD, &fd, 0); 216 | } 217 | 218 | Tex3DS_Texture 219 | Tex3DS_TextureImportStdio(FILE* fp, C3D_Tex* tex, C3D_TexCube* texcube, bool vram) 220 | { 221 | return Tex3DSi_ImportCommon(tex, texcube, vram, decompressCallback_Stdio, fp, 0); 222 | } 223 | 224 | size_t 225 | Tex3DS_GetNumSubTextures(const Tex3DS_Texture texture) 226 | { 227 | return texture->numSubTextures; 228 | } 229 | 230 | const Tex3DS_SubTexture* 231 | Tex3DS_GetSubTexture(const Tex3DS_Texture texture, size_t index) 232 | { 233 | if (index < texture->numSubTextures) 234 | return &texture->subTextures[index]; 235 | return NULL; 236 | } 237 | 238 | void Tex3DS_TextureFree(Tex3DS_Texture texture) 239 | { 240 | free(texture); 241 | } 242 | -------------------------------------------------------------------------------- /source/texture.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | #include 3 | 4 | // Return bits per pixel 5 | static inline size_t fmtSize(GPU_TEXCOLOR fmt) 6 | { 7 | switch (fmt) 8 | { 9 | case GPU_RGBA8: 10 | return 32; 11 | case GPU_RGB8: 12 | return 24; 13 | case GPU_RGBA5551: 14 | case GPU_RGB565: 15 | case GPU_RGBA4: 16 | case GPU_LA8: 17 | case GPU_HILO8: 18 | return 16; 19 | case GPU_L8: 20 | case GPU_A8: 21 | case GPU_LA4: 22 | case GPU_ETC1A4: 23 | return 8; 24 | case GPU_L4: 25 | case GPU_A4: 26 | case GPU_ETC1: 27 | return 4; 28 | default: 29 | return 0; 30 | } 31 | } 32 | 33 | static inline bool checkTexSize(u32 size) 34 | { 35 | if (size < 8 || size > 1024) 36 | return false; 37 | if (size & (size-1)) 38 | return false; 39 | return true; 40 | } 41 | 42 | static inline void allocFree(void* addr) 43 | { 44 | if (addrIsVRAM(addr)) 45 | vramFree(addr); 46 | else 47 | linearFree(addr); 48 | } 49 | 50 | static void C3Di_TexCubeDelete(C3D_TexCube* cube) 51 | { 52 | int i; 53 | for (i = 0; i < 6; i ++) 54 | { 55 | if (cube->data[i]) 56 | { 57 | allocFree(cube->data[i]); 58 | cube->data[i] = NULL; 59 | } 60 | } 61 | } 62 | 63 | bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p) 64 | { 65 | if (!checkTexSize(p.width) || !checkTexSize(p.height)) return false; 66 | 67 | bool isCube = typeIsCube(p.type); 68 | if (isCube && !cube) return false; 69 | 70 | u32 size = fmtSize(p.format); 71 | if (!size) return false; 72 | size *= (u32)p.width * p.height / 8; 73 | u32 total_size = C3D_TexCalcTotalSize(size, p.maxLevel); 74 | 75 | if (!isCube) 76 | { 77 | tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); 78 | if (!tex->data) return false; 79 | } else 80 | { 81 | memset(cube, 0, sizeof(*cube)); 82 | int i; 83 | for (i = 0; i < 6; i ++) 84 | { 85 | cube->data[i] = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); 86 | if (!cube->data[i] || 87 | (i>0 && (((u32)cube->data[0] ^ (u32)cube->data[i])>>(3+22)))) // Check upper 6bits match with first face 88 | { 89 | C3Di_TexCubeDelete(cube); 90 | return false; 91 | } 92 | } 93 | tex->cube = cube; 94 | } 95 | 96 | tex->width = p.width; 97 | tex->height = p.height; 98 | tex->param = GPU_TEXTURE_MODE(p.type); 99 | if (p.format == GPU_ETC1) 100 | tex->param |= GPU_TEXTURE_ETC1_PARAM; 101 | if (p.type == GPU_TEX_SHADOW_2D || p.type == GPU_TEX_SHADOW_CUBE) 102 | tex->param |= GPU_TEXTURE_SHADOW_PARAM; 103 | tex->fmt = p.format; 104 | tex->size = size; 105 | tex->border = 0; 106 | tex->lodBias = 0; 107 | tex->maxLevel = p.maxLevel; 108 | tex->minLevel = 0; 109 | return true; 110 | } 111 | 112 | void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level) 113 | { 114 | u32 size = 0; 115 | void* out = C3D_TexGetImagePtr(tex, 116 | C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face], 117 | level, &size); 118 | 119 | if (!addrIsVRAM(out)) 120 | memcpy(out, data, size); 121 | else 122 | C3D_SyncTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8); 123 | } 124 | 125 | static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4]) 126 | { 127 | u32 i, j; 128 | for (i = 0; i < 64; i ++) 129 | { 130 | const u32* a = src[i>>4] + (i<<2 & 0x3F); 131 | u32 dest = 0; 132 | for (j = 0; j < 32; j += 8) 133 | { 134 | u32 val = (((a[0]>>j)&0xFF)+((a[1]>>j)&0xFF)+((a[2]>>j)&0xFF)+((a[3]>>j)&0xFF))>>2; 135 | dest |= val<>4] + 3*(i<<2 & 0x3F); 147 | for (j = 0; j < 3; j ++) 148 | { 149 | *dst++ = ((u32)a[0] + a[3] + a[6] + a[9])>>2; 150 | a++; 151 | } 152 | } 153 | } 154 | 155 | void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face) 156 | { 157 | int fmt = tex->fmt; 158 | size_t block_size = (8*8*fmtSize(fmt))/8; 159 | 160 | /* 161 | const u32 transfer_flags = 162 | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY) | BIT(5) | 163 | GX_TRANSFER_IN_FORMAT(tex->fmt) | GX_TRANSFER_OUT_FORMAT(tex->fmt); 164 | */ 165 | 166 | void* src = C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face]; 167 | if (addrIsVRAM(src)) 168 | return; // CPU can't write to VRAM 169 | 170 | int i; 171 | u32 level_size = tex->size; 172 | u32 src_width = tex->width; 173 | u32 src_height = tex->height; 174 | for (i = 0; i < tex->maxLevel; i ++) 175 | { 176 | void* dst = (u8*)src + level_size; 177 | u32 dst_width = src_width>>1; 178 | u32 dst_height = src_height>>1; 179 | 180 | /* Doesn't work due to size restriction bullshit 181 | C3D_SyncDisplayTransfer( 182 | (u32*)src, GX_BUFFER_DIM(src_width,src_height), 183 | (u32*)dst, GX_BUFFER_DIM(dst_width,dst_height), 184 | transfer_flags); 185 | */ 186 | 187 | u32 i,j; 188 | u32 src_stride = src_width/8; 189 | u32 dst_stride = dst_width/8; 190 | for (j = 0; j < (dst_height/8); j ++) 191 | { 192 | for (i = 0; i < dst_stride; i ++) 193 | { 194 | void* dst_block = (u8*)dst + block_size*(i + j*dst_stride); 195 | const void* src_blocks[4] = 196 | { 197 | (u8*)src + block_size*(2*i+0 + (2*j+0)*src_stride), 198 | (u8*)src + block_size*(2*i+1 + (2*j+0)*src_stride), 199 | (u8*)src + block_size*(2*i+0 + (2*j+1)*src_stride), 200 | (u8*)src + block_size*(2*i+1 + (2*j+1)*src_stride), 201 | }; 202 | switch (fmt) 203 | { 204 | case GPU_RGBA8: 205 | C3Di_DownscaleRGBA8(dst_block, (const u32**)src_blocks); 206 | break; 207 | case GPU_RGB8: 208 | C3Di_DownscaleRGB8(dst_block, (const u8**)src_blocks); 209 | default: 210 | break; 211 | } 212 | } 213 | } 214 | 215 | level_size >>= 2; 216 | src = dst; 217 | src_width = dst_width; 218 | src_height = dst_height; 219 | } 220 | } 221 | 222 | void C3D_TexBind(int unitId, C3D_Tex* tex) 223 | { 224 | C3D_Context* ctx = C3Di_GetContext(); 225 | 226 | if (!(ctx->flags & C3DiF_Active)) 227 | return; 228 | 229 | if (unitId > 0 && C3D_TexGetType(tex) != GPU_TEX_2D) 230 | return; 231 | 232 | ctx->flags |= C3DiF_Tex(unitId); 233 | ctx->tex[unitId] = tex; 234 | } 235 | 236 | void C3D_TexFlush(C3D_Tex* tex) 237 | { 238 | if (!addrIsVRAM(tex->data)) 239 | GSPGPU_FlushDataCache(tex->data, C3D_TexCalcTotalSize(tex->size, tex->maxLevel)); 240 | } 241 | 242 | void C3D_TexDelete(C3D_Tex* tex) 243 | { 244 | if (C3Di_TexIs2D(tex)) 245 | allocFree(tex->data); 246 | else 247 | C3Di_TexCubeDelete(tex->cube); 248 | } 249 | 250 | void C3D_TexShadowParams(bool perspective, float bias) 251 | { 252 | C3D_Context* ctx = C3Di_GetContext(); 253 | 254 | if (!(ctx->flags & C3DiF_Active)) 255 | return; 256 | 257 | u32 iBias = (u32)(fabs(bias) * BIT(24)); 258 | if (iBias >= BIT(24)) 259 | iBias = BIT(24)-1; 260 | 261 | ctx->texShadow = (iBias &~ 1) | (perspective ? 0 : 1); 262 | ctx->flags |= C3DiF_TexStatus; 263 | } 264 | 265 | void C3Di_SetTex(int unit, C3D_Tex* tex) 266 | { 267 | u32 reg[10]; 268 | u32 regcount = 5; 269 | reg[0] = tex->border; 270 | reg[1] = tex->dim; 271 | reg[2] = tex->param; 272 | reg[3] = tex->lodParam; 273 | if (C3Di_TexIs2D(tex)) 274 | reg[4] = osConvertVirtToPhys(tex->data) >> 3; 275 | else 276 | { 277 | int i; 278 | C3D_TexCube* cube = tex->cube; 279 | regcount = 10; 280 | reg[4] = osConvertVirtToPhys(cube->data[0]) >> 3; 281 | for (i = 1; i < 6; i ++) 282 | reg[4+i] = (osConvertVirtToPhys(cube->data[i]) >> 3) & 0x3FFFFF; 283 | } 284 | 285 | switch (unit) 286 | { 287 | case 0: 288 | GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, regcount); 289 | GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt); 290 | break; 291 | case 1: 292 | GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5); 293 | GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt); 294 | break; 295 | case 2: 296 | GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5); 297 | GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt); 298 | break; 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /source/lightenv.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | 3 | static void C3Di_LightEnvMtlBlend(C3D_LightEnv* env) 4 | { 5 | int i; 6 | C3D_Material* mtl = &env->material; 7 | u32 color = 0; 8 | for (i = 0; i < 3; i ++) 9 | { 10 | int v = 255*(mtl->emission[i] + mtl->ambient[i]*env->ambient[i]); 11 | if (v < 0) v = 0; 12 | else if (v > 255) v = 255; 13 | color |= v << (i*10); 14 | } 15 | env->conf.ambient = color; 16 | } 17 | 18 | static void C3Di_LightLutUpload(u32 config, C3D_LightLut* lut) 19 | { 20 | int i; 21 | GPUCMD_AddWrite(GPUREG_LIGHTING_LUT_INDEX, config); 22 | for (i = 0; i < 256; i += 8) 23 | GPUCMD_AddWrites(GPUREG_LIGHTING_LUT_DATA0, &lut->data[i], 8); 24 | } 25 | 26 | static void C3Di_LightEnvSelectLayer(C3D_LightEnv* env) 27 | { 28 | static const u8 layer_enabled[] = 29 | { 30 | BIT(GPU_LUT_D0) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_SP) | BIT(GPU_LUT_DA), 31 | BIT(GPU_LUT_FR) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_SP) | BIT(GPU_LUT_DA), 32 | BIT(GPU_LUT_D0) | BIT(GPU_LUT_D1) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_DA), 33 | BIT(GPU_LUT_D0) | BIT(GPU_LUT_D1) | BIT(GPU_LUT_FR) | BIT(GPU_LUT_DA), 34 | 0xFF &~ BIT(GPU_LUT_FR), 35 | 0xFF &~ BIT(GPU_LUT_D1), 36 | 0xFF &~ (BIT(GPU_LUT_RB) | BIT(GPU_LUT_RG)), 37 | }; 38 | 39 | u32 reg = ~env->conf.config[1]; 40 | if (reg & (0xFF<< 8)) reg |= GPU_LC1_LUTBIT(GPU_LUT_SP); 41 | if (reg & (0xFF<<24)) reg |= GPU_LC1_LUTBIT(GPU_LUT_DA); 42 | reg = (reg >> 16) & 0xFF; 43 | 44 | int i = 7; 45 | if (!(env->flags & C3DF_LightEnv_IsCP_Any)) 46 | for (i = 0; i < 7; i ++) 47 | if ((layer_enabled[i] & reg) == reg) // Check if the layer supports all LUTs we need 48 | break; 49 | env->conf.config[0] = (env->conf.config[0] &~ (0xF<<4)) | (GPU_LIGHT_ENV_LAYER_CONFIG(i)<<4); 50 | } 51 | 52 | void C3Di_LightEnvUpdate(C3D_LightEnv* env) 53 | { 54 | int i; 55 | C3D_LightEnvConf* conf = &env->conf; 56 | 57 | if (env->flags & C3DF_LightEnv_LCDirty) 58 | { 59 | conf->numLights = 0; 60 | conf->permutation = 0; 61 | for (i = 0; i < 8; i ++) 62 | { 63 | C3D_Light* light = env->lights[i]; 64 | if (!light) continue; 65 | if (!(light->flags & C3DF_Light_Enabled)) continue; 66 | conf->permutation |= GPU_LIGHTPERM(conf->numLights++, i); 67 | } 68 | if (conf->numLights > 0) conf->numLights --; 69 | env->flags &= ~C3DF_LightEnv_LCDirty; 70 | env->flags |= C3DF_LightEnv_Dirty; 71 | } 72 | 73 | if (env->flags & C3DF_LightEnv_MtlDirty) 74 | { 75 | C3Di_LightEnvMtlBlend(env); 76 | env->flags &= ~C3DF_LightEnv_MtlDirty; 77 | env->flags |= C3DF_LightEnv_Dirty; 78 | } 79 | 80 | if (env->flags & C3DF_LightEnv_Dirty) 81 | { 82 | C3Di_LightEnvSelectLayer(env); 83 | GPUCMD_AddWrite(GPUREG_LIGHTING_AMBIENT, conf->ambient); 84 | GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_NUM_LIGHTS, (u32*)&conf->numLights, 3); 85 | GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_LUTINPUT_ABS, (u32*)&conf->lutInput, 3); 86 | GPUCMD_AddWrite(GPUREG_LIGHTING_LIGHT_PERMUTATION, conf->permutation); 87 | env->flags &= ~C3DF_LightEnv_Dirty; 88 | } 89 | 90 | if (env->flags & C3DF_LightEnv_LutDirtyAll) 91 | { 92 | for (i = 0; i < 6; i ++) 93 | { 94 | static const u8 lutIds[] = { 0, 1, 3, 4, 5, 6 }; 95 | if (!(env->flags & C3DF_LightEnv_LutDirty(i))) continue; 96 | C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_COMMON, (u32)lutIds[i], 0), env->luts[i]); 97 | } 98 | 99 | env->flags &= ~C3DF_LightEnv_LutDirtyAll; 100 | } 101 | 102 | for (i = 0; i < 8; i ++) 103 | { 104 | C3D_Light* light = env->lights[i]; 105 | if (!light) continue; 106 | 107 | if (light->flags & C3DF_Light_MatDirty) 108 | { 109 | C3Di_LightMtlBlend(light); 110 | light->flags &= ~C3DF_Light_MatDirty; 111 | light->flags |= C3DF_Light_Dirty; 112 | } 113 | 114 | if (light->flags & C3DF_Light_Dirty) 115 | { 116 | GPUCMD_AddIncrementalWrites(GPUREG_LIGHT0_SPECULAR0 + i*0x10, (u32*)&light->conf, 12); 117 | light->flags &= ~C3DF_Light_Dirty; 118 | } 119 | 120 | if (light->flags & C3DF_Light_SPDirty) 121 | { 122 | C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_SP, i, 0), light->lut_SP); 123 | light->flags &= ~C3DF_Light_SPDirty; 124 | } 125 | 126 | if (light->flags & C3DF_Light_DADirty) 127 | { 128 | C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_DA, i, 0), light->lut_DA); 129 | light->flags &= ~C3DF_Light_DADirty; 130 | } 131 | } 132 | } 133 | 134 | void C3Di_LightEnvDirty(C3D_LightEnv* env) 135 | { 136 | env->flags |= C3DF_LightEnv_Dirty; 137 | int i; 138 | for (i = 0; i < 6; i ++) 139 | if (env->luts[i]) 140 | env->flags |= C3DF_LightEnv_LutDirty(i); 141 | for (i = 0; i < 8; i ++) 142 | { 143 | C3D_Light* light = env->lights[i]; 144 | if (!light) continue; 145 | 146 | light->flags |= C3DF_Light_Dirty; 147 | if (light->lut_SP) 148 | light->flags |= C3DF_Light_SPDirty; 149 | if (light->lut_DA) 150 | light->flags |= C3DF_Light_DADirty; 151 | } 152 | } 153 | 154 | void C3D_LightEnvInit(C3D_LightEnv* env) 155 | { 156 | memset(env, 0, sizeof(*env)); 157 | env->flags = C3DF_LightEnv_Dirty; 158 | env->ambient[0] = env->ambient[1] = env->ambient[2] = 1.0f; 159 | env->conf.config[0] = (4<<8) | BIT(27) | BIT(31); 160 | env->conf.config[1] = ~0; 161 | env->conf.lutInput.select = GPU_LIGHTLUTINPUT(GPU_LUT_SP, GPU_LUTINPUT_SP); 162 | env->conf.lutInput.abs = 0x2222222; 163 | } 164 | 165 | void C3D_LightEnvBind(C3D_LightEnv* env) 166 | { 167 | C3D_Context* ctx = C3Di_GetContext(); 168 | 169 | if (!(ctx->flags & C3DiF_Active)) 170 | return; 171 | 172 | if (ctx->lightEnv == env) 173 | return; 174 | 175 | ctx->flags |= C3DiF_LightEnv; 176 | ctx->lightEnv = env; 177 | } 178 | 179 | void C3D_LightEnvMaterial(C3D_LightEnv* env, const C3D_Material* mtl) 180 | { 181 | int i; 182 | memcpy(&env->material, mtl, sizeof(*mtl)); 183 | env->flags |= C3DF_LightEnv_MtlDirty; 184 | for (i = 0; i < 8; i ++) 185 | { 186 | C3D_Light* light = env->lights[i]; 187 | if (light) light->flags |= C3DF_Light_MatDirty; 188 | } 189 | } 190 | 191 | void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b) 192 | { 193 | env->ambient[0] = b; 194 | env->ambient[1] = g; 195 | env->ambient[2] = r; 196 | env->flags |= C3DF_LightEnv_MtlDirty; 197 | } 198 | 199 | void C3D_LightEnvLut(C3D_LightEnv* env, GPU_LIGHTLUTID lutId, GPU_LIGHTLUTINPUT input, bool negative, C3D_LightLut* lut) 200 | { 201 | static const s8 ids[] = { 0, 1, -1, 2, 3, 4, 5, -1 }; 202 | int id = ids[lutId]; 203 | if (id >= 0) 204 | { 205 | env->luts[id] = lut; 206 | if (lut) 207 | { 208 | env->conf.config[1] &= ~GPU_LC1_LUTBIT(lutId); 209 | env->flags |= C3DF_LightEnv_LutDirty(id); 210 | } else 211 | { 212 | env->conf.config[1] |= GPU_LC1_LUTBIT(lutId); 213 | env->luts[id] = NULL; 214 | } 215 | } 216 | 217 | env->conf.lutInput.select &= ~GPU_LIGHTLUTINPUT(lutId, 0xF); 218 | env->conf.lutInput.select |= GPU_LIGHTLUTINPUT(lutId, input); 219 | 220 | u32 absbit = 1 << (lutId*4 + 1); 221 | env->conf.lutInput.abs &= ~absbit; 222 | if (negative) 223 | env->conf.lutInput.abs |= absbit; 224 | 225 | env->flags |= C3DF_LightEnv_Dirty; 226 | if (input == GPU_LUTINPUT_CP) 227 | env->flags |= C3DF_LightEnv_IsCP(lutId); 228 | else 229 | env->flags &= ~C3DF_LightEnv_IsCP(lutId); 230 | } 231 | 232 | void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector) 233 | { 234 | env->conf.config[0] &= ~(3<<2); 235 | env->conf.config[0] |= (selector&3)<<2; 236 | env->flags |= C3DF_LightEnv_Dirty; 237 | } 238 | 239 | void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode) 240 | { 241 | env->conf.config[0] &= ~(3<<28); 242 | env->conf.config[0] |= (mode&3)<<28; 243 | env->flags |= C3DF_LightEnv_Dirty; 244 | } 245 | 246 | void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit) 247 | { 248 | env->conf.config[0] &= ~(3<<22); 249 | env->conf.config[0] |= (texUnit&3)<<22; 250 | env->flags |= C3DF_LightEnv_Dirty; 251 | } 252 | 253 | void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool usez) { 254 | if (usez) 255 | env->conf.config[0] |= BIT(30); 256 | else 257 | env->conf.config[0] &= ~BIT(30); 258 | env->flags |= C3DF_LightEnv_Dirty; 259 | } 260 | 261 | void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode) 262 | { 263 | mode &= 0xF<<16; 264 | if (mode & (GPU_SHADOW_PRIMARY | GPU_SHADOW_SECONDARY | GPU_SHADOW_ALPHA)) 265 | mode |= BIT(0); 266 | env->conf.config[0] &= ~((0xF<<16) | BIT(0)); 267 | env->conf.config[0] |= mode; 268 | env->flags |= C3DF_LightEnv_Dirty; 269 | } 270 | 271 | void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit) 272 | { 273 | env->conf.config[0] &= ~(3<<24); 274 | env->conf.config[0] |= (texUnit&3)<<24; 275 | env->flags |= C3DF_LightEnv_Dirty; 276 | } 277 | 278 | void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp) 279 | { 280 | if (clamp) 281 | env->conf.config[0] |= BIT(27); 282 | else 283 | env->conf.config[0] &= ~BIT(27); 284 | env->flags |= C3DF_LightEnv_Dirty; 285 | } 286 | -------------------------------------------------------------------------------- /test/3ds/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | 3DSTEX := 3dstex 10 | TOPDIR ?= $(CURDIR) 11 | include $(DEVKITARM)/3ds_rules 12 | 13 | #--------------------------------------------------------------------------------- 14 | # TARGET is the name of the output 15 | # BUILD is the directory where object files & intermediate files will be placed 16 | # SOURCES is a list of directories containing source code 17 | # DATA is a list of directories containing data files 18 | # INCLUDES is a list of directories containing header files 19 | # 20 | # NO_SMDH: if set to anything, no SMDH file is generated. 21 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 22 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 23 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 24 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 25 | # ICON is the filename of the icon (.png), relative to the project folder. 26 | # If not set, it attempts to use one of the following (in this order): 27 | # - .png 28 | # - icon.png 29 | # - /default_icon.png 30 | #--------------------------------------------------------------------------------- 31 | TARGET := citro3d_test 32 | BUILD := build 33 | SOURCES := source 34 | GRAPHICS := gfx 35 | DATA := data 36 | INCLUDES := include 37 | ROMFS := romfs 38 | 39 | APP_TITLE := citro3d test 40 | APP_DESCRIPTION := v1.0 41 | APP_AUTHOR := mtheall 42 | ICON := 43 | 44 | #--------------------------------------------------------------------------------- 45 | # options for code generation 46 | #--------------------------------------------------------------------------------- 47 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 48 | 49 | CFLAGS := -g -Wall -O3 -mword-relocations \ 50 | -ffunction-sections \ 51 | $(ARCH) 52 | 53 | CFLAGS += $(INCLUDE) -D__3DS__ 54 | 55 | CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11 56 | 57 | ASFLAGS := -g $(ARCH) 58 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(TARGET).map 59 | 60 | LIBS := -lcitro3d -lctru 61 | 62 | #--------------------------------------------------------------------------------- 63 | # list of directories containing libraries, this must be the top level containing 64 | # include and lib 65 | #--------------------------------------------------------------------------------- 66 | LIBDIRS := $(CTRULIB) #$(CURDIR)/../.. 67 | 68 | 69 | #--------------------------------------------------------------------------------- 70 | # no real need to edit anything past this point unless you need to add additional 71 | # rules for different file extensions 72 | #--------------------------------------------------------------------------------- 73 | ifneq ($(BUILD),$(notdir $(CURDIR))) 74 | #--------------------------------------------------------------------------------- 75 | 76 | export OUTPUT := $(CURDIR)/$(TARGET) 77 | export TOPDIR := $(CURDIR) 78 | 79 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 80 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 82 | 83 | export DEPSDIR := $(CURDIR)/$(BUILD) 84 | 85 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 86 | CXXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 87 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 88 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 89 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 90 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 91 | 92 | #--------------------------------------------------------------------------------- 93 | # use CXX for linking C++ projects, CC for standard C 94 | #--------------------------------------------------------------------------------- 95 | ifeq ($(strip $(CXXFILES)),) 96 | export LD := $(CC) 97 | else 98 | export LD := $(CXX) 99 | endif 100 | #--------------------------------------------------------------------------------- 101 | 102 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 103 | $(PICAFILES:.v.pica=.shbin.o) \ 104 | $(CXXFILES:.cpp=.o) \ 105 | $(CFILES:.c=.o) \ 106 | $(SFILES:.s=.o) 107 | 108 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 109 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 110 | -I$(CURDIR)/$(BUILD) 111 | 112 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 113 | 114 | PNGROMFS := $(addprefix romfs/,$(PNGFILES:.png=.bin)) 115 | 116 | ifeq ($(strip $(ICON)),) 117 | icons := $(wildcard *.png) 118 | ifneq (,$(findstring $(TARGET).png,$(icons))) 119 | export APP_ICON := $(TOPDIR)/$(TARGET).png 120 | else 121 | ifneq (,$(findstring icon.png,$(icons))) 122 | export APP_ICON := $(TOPDIR)/icon.png 123 | endif 124 | endif 125 | else 126 | export APP_ICON := $(TOPDIR)/$(ICON) 127 | endif 128 | 129 | ifeq ($(strip $(NO_SMDH)),) 130 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 131 | endif 132 | 133 | ifneq ($(ROMFS),) 134 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 135 | endif 136 | 137 | .PHONY: $(BUILD) clean all 138 | 139 | #--------------------------------------------------------------------------------- 140 | all: $(BUILD) 141 | 142 | $(BUILD): $(PNGROMFS) 143 | @[ -d $@ ] || mkdir -p $@ 144 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 145 | 146 | #--------------------------------------------------------------------------------- 147 | clean: 148 | @echo clean ... 149 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(PNGROMFS) 150 | 151 | $(ROMFS)/%.rgba.bin: %.rgba.png 152 | @$(3DSTEX) -o $@ --rgba $< 153 | 154 | $(ROMFS)/%.rgb.bin: %.rgb.png 155 | @$(3DSTEX) -o $@ --rgb $< 156 | 157 | $(ROMFS)/%.rgba5551.bin: %.rgba5551.png 158 | @$(3DSTEX) -o $@ --rgba5551 $< 159 | 160 | $(ROMFS)/%.rgb565.bin: %.rgb565.png 161 | @$(3DSTEX) -o $@ --rgb565 $< 162 | 163 | $(ROMFS)/%.rgba4.bin: %.rgba4.png 164 | @$(3DSTEX) -o $@ --rgba4 $< 165 | 166 | $(ROMFS)/%.la.bin: %.la.png 167 | @$(3DSTEX) -o $@ --la $< 168 | 169 | $(ROMFS)/%.hilo.bin: %.hilo.png 170 | @$(3DSTEX) -o $@ --hilo $< 171 | 172 | $(ROMFS)/%.l.bin: %.l.png 173 | @$(3DSTEX) -o $@ --l $< 174 | 175 | $(ROMFS)/%.a.bin: %.a.png 176 | @$(3DSTEX) -o $@ --a $< 177 | 178 | $(ROMFS)/%.la4.bin: %.la4.png 179 | @$(3DSTEX) -o $@ --la4 $< 180 | 181 | $(ROMFS)/%.l4.bin: %.l4.png 182 | @$(3DSTEX) -o $@ --l4 $< 183 | 184 | $(ROMFS)/%.a4.bin: %.a4.png 185 | @$(3DSTEX) -o $@ --a4 $< 186 | 187 | $(ROMFS)/%.etc1.bin: %.etc1.png 188 | @$(3DSTEX) -o $@ --etc1 $< 189 | 190 | $(ROMFS)/%.etc1a4.bin: %.etc1a4.png 191 | @$(3DSTEX) -o $@ --etc1a4 $< 192 | 193 | $(ROMFS)/%.bin: %.png 194 | @$(3DSTEX) -o $@ $< 195 | 196 | #--------------------------------------------------------------------------------- 197 | else 198 | 199 | DEPENDS := $(OFILES:.o=.d) 200 | 201 | #--------------------------------------------------------------------------------- 202 | # main targets 203 | #--------------------------------------------------------------------------------- 204 | ifeq ($(strip $(NO_SMDH)),) 205 | .PHONY: all 206 | all : $(OUTPUT).3dsx $(OUTPUT).smdh 207 | $(OUTPUT).smdh : $(TOPDIR)/Makefile 208 | $(OUTPUT).3dsx: $(OUTPUT).smdh 209 | endif 210 | $(OUTPUT).3dsx: $(OUTPUT).elf 211 | $(OUTPUT).elf: $(OFILES) 212 | 213 | #--------------------------------------------------------------------------------- 214 | # you need a rule like this for each extension you use as binary data 215 | #--------------------------------------------------------------------------------- 216 | %.bin.o: %.bin 217 | #--------------------------------------------------------------------------------- 218 | @echo $(notdir $<) 219 | @$(bin2o) 220 | 221 | #--------------------------------------------------------------------------------- 222 | # rules for assembling GPU shaders 223 | #--------------------------------------------------------------------------------- 224 | define shader-as 225 | $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) 226 | picasso -o $(CURBIN) $1 227 | bin2s $(CURBIN) | $(AS) -o $@ 228 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 229 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 230 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 231 | endef 232 | 233 | %.shbin.o : %.v.pica %.g.pica 234 | @echo $(notdir $^) 235 | @$(call shader-as,$^) 236 | 237 | %.shbin.o : %.v.pica 238 | @echo $(notdir $<) 239 | @$(call shader-as,$<) 240 | 241 | %.shbin.o : %.shlist 242 | @echo $(notdir $<) 243 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) 244 | 245 | -include $(DEPENDS) 246 | 247 | #--------------------------------------------------------------------------------------- 248 | endif 249 | #--------------------------------------------------------------------------------------- 250 | -------------------------------------------------------------------------------- /source/base.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | C3D_Context __C3D_Context; 8 | 9 | static aptHookCookie hookCookie; 10 | 11 | __attribute__((weak)) void C3Di_LightEnvUpdate(C3D_LightEnv* env) 12 | { 13 | (void)env; 14 | } 15 | 16 | __attribute__((weak)) void C3Di_LightEnvDirty(C3D_LightEnv* env) 17 | { 18 | (void)env; 19 | } 20 | 21 | __attribute__((weak)) void C3Di_ProcTexUpdate(C3D_Context* ctx) 22 | { 23 | (void)ctx; 24 | } 25 | 26 | __attribute__((weak)) void C3Di_ProcTexDirty(C3D_Context* ctx) 27 | { 28 | (void)ctx; 29 | } 30 | 31 | __attribute__((weak)) void C3Di_GasUpdate(C3D_Context* ctx) 32 | { 33 | (void)ctx; 34 | } 35 | 36 | static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) 37 | { 38 | C3D_Context* ctx = C3Di_GetContext(); 39 | 40 | switch (hookType) 41 | { 42 | case APTHOOK_ONSUSPEND: 43 | { 44 | C3Di_RenderQueueWaitDone(); 45 | C3Di_RenderQueueDisableVBlank(); 46 | break; 47 | } 48 | case APTHOOK_ONRESTORE: 49 | { 50 | C3Di_RenderQueueEnableVBlank(); 51 | ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_FrameBuf 52 | | C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode 53 | | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv | C3DiF_Gas; 54 | 55 | C3Di_DirtyUniforms(GPU_VERTEX_SHADER); 56 | C3Di_DirtyUniforms(GPU_GEOMETRY_SHADER); 57 | 58 | ctx->fixedAttribDirty |= ctx->fixedAttribEverDirty; 59 | ctx->gasFlags |= C3DiG_BeginAcc | C3DiG_AccStage | C3DiG_RenderStage; 60 | 61 | C3D_LightEnv* env = ctx->lightEnv; 62 | if (ctx->fogLut) 63 | ctx->flags |= C3DiF_FogLut; 64 | if (ctx->gasLut) 65 | ctx->flags |= C3DiF_GasLut; 66 | if (env) 67 | C3Di_LightEnvDirty(env); 68 | C3Di_ProcTexDirty(ctx); 69 | break; 70 | } 71 | default: 72 | break; 73 | } 74 | } 75 | 76 | bool C3D_Init(size_t cmdBufSize) 77 | { 78 | int i; 79 | C3D_Context* ctx = C3Di_GetContext(); 80 | 81 | if (ctx->flags & C3DiF_Active) 82 | return false; 83 | 84 | cmdBufSize = (cmdBufSize + 0xF) &~ 0xF; // 0x10-byte align 85 | ctx->cmdBufSize = cmdBufSize/4; 86 | ctx->cmdBuf = (u32*)linearAlloc(cmdBufSize); 87 | ctx->cmdBufUsage = 0; 88 | if (!ctx->cmdBuf) 89 | return false; 90 | 91 | ctx->gxQueue.maxEntries = 32; 92 | ctx->gxQueue.entries = (gxCmdEntry_s*)malloc(ctx->gxQueue.maxEntries*sizeof(gxCmdEntry_s)); 93 | if (!ctx->gxQueue.entries) 94 | { 95 | linearFree(ctx->cmdBuf); 96 | return false; 97 | } 98 | 99 | ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexStatus | C3DiF_TexAll; 100 | 101 | // TODO: replace with direct struct access 102 | C3D_DepthMap(true, -1.0f, 0.0f); 103 | C3D_CullFace(GPU_CULL_BACK_CCW); 104 | C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); 105 | C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); 106 | C3D_BlendingColor(0); 107 | C3D_EarlyDepthTest(false, GPU_EARLYDEPTH_GREATER, 0); 108 | C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); 109 | C3D_AlphaTest(false, GPU_ALWAYS, 0x00); 110 | C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); 111 | C3D_FragOpMode(GPU_FRAGOPMODE_GL); 112 | C3D_FragOpShadow(0.0, 1.0); 113 | 114 | ctx->texConfig = BIT(12); 115 | ctx->texShadow = BIT(0); 116 | ctx->texEnvBuf = 0; 117 | ctx->texEnvBufClr = 0xFFFFFFFF; 118 | ctx->fogClr = 0; 119 | ctx->fogLut = NULL; 120 | 121 | for (i = 0; i < 3; i ++) 122 | ctx->tex[i] = NULL; 123 | 124 | for (i = 0; i < 6; i ++) 125 | C3D_TexEnvInit(&ctx->texEnv[i]); 126 | 127 | ctx->fixedAttribDirty = 0; 128 | ctx->fixedAttribEverDirty = 0; 129 | 130 | C3Di_RenderQueueInit(); 131 | aptHook(&hookCookie, C3Di_AptEventHook, NULL); 132 | 133 | return true; 134 | } 135 | 136 | void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h) 137 | { 138 | C3D_Context* ctx = C3Di_GetContext(); 139 | ctx->flags |= C3DiF_Viewport | C3DiF_Scissor; 140 | ctx->viewport[0] = f32tof24(w / 2.0f); 141 | ctx->viewport[1] = f32tof31(2.0f / w) << 1; 142 | ctx->viewport[2] = f32tof24(h / 2.0f); 143 | ctx->viewport[3] = f32tof31(2.0f / h) << 1; 144 | ctx->viewport[4] = (y << 16) | (x & 0xFFFF); 145 | ctx->scissor[0] = GPU_SCISSOR_DISABLE; 146 | } 147 | 148 | void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom) 149 | { 150 | C3D_Context* ctx = C3Di_GetContext(); 151 | ctx->flags |= C3DiF_Scissor; 152 | ctx->scissor[0] = mode; 153 | if (mode == GPU_SCISSOR_DISABLE) return; 154 | ctx->scissor[1] = (top << 16) | (left & 0xFFFF); 155 | ctx->scissor[2] = ((bottom-1) << 16) | ((right-1) & 0xFFFF); 156 | } 157 | 158 | void C3Di_UpdateContext(void) 159 | { 160 | int i; 161 | C3D_Context* ctx = C3Di_GetContext(); 162 | 163 | if (ctx->flags & C3DiF_FrameBuf) 164 | { 165 | ctx->flags &= ~C3DiF_FrameBuf; 166 | if (ctx->flags & C3DiF_DrawUsed) 167 | { 168 | ctx->flags &= ~C3DiF_DrawUsed; 169 | GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); 170 | GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); 171 | } 172 | C3Di_FrameBufBind(&ctx->fb); 173 | } 174 | 175 | if (ctx->flags & C3DiF_Viewport) 176 | { 177 | ctx->flags &= ~C3DiF_Viewport; 178 | GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, ctx->viewport, 4); 179 | GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, ctx->viewport[4]); 180 | } 181 | 182 | if (ctx->flags & C3DiF_Scissor) 183 | { 184 | ctx->flags &= ~C3DiF_Scissor; 185 | GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, ctx->scissor, 3); 186 | } 187 | 188 | if (ctx->flags & C3DiF_Program) 189 | { 190 | shaderProgramConfigure(ctx->program, (ctx->flags & C3DiF_VshCode) != 0, (ctx->flags & C3DiF_GshCode) != 0); 191 | ctx->flags &= ~(C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode); 192 | } 193 | 194 | if (ctx->flags & C3DiF_AttrInfo) 195 | { 196 | ctx->flags &= ~C3DiF_AttrInfo; 197 | C3Di_AttrInfoBind(&ctx->attrInfo); 198 | } 199 | 200 | if (ctx->flags & C3DiF_BufInfo) 201 | { 202 | ctx->flags &= ~C3DiF_BufInfo; 203 | C3Di_BufInfoBind(&ctx->bufInfo); 204 | } 205 | 206 | if (ctx->flags & C3DiF_Effect) 207 | { 208 | ctx->flags &= ~C3DiF_Effect; 209 | C3Di_EffectBind(&ctx->effect); 210 | } 211 | 212 | if (ctx->flags & C3DiF_TexAll) 213 | { 214 | u32 units = 0; 215 | for (i = 0; i < 3; i ++) 216 | { 217 | if (ctx->tex[i]) 218 | { 219 | units |= BIT(i); 220 | if (ctx->flags & C3DiF_Tex(i)) 221 | C3Di_SetTex(i, ctx->tex[i]); 222 | } 223 | } 224 | 225 | // Enable texture units and clear texture cache 226 | ctx->texConfig &= ~7; 227 | ctx->texConfig |= units | BIT(16); 228 | ctx->flags &= ~C3DiF_TexAll; 229 | ctx->flags |= C3DiF_TexStatus; 230 | } 231 | 232 | if (ctx->flags & C3DiF_TexStatus) 233 | { 234 | ctx->flags &= ~C3DiF_TexStatus; 235 | GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0xB, ctx->texConfig); 236 | // Clear texture cache if requested *after* configuring texture units 237 | if (ctx->texConfig & BIT(16)) 238 | { 239 | ctx->texConfig &= ~BIT(16); 240 | GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0x4, BIT(16)); 241 | } 242 | GPUCMD_AddWrite(GPUREG_TEXUNIT0_SHADOW, ctx->texShadow); 243 | } 244 | 245 | if (ctx->flags & (C3DiF_ProcTex | C3DiF_ProcTexColorLut | C3DiF_ProcTexLutAll)) 246 | C3Di_ProcTexUpdate(ctx); 247 | 248 | if (ctx->flags & C3DiF_TexEnvBuf) 249 | { 250 | ctx->flags &= ~C3DiF_TexEnvBuf; 251 | GPUCMD_AddMaskedWrite(GPUREG_TEXENV_UPDATE_BUFFER, 0x7, ctx->texEnvBuf); 252 | GPUCMD_AddWrite(GPUREG_TEXENV_BUFFER_COLOR, ctx->texEnvBufClr); 253 | GPUCMD_AddWrite(GPUREG_FOG_COLOR, ctx->fogClr); 254 | } 255 | 256 | if ((ctx->flags & C3DiF_FogLut) && (ctx->texEnvBuf&7) != GPU_NO_FOG) 257 | { 258 | ctx->flags &= ~C3DiF_FogLut; 259 | if (ctx->fogLut) 260 | { 261 | GPUCMD_AddWrite(GPUREG_FOG_LUT_INDEX, 0); 262 | GPUCMD_AddWrites(GPUREG_FOG_LUT_DATA0, ctx->fogLut->data, 128); 263 | } 264 | } 265 | 266 | if ((ctx->texEnvBuf&7) == GPU_GAS) 267 | C3Di_GasUpdate(ctx); 268 | 269 | if (ctx->flags & C3DiF_TexEnvAll) 270 | { 271 | for (i = 0; i < 6; i ++) 272 | { 273 | if (!(ctx->flags & C3DiF_TexEnv(i))) continue; 274 | C3Di_TexEnvBind(i, &ctx->texEnv[i]); 275 | } 276 | ctx->flags &= ~C3DiF_TexEnvAll; 277 | } 278 | 279 | C3D_LightEnv* env = ctx->lightEnv; 280 | 281 | if (ctx->flags & C3DiF_LightEnv) 282 | { 283 | u32 enable = env != NULL; 284 | GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE0, enable); 285 | GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE1, !enable); 286 | ctx->flags &= ~C3DiF_LightEnv; 287 | } 288 | 289 | if (env) 290 | C3Di_LightEnvUpdate(env); 291 | 292 | if (ctx->fixedAttribDirty) 293 | { 294 | for (i = 0; i < 12; i ++) 295 | { 296 | if (!(ctx->fixedAttribDirty & BIT(i))) continue; 297 | C3D_FVec* v = &ctx->fixedAttribs[i]; 298 | 299 | GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, i); 300 | C3D_ImmSendAttrib(v->x, v->y, v->z, v->w); 301 | } 302 | ctx->fixedAttribDirty = 0; 303 | } 304 | 305 | C3D_UpdateUniforms(GPU_VERTEX_SHADER); 306 | C3D_UpdateUniforms(GPU_GEOMETRY_SHADER); 307 | } 308 | 309 | bool C3Di_SplitFrame(u32** pBuf, u32* pSize) 310 | { 311 | C3D_Context* ctx = C3Di_GetContext(); 312 | 313 | if (!gpuCmdBufOffset) 314 | return false; // Nothing was drawn 315 | 316 | if (ctx->flags & C3DiF_DrawUsed) 317 | { 318 | ctx->flags &= ~C3DiF_DrawUsed; 319 | GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); 320 | GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); 321 | GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); 322 | } 323 | 324 | GPUCMD_Split(pBuf, pSize); 325 | u32 totalCmdBufSize = *pBuf + *pSize - ctx->cmdBuf; 326 | ctx->cmdBufUsage = (float)totalCmdBufSize / ctx->cmdBufSize; 327 | return true; 328 | } 329 | 330 | float C3D_GetCmdBufUsage(void) 331 | { 332 | return C3Di_GetContext()->cmdBufUsage; 333 | } 334 | 335 | void C3D_Fini(void) 336 | { 337 | C3D_Context* ctx = C3Di_GetContext(); 338 | 339 | if (!(ctx->flags & C3DiF_Active)) 340 | return; 341 | 342 | aptUnhook(&hookCookie); 343 | C3Di_RenderQueueExit(); 344 | free(ctx->gxQueue.entries); 345 | linearFree(ctx->cmdBuf); 346 | ctx->flags = 0; 347 | } 348 | 349 | void C3D_BindProgram(shaderProgram_s* program) 350 | { 351 | C3D_Context* ctx = C3Di_GetContext(); 352 | 353 | if (!(ctx->flags & C3DiF_Active)) 354 | return; 355 | 356 | shaderProgram_s* oldProg = ctx->program; 357 | shaderInstance_s* newGsh = program->geometryShader; 358 | if (oldProg != program) 359 | { 360 | ctx->program = program; 361 | ctx->flags |= C3DiF_Program | C3DiF_AttrInfo; 362 | 363 | if (!oldProg) 364 | ctx->flags |= C3DiF_VshCode | C3DiF_GshCode; 365 | else 366 | { 367 | shaderInstance_s* oldGsh = oldProg->geometryShader; 368 | 369 | DVLP_s* oldProgV = oldProg->vertexShader->dvle->dvlp; 370 | DVLP_s* oldProgG = oldGsh ? oldGsh->dvle->dvlp : oldProgV; 371 | 372 | DVLP_s* newProgV = program->vertexShader->dvle->dvlp; 373 | DVLP_s* newProgG = newGsh ? newGsh->dvle->dvlp : newProgV; 374 | 375 | if (oldProgV != newProgV || (!newGsh && oldProgG != newProgG)) 376 | ctx->flags |= C3DiF_VshCode; 377 | if (oldProgG != newProgG || (newProgG==oldProgV && newProgG->codeSize >= 512)) 378 | ctx->flags |= C3DiF_GshCode; 379 | } 380 | } 381 | 382 | C3Di_LoadShaderUniforms(program->vertexShader); 383 | if (newGsh) 384 | C3Di_LoadShaderUniforms(newGsh); 385 | else 386 | C3Di_ClearShaderUniforms(GPU_GEOMETRY_SHADER); 387 | } 388 | 389 | C3D_FVec* C3D_FixedAttribGetWritePtr(int id) 390 | { 391 | if (id < 0 || id >= 12) 392 | return NULL; 393 | 394 | C3D_Context* ctx = C3Di_GetContext(); 395 | 396 | if (!(ctx->flags & C3DiF_Active)) 397 | return NULL; 398 | 399 | ctx->fixedAttribDirty |= BIT(id); 400 | ctx->fixedAttribEverDirty |= BIT(id); 401 | return &ctx->fixedAttribs[id]; 402 | } 403 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.8.11 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | DOXYFILE_ENCODING = UTF-8 7 | PROJECT_NAME = "citro3d" 8 | PROJECT_NUMBER = "1.2.0" 9 | PROJECT_BRIEF = 10 | PROJECT_LOGO = citro3d_logo.png 11 | OUTPUT_DIRECTORY = doc 12 | CREATE_SUBDIRS = NO 13 | ALLOW_UNICODE_NAMES = NO 14 | OUTPUT_LANGUAGE = English 15 | BRIEF_MEMBER_DESC = YES 16 | REPEAT_BRIEF = YES 17 | ABBREVIATE_BRIEF = 18 | ALWAYS_DETAILED_SEC = NO 19 | INLINE_INHERITED_MEMB = NO 20 | FULL_PATH_NAMES = YES 21 | STRIP_FROM_PATH = 22 | STRIP_FROM_INC_PATH = 23 | SHORT_NAMES = NO 24 | JAVADOC_AUTOBRIEF = NO 25 | QT_AUTOBRIEF = NO 26 | MULTILINE_CPP_IS_BRIEF = NO 27 | INHERIT_DOCS = YES 28 | SEPARATE_MEMBER_PAGES = NO 29 | TAB_SIZE = 4 30 | ALIASES = 31 | TCL_SUBST = 32 | OPTIMIZE_OUTPUT_FOR_C = YES 33 | OPTIMIZE_OUTPUT_JAVA = NO 34 | OPTIMIZE_FOR_FORTRAN = NO 35 | OPTIMIZE_OUTPUT_VHDL = NO 36 | EXTENSION_MAPPING = 37 | MARKDOWN_SUPPORT = YES 38 | AUTOLINK_SUPPORT = YES 39 | BUILTIN_STL_SUPPORT = NO 40 | CPP_CLI_SUPPORT = NO 41 | SIP_SUPPORT = NO 42 | IDL_PROPERTY_SUPPORT = YES 43 | DISTRIBUTE_GROUP_DOC = NO 44 | GROUP_NESTED_COMPOUNDS = NO 45 | SUBGROUPING = YES 46 | INLINE_GROUPED_CLASSES = NO 47 | INLINE_SIMPLE_STRUCTS = NO 48 | TYPEDEF_HIDES_STRUCT = YES 49 | LOOKUP_CACHE_SIZE = 0 50 | #--------------------------------------------------------------------------- 51 | # Build related configuration options 52 | #--------------------------------------------------------------------------- 53 | EXTRACT_ALL = YES 54 | EXTRACT_PRIVATE = NO 55 | EXTRACT_PACKAGE = NO 56 | EXTRACT_STATIC = YES 57 | EXTRACT_LOCAL_CLASSES = YES 58 | EXTRACT_LOCAL_METHODS = NO 59 | EXTRACT_ANON_NSPACES = NO 60 | HIDE_UNDOC_MEMBERS = NO 61 | HIDE_UNDOC_CLASSES = NO 62 | HIDE_FRIEND_COMPOUNDS = NO 63 | HIDE_IN_BODY_DOCS = NO 64 | INTERNAL_DOCS = NO 65 | CASE_SENSE_NAMES = YES 66 | HIDE_SCOPE_NAMES = NO 67 | HIDE_COMPOUND_REFERENCE= NO 68 | SHOW_INCLUDE_FILES = YES 69 | SHOW_GROUPED_MEMB_INC = NO 70 | FORCE_LOCAL_INCLUDES = NO 71 | INLINE_INFO = YES 72 | SORT_MEMBER_DOCS = YES 73 | SORT_BRIEF_DOCS = NO 74 | SORT_MEMBERS_CTORS_1ST = NO 75 | SORT_GROUP_NAMES = NO 76 | SORT_BY_SCOPE_NAME = NO 77 | STRICT_PROTO_MATCHING = NO 78 | GENERATE_TODOLIST = YES 79 | GENERATE_TESTLIST = YES 80 | GENERATE_BUGLIST = YES 81 | GENERATE_DEPRECATEDLIST= YES 82 | ENABLED_SECTIONS = 83 | MAX_INITIALIZER_LINES = 30 84 | SHOW_USED_FILES = YES 85 | SHOW_FILES = YES 86 | SHOW_NAMESPACES = YES 87 | FILE_VERSION_FILTER = 88 | LAYOUT_FILE = 89 | CITE_BIB_FILES = 90 | #--------------------------------------------------------------------------- 91 | # Configuration options related to warning and progress messages 92 | #--------------------------------------------------------------------------- 93 | QUIET = NO 94 | WARNINGS = YES 95 | WARN_IF_UNDOCUMENTED = YES 96 | WARN_IF_DOC_ERROR = YES 97 | WARN_NO_PARAMDOC = NO 98 | WARN_AS_ERROR = NO 99 | WARN_FORMAT = "$file:$line: $text" 100 | WARN_LOGFILE = 101 | #--------------------------------------------------------------------------- 102 | # Configuration options related to the input files 103 | #--------------------------------------------------------------------------- 104 | INPUT = include source 105 | INPUT_ENCODING = UTF-8 106 | FILE_PATTERNS = 107 | RECURSIVE = YES 108 | EXCLUDE = 109 | EXCLUDE_SYMLINKS = NO 110 | EXCLUDE_PATTERNS = 111 | EXCLUDE_SYMBOLS = 112 | EXAMPLE_PATH = 113 | EXAMPLE_PATTERNS = 114 | EXAMPLE_RECURSIVE = NO 115 | IMAGE_PATH = 116 | INPUT_FILTER = 117 | FILTER_PATTERNS = 118 | FILTER_SOURCE_FILES = NO 119 | FILTER_SOURCE_PATTERNS = 120 | USE_MDFILE_AS_MAINPAGE = 121 | #--------------------------------------------------------------------------- 122 | # Configuration options related to source browsing 123 | #--------------------------------------------------------------------------- 124 | SOURCE_BROWSER = NO 125 | INLINE_SOURCES = NO 126 | STRIP_CODE_COMMENTS = YES 127 | REFERENCED_BY_RELATION = NO 128 | REFERENCES_RELATION = NO 129 | REFERENCES_LINK_SOURCE = YES 130 | SOURCE_TOOLTIPS = YES 131 | USE_HTAGS = NO 132 | VERBATIM_HEADERS = YES 133 | #--------------------------------------------------------------------------- 134 | # Configuration options related to the alphabetical class index 135 | #--------------------------------------------------------------------------- 136 | ALPHABETICAL_INDEX = YES 137 | COLS_IN_ALPHA_INDEX = 5 138 | IGNORE_PREFIX = 139 | #--------------------------------------------------------------------------- 140 | # Configuration options related to the HTML output 141 | #--------------------------------------------------------------------------- 142 | GENERATE_HTML = YES 143 | HTML_OUTPUT = html 144 | HTML_FILE_EXTENSION = .html 145 | HTML_HEADER = 146 | HTML_FOOTER = 147 | HTML_STYLESHEET = 148 | HTML_EXTRA_STYLESHEET = 149 | HTML_EXTRA_FILES = 150 | HTML_COLORSTYLE_HUE = 220 151 | HTML_COLORSTYLE_SAT = 100 152 | HTML_COLORSTYLE_GAMMA = 80 153 | HTML_TIMESTAMP = NO 154 | HTML_DYNAMIC_SECTIONS = NO 155 | HTML_INDEX_NUM_ENTRIES = 100 156 | GENERATE_DOCSET = NO 157 | DOCSET_FEEDNAME = "Doxygen generated docs" 158 | DOCSET_BUNDLE_ID = org.doxygen.Project 159 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher 160 | DOCSET_PUBLISHER_NAME = Publisher 161 | GENERATE_HTMLHELP = NO 162 | CHM_FILE = 163 | HHC_LOCATION = 164 | GENERATE_CHI = NO 165 | CHM_INDEX_ENCODING = 166 | BINARY_TOC = NO 167 | TOC_EXPAND = NO 168 | GENERATE_QHP = NO 169 | QCH_FILE = 170 | QHP_NAMESPACE = org.doxygen.Project 171 | QHP_VIRTUAL_FOLDER = doc 172 | QHP_CUST_FILTER_NAME = 173 | QHP_CUST_FILTER_ATTRS = 174 | QHP_SECT_FILTER_ATTRS = 175 | QHG_LOCATION = 176 | GENERATE_ECLIPSEHELP = NO 177 | ECLIPSE_DOC_ID = org.doxygen.Project 178 | DISABLE_INDEX = NO 179 | GENERATE_TREEVIEW = NO 180 | ENUM_VALUES_PER_LINE = 4 181 | TREEVIEW_WIDTH = 250 182 | EXT_LINKS_IN_WINDOW = NO 183 | FORMULA_FONTSIZE = 10 184 | FORMULA_TRANSPARENT = YES 185 | USE_MATHJAX = NO 186 | MATHJAX_FORMAT = HTML-CSS 187 | MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest 188 | MATHJAX_EXTENSIONS = 189 | MATHJAX_CODEFILE = 190 | SEARCHENGINE = YES 191 | SERVER_BASED_SEARCH = NO 192 | EXTERNAL_SEARCH = NO 193 | SEARCHENGINE_URL = 194 | SEARCHDATA_FILE = searchdata.xml 195 | EXTERNAL_SEARCH_ID = 196 | EXTRA_SEARCH_MAPPINGS = 197 | #--------------------------------------------------------------------------- 198 | # Configuration options related to the LaTeX output 199 | #--------------------------------------------------------------------------- 200 | GENERATE_LATEX = NO 201 | LATEX_OUTPUT = latex 202 | LATEX_CMD_NAME = latex 203 | MAKEINDEX_CMD_NAME = makeindex 204 | COMPACT_LATEX = NO 205 | PAPER_TYPE = a4 206 | EXTRA_PACKAGES = 207 | LATEX_HEADER = 208 | LATEX_FOOTER = 209 | LATEX_EXTRA_STYLESHEET = 210 | LATEX_EXTRA_FILES = 211 | PDF_HYPERLINKS = YES 212 | USE_PDFLATEX = YES 213 | LATEX_BATCHMODE = NO 214 | LATEX_HIDE_INDICES = NO 215 | LATEX_SOURCE_CODE = NO 216 | LATEX_BIB_STYLE = plain 217 | LATEX_TIMESTAMP = NO 218 | #--------------------------------------------------------------------------- 219 | # Configuration options related to the RTF output 220 | #--------------------------------------------------------------------------- 221 | GENERATE_RTF = NO 222 | RTF_OUTPUT = rtf 223 | COMPACT_RTF = NO 224 | RTF_HYPERLINKS = NO 225 | RTF_STYLESHEET_FILE = 226 | RTF_EXTENSIONS_FILE = 227 | RTF_SOURCE_CODE = NO 228 | #--------------------------------------------------------------------------- 229 | # Configuration options related to the man page output 230 | #--------------------------------------------------------------------------- 231 | GENERATE_MAN = NO 232 | MAN_OUTPUT = man 233 | MAN_EXTENSION = .3 234 | MAN_SUBDIR = 235 | MAN_LINKS = NO 236 | #--------------------------------------------------------------------------- 237 | # Configuration options related to the XML output 238 | #--------------------------------------------------------------------------- 239 | GENERATE_XML = NO 240 | XML_OUTPUT = xml 241 | XML_PROGRAMLISTING = YES 242 | #--------------------------------------------------------------------------- 243 | # Configuration options related to the DOCBOOK output 244 | #--------------------------------------------------------------------------- 245 | GENERATE_DOCBOOK = NO 246 | DOCBOOK_OUTPUT = docbook 247 | DOCBOOK_PROGRAMLISTING = NO 248 | #--------------------------------------------------------------------------- 249 | # Configuration options for the AutoGen Definitions output 250 | #--------------------------------------------------------------------------- 251 | GENERATE_AUTOGEN_DEF = NO 252 | #--------------------------------------------------------------------------- 253 | # Configuration options related to the Perl module output 254 | #--------------------------------------------------------------------------- 255 | GENERATE_PERLMOD = NO 256 | PERLMOD_LATEX = NO 257 | PERLMOD_PRETTY = YES 258 | PERLMOD_MAKEVAR_PREFIX = 259 | #--------------------------------------------------------------------------- 260 | # Configuration options related to the preprocessor 261 | #--------------------------------------------------------------------------- 262 | ENABLE_PREPROCESSING = YES 263 | MACRO_EXPANSION = NO 264 | EXPAND_ONLY_PREDEF = NO 265 | SEARCH_INCLUDES = YES 266 | INCLUDE_PATH = 267 | INCLUDE_FILE_PATTERNS = 268 | PREDEFINED = _3DS 269 | EXPAND_AS_DEFINED = 270 | SKIP_FUNCTION_MACROS = YES 271 | #--------------------------------------------------------------------------- 272 | # Configuration options related to external references 273 | #--------------------------------------------------------------------------- 274 | TAGFILES = 275 | GENERATE_TAGFILE = 276 | ALLEXTERNALS = NO 277 | EXTERNAL_GROUPS = YES 278 | EXTERNAL_PAGES = YES 279 | PERL_PATH = /usr/bin/perl 280 | #--------------------------------------------------------------------------- 281 | # Configuration options related to the dot tool 282 | #--------------------------------------------------------------------------- 283 | CLASS_DIAGRAMS = YES 284 | MSCGEN_PATH = 285 | DIA_PATH = 286 | HIDE_UNDOC_RELATIONS = YES 287 | HAVE_DOT = YES 288 | DOT_NUM_THREADS = 0 289 | DOT_FONTNAME = Helvetica 290 | DOT_FONTSIZE = 10 291 | DOT_FONTPATH = 292 | CLASS_GRAPH = YES 293 | COLLABORATION_GRAPH = YES 294 | GROUP_GRAPHS = YES 295 | UML_LOOK = NO 296 | UML_LIMIT_NUM_FIELDS = 10 297 | TEMPLATE_RELATIONS = NO 298 | INCLUDE_GRAPH = YES 299 | INCLUDED_BY_GRAPH = YES 300 | CALL_GRAPH = NO 301 | CALLER_GRAPH = NO 302 | GRAPHICAL_HIERARCHY = YES 303 | DIRECTORY_GRAPH = YES 304 | DOT_IMAGE_FORMAT = png 305 | INTERACTIVE_SVG = NO 306 | DOT_PATH = 307 | DOTFILE_DIRS = 308 | MSCFILE_DIRS = 309 | DIAFILE_DIRS = 310 | PLANTUML_JAR_PATH = 311 | PLANTUML_INCLUDE_PATH = 312 | DOT_GRAPH_MAX_NODES = 50 313 | MAX_DOT_GRAPH_DEPTH = 0 314 | DOT_TRANSPARENT = NO 315 | DOT_MULTI_TARGETS = NO 316 | GENERATE_LEGEND = YES 317 | DOT_CLEANUP = YES 318 | -------------------------------------------------------------------------------- /source/renderqueue.c: -------------------------------------------------------------------------------- 1 | #include "internal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | static C3D_RenderTarget *firstTarget, *lastTarget; 7 | static C3D_RenderTarget *linkedTarget[3]; 8 | 9 | static TickCounter gpuTime, cpuTime; 10 | 11 | static bool inFrame, inSafeTransfer, measureGpuTime; 12 | static bool needSwapTop, needSwapBot, isTopStereo; 13 | static float framerate = 60.0f; 14 | static float framerateCounter[2] = { 60.0f, 60.0f }; 15 | static u32 frameCounter[2]; 16 | static void (* frameEndCb)(void*); 17 | static void* frameEndCbData; 18 | 19 | static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target); 20 | 21 | static bool framerateLimit(int id) 22 | { 23 | framerateCounter[id] -= framerate; 24 | if (framerateCounter[id] <= 0.0f) 25 | { 26 | framerateCounter[id] += 60.0f; 27 | return true; 28 | } 29 | return false; 30 | } 31 | 32 | static void onVBlank0(C3D_UNUSED void* unused) 33 | { 34 | if (framerateLimit(0)) 35 | frameCounter[0]++; 36 | } 37 | 38 | static void onVBlank1(C3D_UNUSED void* unused) 39 | { 40 | if (framerateLimit(1)) 41 | frameCounter[1]++; 42 | } 43 | 44 | static void onQueueFinish(gxCmdQueue_s* queue) 45 | { 46 | if (measureGpuTime) 47 | { 48 | osTickCounterUpdate(&gpuTime); 49 | measureGpuTime = false; 50 | } 51 | if (inSafeTransfer) 52 | { 53 | inSafeTransfer = false; 54 | if (inFrame) 55 | { 56 | gxCmdQueueStop(queue); 57 | gxCmdQueueClear(queue); 58 | } 59 | } 60 | else 61 | { 62 | if (needSwapTop) 63 | { 64 | gfxScreenSwapBuffers(GFX_TOP, isTopStereo); 65 | needSwapTop = false; 66 | } 67 | if (needSwapBot) 68 | { 69 | gfxScreenSwapBuffers(GFX_BOTTOM, false); 70 | needSwapBot = false; 71 | } 72 | } 73 | } 74 | 75 | void C3D_FrameSync(void) 76 | { 77 | u32 cur[2]; 78 | u32 start[2] = { frameCounter[0], frameCounter[1] }; 79 | do 80 | { 81 | gspWaitForAnyEvent(); 82 | cur[0] = frameCounter[0]; 83 | cur[1] = frameCounter[1]; 84 | } while (cur[0]==start[0] || cur[1]==start[1]); 85 | } 86 | 87 | u32 C3D_FrameCounter(int id) 88 | { 89 | return frameCounter[id]; 90 | } 91 | 92 | static bool C3Di_WaitAndClearQueue(s64 timeout) 93 | { 94 | gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue; 95 | if (!gxCmdQueueWait(queue, timeout)) 96 | return false; 97 | gxCmdQueueStop(queue); 98 | gxCmdQueueClear(queue); 99 | return true; 100 | } 101 | 102 | void C3Di_RenderQueueEnableVBlank(void) 103 | { 104 | gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false); 105 | gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false); 106 | } 107 | 108 | void C3Di_RenderQueueDisableVBlank(void) 109 | { 110 | gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false); 111 | gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false); 112 | } 113 | 114 | void C3Di_RenderQueueInit(void) 115 | { 116 | C3D_Context* ctx = C3Di_GetContext(); 117 | 118 | C3Di_RenderQueueEnableVBlank(); 119 | 120 | GX_BindQueue(&ctx->gxQueue); 121 | gxCmdQueueSetCallback(&ctx->gxQueue, onQueueFinish, NULL); 122 | gxCmdQueueRun(&ctx->gxQueue); 123 | } 124 | 125 | void C3Di_RenderQueueExit(void) 126 | { 127 | int i; 128 | C3D_RenderTarget *a, *next; 129 | 130 | C3Di_WaitAndClearQueue(-1); 131 | gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL); 132 | GX_BindQueue(NULL); 133 | 134 | C3Di_RenderQueueDisableVBlank(); 135 | 136 | for (i = 0; i < 3; i ++) 137 | linkedTarget[i] = NULL; 138 | 139 | for (a = firstTarget; a; a = next) 140 | { 141 | next = a->next; 142 | C3Di_RenderTargetDestroy(a); 143 | } 144 | } 145 | 146 | void C3Di_RenderQueueWaitDone(void) 147 | { 148 | C3Di_WaitAndClearQueue(-1); 149 | } 150 | 151 | float C3D_FrameRate(float fps) 152 | { 153 | float old = framerate; 154 | if (fps > 0.0f && fps <= 60.0f) 155 | { 156 | framerate = fps; 157 | framerateCounter[0] = fps; 158 | framerateCounter[1] = fps; 159 | } 160 | return old; 161 | } 162 | 163 | bool C3D_FrameBegin(u8 flags) 164 | { 165 | C3D_Context* ctx = C3Di_GetContext(); 166 | if (inFrame) return false; 167 | 168 | if (flags & C3D_FRAME_SYNCDRAW) 169 | C3D_FrameSync(); 170 | if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1)) 171 | return false; 172 | 173 | inFrame = true; 174 | osTickCounterStart(&cpuTime); 175 | GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0); 176 | return true; 177 | } 178 | 179 | bool C3D_FrameDrawOn(C3D_RenderTarget* target) 180 | { 181 | if (!inFrame) return false; 182 | 183 | target->used = true; 184 | C3D_SetFrameBuf(&target->frameBuf); 185 | C3D_SetViewport(0, 0, target->frameBuf.width, target->frameBuf.height); 186 | return true; 187 | } 188 | 189 | void C3D_FrameSplit(u8 flags) 190 | { 191 | u32 *cmdBuf, cmdBufSize; 192 | if (!inFrame) return; 193 | if (C3Di_SplitFrame(&cmdBuf, &cmdBufSize)) 194 | GX_ProcessCommandList(cmdBuf, cmdBufSize*4, flags); 195 | } 196 | 197 | void C3D_FrameEnd(u8 flags) 198 | { 199 | C3D_Context* ctx = C3Di_GetContext(); 200 | if (!inFrame) return; 201 | 202 | if (frameEndCb) 203 | frameEndCb(frameEndCbData); 204 | 205 | C3D_FrameSplit(flags); 206 | GPUCMD_SetBuffer(NULL, 0, 0); 207 | osTickCounterUpdate(&cpuTime); 208 | inFrame = false; 209 | 210 | // Flush the entire linear memory if the user did not explicitly mandate to flush the command list 211 | if (!(flags & GX_CMDLIST_FLUSH)) 212 | { 213 | extern u32 __ctru_linear_heap; 214 | extern u32 __ctru_linear_heap_size; 215 | GSPGPU_FlushDataCache((void*)__ctru_linear_heap, __ctru_linear_heap_size); 216 | } 217 | 218 | int i; 219 | C3D_RenderTarget* target; 220 | isTopStereo = false; 221 | for (i = 2; i >= 0; i --) 222 | { 223 | target = linkedTarget[i]; 224 | if (!target || !target->used) 225 | continue; 226 | target->used = false; 227 | C3D_FrameBufTransfer(&target->frameBuf, target->screen, target->side, target->transferFlags); 228 | if (target->screen == GFX_TOP) 229 | { 230 | needSwapTop = true; 231 | if (target->side == GFX_RIGHT) 232 | isTopStereo = true; 233 | } 234 | else if (target->screen == GFX_BOTTOM) 235 | needSwapBot = true; 236 | } 237 | 238 | measureGpuTime = true; 239 | osTickCounterStart(&gpuTime); 240 | gxCmdQueueRun(&ctx->gxQueue); 241 | } 242 | 243 | void C3D_FrameEndHook(void (* hook)(void*), void* param) 244 | { 245 | frameEndCb = hook; 246 | frameEndCbData = param; 247 | } 248 | 249 | float C3D_GetDrawingTime(void) 250 | { 251 | return osTickCounterRead(&gpuTime); 252 | } 253 | 254 | float C3D_GetProcessingTime(void) 255 | { 256 | return osTickCounterRead(&cpuTime); 257 | } 258 | 259 | static C3D_RenderTarget* C3Di_RenderTargetNew(void) 260 | { 261 | C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget)); 262 | if (!target) return NULL; 263 | memset(target, 0, sizeof(C3D_RenderTarget)); 264 | return target; 265 | } 266 | 267 | static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target) 268 | { 269 | target->prev = lastTarget; 270 | target->next = NULL; 271 | if (lastTarget) 272 | lastTarget->next = target; 273 | if (!firstTarget) 274 | firstTarget = target; 275 | lastTarget = target; 276 | } 277 | 278 | C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt) 279 | { 280 | GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16; 281 | void* depthBuf = NULL; 282 | void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt)); 283 | if (!colorBuf) goto _fail0; 284 | if (C3D_DEPTHTYPE_OK(depthFmt)) 285 | { 286 | depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt); 287 | size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmtReal); 288 | vramAllocPos vramBank = addrGetVRAMBank(colorBuf); 289 | depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first... 290 | if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank 291 | if (!depthBuf) goto _fail1; 292 | } 293 | 294 | C3D_RenderTarget* target = C3Di_RenderTargetNew(); 295 | if (!target) goto _fail2; 296 | 297 | C3D_FrameBuf* fb = &target->frameBuf; 298 | C3D_FrameBufAttrib(fb, width, height, false); 299 | C3D_FrameBufColor(fb, colorBuf, colorFmt); 300 | target->ownsColor = true; 301 | if (depthBuf) 302 | { 303 | C3D_FrameBufDepth(fb, depthBuf, depthFmtReal); 304 | target->ownsDepth = true; 305 | } 306 | C3Di_RenderTargetFinishInit(target); 307 | return target; 308 | 309 | _fail2: 310 | if (depthBuf) vramFree(depthBuf); 311 | _fail1: 312 | vramFree(colorBuf); 313 | _fail0: 314 | return NULL; 315 | } 316 | 317 | C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt) 318 | { 319 | if (!addrIsVRAM(tex->data)) return NULL; // Render targets must be in VRAM 320 | C3D_RenderTarget* target = C3Di_RenderTargetNew(); 321 | if (!target) return NULL; 322 | 323 | C3D_FrameBuf* fb = &target->frameBuf; 324 | C3D_FrameBufTex(fb, tex, face, level); 325 | 326 | if (C3D_DEPTHTYPE_OK(depthFmt)) 327 | { 328 | GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt); 329 | size_t depthSize = C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal); 330 | vramAllocPos vramBank = addrGetVRAMBank(tex->data); 331 | void* depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first... 332 | if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank 333 | if (!depthBuf) 334 | { 335 | free(target); 336 | return NULL; 337 | } 338 | 339 | C3D_FrameBufDepth(fb, depthBuf, depthFmtReal); 340 | target->ownsDepth = true; 341 | } 342 | 343 | C3Di_RenderTargetFinishInit(target); 344 | return target; 345 | } 346 | 347 | void C3Di_RenderTargetDestroy(C3D_RenderTarget* target) 348 | { 349 | if (target->ownsColor) 350 | vramFree(target->frameBuf.colorBuf); 351 | if (target->ownsDepth) 352 | vramFree(target->frameBuf.depthBuf); 353 | 354 | C3D_RenderTarget** prevNext = target->prev ? &target->prev->next : &firstTarget; 355 | C3D_RenderTarget** nextPrev = target->next ? &target->next->prev : &lastTarget; 356 | *prevNext = target->next; 357 | *nextPrev = target->prev; 358 | free(target); 359 | } 360 | 361 | void C3D_RenderTargetDelete(C3D_RenderTarget* target) 362 | { 363 | if (inFrame) 364 | svcBreak(USERBREAK_PANIC); // Shouldn't happen. 365 | if (target->linked) 366 | C3D_RenderTargetDetachOutput(target); 367 | else 368 | C3Di_WaitAndClearQueue(-1); 369 | C3Di_RenderTargetDestroy(target); 370 | } 371 | 372 | void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags) 373 | { 374 | int id = 0; 375 | if (screen==GFX_BOTTOM) id = 2; 376 | else if (side==GFX_RIGHT) id = 1; 377 | if (linkedTarget[id]) 378 | { 379 | linkedTarget[id]->linked = false; 380 | if (!inFrame) 381 | C3Di_WaitAndClearQueue(-1); 382 | } 383 | linkedTarget[id] = target; 384 | if (target) 385 | { 386 | target->linked = true; 387 | target->transferFlags = transferFlags; 388 | target->screen = screen; 389 | target->side = side; 390 | } 391 | } 392 | 393 | static void C3Di_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags) 394 | { 395 | C3Di_WaitAndClearQueue(-1); 396 | inSafeTransfer = true; 397 | GX_DisplayTransfer(inadr, indim, outadr, outdim, flags); 398 | gxCmdQueueRun(&C3Di_GetContext()->gxQueue); 399 | } 400 | 401 | static void C3Di_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags) 402 | { 403 | C3Di_WaitAndClearQueue(-1); 404 | inSafeTransfer = true; 405 | GX_TextureCopy(inadr, indim, outadr, outdim, size, flags); 406 | gxCmdQueueRun(&C3Di_GetContext()->gxQueue); 407 | } 408 | 409 | static void C3Di_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1) 410 | { 411 | C3Di_WaitAndClearQueue(-1); 412 | inSafeTransfer = true; 413 | GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1); 414 | gxCmdQueueRun(&C3Di_GetContext()->gxQueue); 415 | } 416 | 417 | void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags) 418 | { 419 | if (inFrame) 420 | { 421 | C3D_FrameSplit(0); 422 | GX_DisplayTransfer(inadr, indim, outadr, outdim, flags); 423 | } else 424 | { 425 | C3Di_SafeDisplayTransfer(inadr, indim, outadr, outdim, flags); 426 | gspWaitForPPF(); 427 | } 428 | } 429 | 430 | void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags) 431 | { 432 | if (inFrame) 433 | { 434 | C3D_FrameSplit(0); 435 | GX_TextureCopy(inadr, indim, outadr, outdim, size, flags); 436 | } else 437 | { 438 | C3Di_SafeTextureCopy(inadr, indim, outadr, outdim, size, flags); 439 | gspWaitForPPF(); 440 | } 441 | } 442 | 443 | void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1) 444 | { 445 | if (inFrame) 446 | { 447 | C3D_FrameSplit(0); 448 | GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1); 449 | } else 450 | { 451 | C3Di_SafeMemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1); 452 | gspWaitForPSC0(); 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /include/c3d/maths.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | #include 4 | #include 5 | 6 | /** 7 | * @addtogroup math_support 8 | * @brief Implementations of matrix, vector, and quaternion operations. 9 | * @{ 10 | */ 11 | 12 | /** 13 | * The one true circumference-to-radius ratio. 14 | * See http://tauday.com/tau-manifesto 15 | */ 16 | #define M_TAU (6.28318530717958647692528676655900576) 17 | 18 | // Define the legacy circle constant as well 19 | #ifndef M_PI 20 | #define M_PI (M_TAU/2) 21 | #endif 22 | 23 | /** 24 | * @brief Convert an angle from revolutions to radians 25 | * @param[in] _angle Proportion of a full revolution 26 | * @return Angle in radians 27 | */ 28 | #define C3D_Angle(_angle) ((_angle)*M_TAU) 29 | 30 | /** 31 | * @brief Convert an angle from degrees to radians 32 | * @param[in] _angle Angle in degrees 33 | * @return Angle in radians 34 | */ 35 | #define C3D_AngleFromDegrees(_angle) ((_angle)*M_TAU/360.0f) 36 | 37 | #define C3D_AspectRatioTop (400.0f / 240.0f) ///< Aspect ratio for 3DS top screen 38 | #define C3D_AspectRatioBot (320.0f / 240.0f) ///< Aspect ratio for 3DS bottom screen 39 | 40 | /** 41 | * @name Vector Math 42 | * @{ 43 | */ 44 | 45 | /** 46 | * @brief Create a new FVec4 47 | * @param[in] x X-component 48 | * @param[in] y Y-component 49 | * @param[in] z Z-component 50 | * @param[in] w W-component 51 | * @return New FVec4 52 | */ 53 | static inline C3D_FVec FVec4_New(float x, float y, float z, float w) 54 | { 55 | return (C3D_FVec){{ w, z, y, x }}; 56 | } 57 | 58 | /** 59 | * @brief Add two FVec4s 60 | * @param[in] lhs Augend 61 | * @param[in] rhs Addend 62 | * @return lhs+rhs (sum) 63 | */ 64 | static inline C3D_FVec FVec4_Add(C3D_FVec lhs, C3D_FVec rhs) 65 | { 66 | // component-wise addition 67 | return FVec4_New(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z, lhs.w+rhs.w); 68 | } 69 | 70 | /** 71 | * @brief Subtract two FVec4s 72 | * @param[in] lhs Minuend 73 | * @param[in] rhs Subtrahend 74 | * @return lhs-rhs (difference) 75 | */ 76 | static inline C3D_FVec FVec4_Subtract(C3D_FVec lhs, C3D_FVec rhs) 77 | { 78 | // component-wise subtraction 79 | return FVec4_New(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); 80 | } 81 | 82 | /** 83 | * @brief Negate a FVec4 84 | * @note This is equivalent to `FVec4_Scale(v, -1)` 85 | * @param[in] v Vector to negate 86 | * @return -v 87 | */ 88 | static inline C3D_FVec FVec4_Negate(C3D_FVec v) 89 | { 90 | // component-wise negation 91 | return FVec4_New(-v.x, -v.y, -v.z, -v.w); 92 | } 93 | 94 | /** 95 | * @brief Scale a FVec4 96 | * @param[in] v Vector to scale 97 | * @param[in] s Scale factor 98 | * @return v*s 99 | */ 100 | static inline C3D_FVec FVec4_Scale(C3D_FVec v, float s) 101 | { 102 | // component-wise scaling 103 | return FVec4_New(v.x*s, v.y*s, v.z*s, v.w*s); 104 | } 105 | 106 | /** 107 | * @brief Perspective divide 108 | * @param[in] v Vector to divide 109 | * @return v/v.w 110 | */ 111 | static inline C3D_FVec FVec4_PerspDivide(C3D_FVec v) 112 | { 113 | // divide by w 114 | return FVec4_New(v.x/v.w, v.y/v.w, v.z/v.w, 1.0f); 115 | } 116 | 117 | /** 118 | * @brief Dot product of two FVec4s 119 | * @param[in] lhs Left-side FVec4 120 | * @param[in] rhs Right-side FVec4 121 | * @return lhs∙rhs 122 | */ 123 | static inline float FVec4_Dot(C3D_FVec lhs, C3D_FVec rhs) 124 | { 125 | // A∙B = sum of component-wise products 126 | return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w; 127 | } 128 | 129 | /** 130 | * @brief Magnitude of a FVec4 131 | * @param[in] v Vector 132 | * @return ‖v‖ 133 | */ 134 | static inline float FVec4_Magnitude(C3D_FVec v) 135 | { 136 | // ‖v‖ = √(v∙v) 137 | return sqrtf(FVec4_Dot(v,v)); 138 | } 139 | 140 | /** 141 | * @brief Normalize a FVec4 142 | * @param[in] v FVec4 to normalize 143 | * @return v/‖v‖ 144 | */ 145 | static inline C3D_FVec FVec4_Normalize(C3D_FVec v) 146 | { 147 | // get vector magnitude 148 | float m = FVec4_Magnitude(v); 149 | 150 | // scale by inverse magnitude to get a unit vector 151 | return FVec4_New(v.x/m, v.y/m, v.z/m, v.w/m); 152 | } 153 | 154 | /** 155 | * @brief Create a new FVec3 156 | * @param[in] x X-component 157 | * @param[in] y Y-component 158 | * @param[in] z Z-component 159 | * @return New FVec3 160 | */ 161 | static inline C3D_FVec FVec3_New(float x, float y, float z) 162 | { 163 | return FVec4_New(x, y, z, 0.0f); 164 | } 165 | 166 | /** 167 | * @brief Dot product of two FVec3s 168 | * @param[in] lhs Left-side FVec3 169 | * @param[in] rhs Right-side FVec3 170 | * @return lhs∙rhs 171 | */ 172 | static inline float FVec3_Dot(C3D_FVec lhs, C3D_FVec rhs) 173 | { 174 | // A∙B = sum of component-wise products 175 | return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; 176 | } 177 | 178 | /** 179 | * @brief Magnitude of a FVec3 180 | * @param[in] v Vector 181 | * @return ‖v‖ 182 | */ 183 | static inline float FVec3_Magnitude(C3D_FVec v) 184 | { 185 | // ‖v‖ = √(v∙v) 186 | return sqrtf(FVec3_Dot(v,v)); 187 | } 188 | 189 | /** 190 | * @brief Normalize a FVec3 191 | * @param[in] v FVec3 to normalize 192 | * @return v/‖v‖ 193 | */ 194 | static inline C3D_FVec FVec3_Normalize(C3D_FVec v) 195 | { 196 | // get vector magnitude 197 | float m = FVec3_Magnitude(v); 198 | 199 | // scale by inverse magnitude to get a unit vector 200 | return FVec3_New(v.x/m, v.y/m, v.z/m); 201 | } 202 | 203 | /** 204 | * @brief Add two FVec3s 205 | * @param[in] lhs Augend 206 | * @param[in] rhs Addend 207 | * @return lhs+rhs (sum) 208 | */ 209 | static inline C3D_FVec FVec3_Add(C3D_FVec lhs, C3D_FVec rhs) 210 | { 211 | // component-wise addition 212 | return FVec3_New(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); 213 | } 214 | 215 | /** 216 | * @brief Subtract two FVec3s 217 | * @param[in] lhs Minuend 218 | * @param[in] rhs Subtrahend 219 | * @return lhs-rhs (difference) 220 | */ 221 | static inline C3D_FVec FVec3_Subtract(C3D_FVec lhs, C3D_FVec rhs) 222 | { 223 | // component-wise subtraction 224 | return FVec3_New(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z); 225 | } 226 | 227 | /** 228 | * @brief Distance between two 3D points 229 | * @param[in] lhs Relative origin 230 | * @param[in] rhs Relative point of interest 231 | * @return ‖lhs-rhs‖ 232 | */ 233 | static inline float FVec3_Distance(C3D_FVec lhs, C3D_FVec rhs) 234 | { 235 | // distance = ‖lhs-rhs‖ 236 | return FVec3_Magnitude(FVec3_Subtract(lhs, rhs)); 237 | } 238 | 239 | /** 240 | * @brief Scale a FVec3 241 | * @param[in] v Vector to scale 242 | * @param[in] s Scale factor 243 | * @return v*s 244 | */ 245 | static inline C3D_FVec FVec3_Scale(C3D_FVec v, float s) 246 | { 247 | // component-wise scaling 248 | return FVec3_New(v.x*s, v.y*s, v.z*s); 249 | } 250 | 251 | /** 252 | * @brief Negate a FVec3 253 | * @note This is equivalent to `FVec3_Scale(v, -1)` 254 | * @param[in] v Vector to negate 255 | * @return -v 256 | */ 257 | static inline C3D_FVec FVec3_Negate(C3D_FVec v) 258 | { 259 | // component-wise negation 260 | return FVec3_New(-v.x, -v.y, -v.z); 261 | } 262 | 263 | /** 264 | * @brief Cross product of two FVec3s 265 | * @note This returns a pseudo-vector which is perpendicular to the plane 266 | * spanned by the two input vectors. 267 | * @param[in] lhs Left-side FVec3 268 | * @param[in] rhs Right-side FVec3 269 | * @return lhs×rhs 270 | */ 271 | static inline C3D_FVec FVec3_Cross(C3D_FVec lhs, C3D_FVec rhs) 272 | { 273 | // A×B = (AyBz - AzBy, AzBx - AxBz, AxBy - AyBx) 274 | return FVec3_New(lhs.y*rhs.z - lhs.z*rhs.y, lhs.z*rhs.x - lhs.x*rhs.z, lhs.x*rhs.y - lhs.y*rhs.x); 275 | } 276 | /** @} */ 277 | 278 | /** 279 | * @name Matrix Math 280 | * @note All matrices are 4x4 unless otherwise noted. 281 | * @{ 282 | */ 283 | 284 | /** 285 | * @brief Zero matrix 286 | * @param[out] out Matrix to zero 287 | */ 288 | static inline void Mtx_Zeros(C3D_Mtx* out) 289 | { 290 | memset(out, 0, sizeof(*out)); 291 | } 292 | 293 | /** 294 | * @brief Copy a matrix 295 | * @param[out] out Output matrix 296 | * @param[in] in Input matrix 297 | */ 298 | static inline void Mtx_Copy(C3D_Mtx* out, const C3D_Mtx* in) 299 | { 300 | *out = *in; 301 | } 302 | 303 | /** 304 | * @brief Creates a matrix with the diagonal using the given parameters. 305 | * @param[out] out Output matrix. 306 | * @param[in] x The X component. 307 | * @param[in] y The Y component. 308 | * @param[in] z The Z component. 309 | * @param[in] w The W component. 310 | */ 311 | static inline void Mtx_Diagonal(C3D_Mtx* out, float x, float y, float z, float w) 312 | { 313 | Mtx_Zeros(out); 314 | out->r[0].x = x; 315 | out->r[1].y = y; 316 | out->r[2].z = z; 317 | out->r[3].w = w; 318 | } 319 | 320 | /** 321 | * @brief Identity matrix 322 | * @param[out] out Matrix to fill 323 | */ 324 | static inline void Mtx_Identity(C3D_Mtx* out) 325 | { 326 | Mtx_Diagonal(out, 1.0f, 1.0f, 1.0f, 1.0f); 327 | } 328 | 329 | /** 330 | *@brief Transposes the matrix. Row => Column, and vice versa. 331 | *@param[in,out] out Output matrix. 332 | */ 333 | void Mtx_Transpose(C3D_Mtx* out); 334 | 335 | /** 336 | * @brief Matrix addition 337 | * @param[out] out Output matrix. 338 | * @param[in] lhs Left matrix. 339 | * @param[in] rhs Right matrix. 340 | * @return lhs+rhs (sum) 341 | */ 342 | static inline void Mtx_Add(C3D_Mtx* out, const C3D_Mtx* lhs, const C3D_Mtx* rhs) 343 | { 344 | for (int i = 0; i < 16; i++) 345 | out->m[i] = lhs->m[i] + rhs->m[i]; 346 | } 347 | 348 | /** 349 | * @brief Matrix subtraction 350 | * @param[out] out Output matrix. 351 | * @param[in] lhs Left matrix. 352 | * @param[in] rhs Right matrix. 353 | * @return lhs-rhs (difference) 354 | */ 355 | static inline void Mtx_Subtract(C3D_Mtx* out, const C3D_Mtx* lhs, const C3D_Mtx* rhs) 356 | { 357 | for (int i = 0; i < 16; i++) 358 | out->m[i] = lhs->m[i] - rhs->m[i]; 359 | } 360 | 361 | /** 362 | * @brief Multiply two matrices 363 | * @param[out] out Output matrix 364 | * @param[in] a Multiplicand 365 | * @param[in] b Multiplier 366 | */ 367 | void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b); 368 | 369 | /** 370 | * @brief Inverse a matrix 371 | * @param[in,out] out Matrix to inverse 372 | * @retval 0.0f Degenerate matrix (no inverse) 373 | * @return determinant 374 | */ 375 | float Mtx_Inverse(C3D_Mtx* out); 376 | 377 | /** 378 | * @brief Multiply 3x3 matrix by a FVec3 379 | * @param[in] mtx Matrix 380 | * @param[in] v Vector 381 | * @return mtx*v (product) 382 | */ 383 | C3D_FVec Mtx_MultiplyFVec3(const C3D_Mtx* mtx, C3D_FVec v); 384 | 385 | /** 386 | * @brief Multiply 4x4 matrix by a FVec4 387 | * @param[in] mtx Matrix 388 | * @param[in] v Vector 389 | * @return mtx*v (product) 390 | */ 391 | C3D_FVec Mtx_MultiplyFVec4(const C3D_Mtx* mtx, C3D_FVec v); 392 | 393 | /** 394 | * @brief Multiply 4x3 matrix by a FVec3 395 | * @param[in] mtx Matrix 396 | * @param[in] v Vector 397 | * @return mtx*v (product) 398 | */ 399 | static inline C3D_FVec Mtx_MultiplyFVecH(const C3D_Mtx* mtx, C3D_FVec v) 400 | { 401 | v.w = 1.0f; 402 | 403 | return Mtx_MultiplyFVec4(mtx, v); 404 | } 405 | 406 | /** 407 | * @brief Get 4x4 matrix equivalent to Quaternion 408 | * @param[out] m Output matrix 409 | * @param[in] q Input Quaternion 410 | */ 411 | void Mtx_FromQuat(C3D_Mtx* m, C3D_FQuat q); 412 | /** @} */ 413 | 414 | /** 415 | * @name 3D Transformation Matrix Math 416 | * @note bRightSide is used to determine which side to perform the transformation. 417 | * With an input matrix A and a transformation matrix B, bRightSide being 418 | * true yields AB, while being false yield BA. 419 | * @{ 420 | */ 421 | 422 | /** 423 | * @brief 3D translation 424 | * @param[in,out] mtx Matrix to translate 425 | * @param[in] x X component to translate 426 | * @param[in] y Y component to translate 427 | * @param[in] z Z component to translate 428 | * @param[in] bRightSide Whether to transform from the right side 429 | */ 430 | void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z, bool bRightSide); 431 | 432 | /** 433 | * @brief 3D Scale 434 | * @param[in,out] mtx Matrix to scale 435 | * @param[in] x X component to scale 436 | * @param[in] y Y component to scale 437 | * @param[in] z Z component to scale 438 | */ 439 | void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z); 440 | 441 | /** 442 | * @brief 3D Rotation 443 | * @param[in,out] mtx Matrix to rotate 444 | * @param[in] axis Axis about which to rotate 445 | * @param[in] angle Radians to rotate 446 | * @param[in] bRightSide Whether to transform from the right side 447 | */ 448 | void Mtx_Rotate(C3D_Mtx* mtx, C3D_FVec axis, float angle, bool bRightSide); 449 | 450 | /** 451 | * @brief 3D Rotation about the X axis 452 | * @param[in,out] mtx Matrix to rotate 453 | * @param[in] angle Radians to rotate 454 | * @param[in] bRightSide Whether to transform from the right side 455 | */ 456 | void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide); 457 | 458 | /** 459 | * @brief 3D Rotation about the Y axis 460 | * @param[in,out] mtx Matrix to rotate 461 | * @param[in] angle Radians to rotate 462 | * @param[in] bRightSide Whether to transform from the right side 463 | */ 464 | void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide); 465 | 466 | /** 467 | * @brief 3D Rotation about the Z axis 468 | * @param[in,out] mtx Matrix to rotate 469 | * @param[in] angle Radians to rotate 470 | * @param[in] bRightSide Whether to transform from the right side 471 | */ 472 | void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide); 473 | /** @} */ 474 | 475 | /** 476 | * @name 3D Projection Matrix Math 477 | * @{ 478 | */ 479 | 480 | /** 481 | * @brief Orthogonal projection 482 | * @param[out] mtx Output matrix 483 | * @param[in] left Left clip plane (X=left) 484 | * @param[in] right Right clip plane (X=right) 485 | * @param[in] bottom Bottom clip plane (Y=bottom) 486 | * @param[in] top Top clip plane (Y=top) 487 | * @param[in] near Near clip plane (Z=near) 488 | * @param[in] far Far clip plane (Z=far) 489 | * @param[in] isLeftHanded Whether to build a LH projection 490 | * @sa Mtx_OrthoTilt 491 | */ 492 | void Mtx_Ortho(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded); 493 | 494 | /** 495 | * @brief Perspective projection 496 | * @param[out] mtx Output matrix 497 | * @param[in] fovy Vertical field of view in radians 498 | * @param[in] aspect Aspect ration of projection plane (width/height) 499 | * @param[in] near Near clip plane (Z=near) 500 | * @param[in] far Far clip plane (Z=far) 501 | * @param[in] isLeftHanded Whether to build a LH projection 502 | * @sa Mtx_PerspTilt 503 | * @sa Mtx_PerspStereo 504 | * @sa Mtx_PerspStereoTilt 505 | */ 506 | void Mtx_Persp(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded); 507 | 508 | /** 509 | * @brief Stereo perspective projection 510 | * @note Typically you will use iod to mean the distance between the eyes. Plug 511 | * in -iod for the left eye and iod for the right eye. 512 | * @note The focal length is defined by screen. If objects are further than this, 513 | * they will appear to be inside the screen. If objects are closer than this, 514 | * they will appear to pop out of the screen. Objects at this distance appear 515 | * to be at the screen. 516 | * @param[out] mtx Output matrix 517 | * @param[in] fovy Vertical field of view in radians 518 | * @param[in] aspect Aspect ration of projection plane (width/height) 519 | * @param[in] near Near clip plane (Z=near) 520 | * @param[in] far Far clip plane (Z=far) 521 | * @param[in] iod Interocular distance 522 | * @param[in] screen Focal length 523 | * @param[in] isLeftHanded Whether to build a LH projection 524 | * @sa Mtx_Persp 525 | * @sa Mtx_PerspTilt 526 | * @sa Mtx_PerspStereoTilt 527 | */ 528 | void Mtx_PerspStereo(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded); 529 | 530 | /** 531 | * @brief Orthogonal projection, tilted to account for the 3DS screen rotation 532 | * @param[out] mtx Output matrix 533 | * @param[in] left Left clip plane (X=left) 534 | * @param[in] right Right clip plane (X=right) 535 | * @param[in] bottom Bottom clip plane (Y=bottom) 536 | * @param[in] top Top clip plane (Y=top) 537 | * @param[in] near Near clip plane (Z=near) 538 | * @param[in] far Far clip plane (Z=far) 539 | * @param[in] isLeftHanded Whether to build a LH projection 540 | * @sa Mtx_Ortho 541 | */ 542 | void Mtx_OrthoTilt(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded); 543 | 544 | /** 545 | * @brief Perspective projection, tilted to account for the 3DS screen rotation 546 | * @param[out] mtx Output matrix 547 | * @param[in] fovy Vertical field of view in radians 548 | * @param[in] aspect Aspect ration of projection plane (width/height) 549 | * @param[in] near Near clip plane (Z=near) 550 | * @param[in] far Far clip plane (Z=far) 551 | * @param[in] isLeftHanded Whether to build a LH projection 552 | * @sa Mtx_Persp 553 | * @sa Mtx_PerspStereo 554 | * @sa Mtx_PerspStereoTilt 555 | */ 556 | void Mtx_PerspTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded); 557 | 558 | /** 559 | * @brief Stereo perspective projection, tilted to account for the 3DS screen rotation 560 | * @note See the notes for @ref Mtx_PerspStereo 561 | * @param[out] mtx Output matrix 562 | * @param[in] fovy Vertical field of view in radians 563 | * @param[in] aspect Aspect ration of projection plane (width/height) 564 | * @param[in] near Near clip plane (Z=near) 565 | * @param[in] far Far clip plane (Z=far) 566 | * @param[in] iod Interocular distance 567 | * @param[in] screen Focal length 568 | * @param[in] isLeftHanded Whether to build a LH projection 569 | * @sa Mtx_Persp 570 | * @sa Mtx_PerspTilt 571 | * @sa Mtx_PerspStereo 572 | */ 573 | void Mtx_PerspStereoTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded); 574 | 575 | /** 576 | * @brief Look-At matrix, based on DirectX implementation 577 | * @note See https://msdn.microsoft.com/en-us/library/windows/desktop/bb205342 578 | * @param[out] out Output matrix. 579 | * @param[in] cameraPosition Position of the intended camera in 3D space. 580 | * @param[in] cameraTarget Position of the intended target the camera is supposed to face in 3D space. 581 | * @param[in] cameraUpVector The vector that points straight up depending on the camera's "Up" direction. 582 | * @param[in] isLeftHanded Whether to build a LH projection 583 | */ 584 | void Mtx_LookAt(C3D_Mtx* out, C3D_FVec cameraPosition, C3D_FVec cameraTarget, C3D_FVec cameraUpVector, bool isLeftHanded); 585 | /** @} */ 586 | 587 | /** 588 | * @name Quaternion Math 589 | * @{ 590 | */ 591 | 592 | /** 593 | * @brief Create a new Quaternion 594 | * @param[in] i I-component 595 | * @param[in] j J-component 596 | * @param[in] k K-component 597 | * @param[in] r Real component 598 | * @return New Quaternion 599 | */ 600 | #define Quat_New(i,j,k,r) FVec4_New(i,j,k,r) 601 | 602 | /** 603 | * @brief Negate a Quaternion 604 | * @note This is equivalent to `Quat_Scale(v, -1)` 605 | * @param[in] q Quaternion to negate 606 | * @return -q 607 | */ 608 | #define Quat_Negate(q) FVec4_Negate(q) 609 | 610 | /** 611 | * @brief Add two Quaternions 612 | * @param[in] lhs Augend 613 | * @param[in] rhs Addend 614 | * @return lhs+rhs (sum) 615 | */ 616 | #define Quat_Add(lhs,rhs) FVec4_Add(lhs,rhs) 617 | 618 | /** 619 | * @brief Subtract two Quaternions 620 | * @param[in] lhs Minuend 621 | * @param[in] rhs Subtrahend 622 | * @return lhs-rhs (difference) 623 | */ 624 | #define Quat_Subtract(lhs,rhs) FVec4_Subtract(lhs,rhs) 625 | 626 | /** 627 | * @brief Scale a Quaternion 628 | * @param[in] q Quaternion to scale 629 | * @param[in] s Scale factor 630 | * @return q*s 631 | */ 632 | #define Quat_Scale(q,s) FVec4_Scale(q,s) 633 | 634 | /** 635 | * @brief Normalize a Quaternion 636 | * @param[in] q Quaternion to normalize 637 | * @return q/‖q‖ 638 | */ 639 | #define Quat_Normalize(q) FVec4_Normalize(q) 640 | 641 | /** 642 | * @brief Dot product of two Quaternions 643 | * @param[in] lhs Left-side Quaternion 644 | * @param[in] rhs Right-side Quaternion 645 | * @return lhs∙rhs 646 | */ 647 | #define Quat_Dot(lhs,rhs) FVec4_Dot(lhs,rhs) 648 | 649 | /** 650 | * @brief Multiply two Quaternions 651 | * @param[in] lhs Multiplicand 652 | * @param[in] rhs Multiplier 653 | * @return lhs*rhs 654 | */ 655 | C3D_FQuat Quat_Multiply(C3D_FQuat lhs, C3D_FQuat rhs); 656 | 657 | /** 658 | * @brief Raise Quaternion to a power 659 | * @note If p is 0, this returns the identity Quaternion. 660 | * If p is 1, this returns q. 661 | * @param[in] q Base Quaternion 662 | * @param[in] p Power 663 | * @return qp 664 | */ 665 | C3D_FQuat Quat_Pow(C3D_FQuat q, float p); 666 | 667 | /** 668 | * @brief Cross product of Quaternion and FVec3 669 | * @param[in] q Base Quaternion 670 | * @param[in] v Vector to cross 671 | * @return q×v 672 | */ 673 | C3D_FVec Quat_CrossFVec3(C3D_FQuat q, C3D_FVec v); 674 | 675 | /** 676 | * @brief 3D Rotation 677 | * @param[in] q Quaternion to rotate 678 | * @param[in] axis Axis about which to rotate 679 | * @param[in] r Radians to rotate 680 | * @param[in] bRightSide Whether to transform from the right side 681 | * @return Rotated Quaternion 682 | */ 683 | C3D_FQuat Quat_Rotate(C3D_FQuat q, C3D_FVec axis, float r, bool bRightSide); 684 | 685 | /** 686 | * @brief 3D Rotation about the X axis 687 | * @param[in] q Quaternion to rotate 688 | * @param[in] r Radians to rotate 689 | * @param[in] bRightSide Whether to transform from the right side 690 | * @return Rotated Quaternion 691 | */ 692 | C3D_FQuat Quat_RotateX(C3D_FQuat q, float r, bool bRightSide); 693 | 694 | /** 695 | * @brief 3D Rotation about the Y axis 696 | * @param[in] q Quaternion to rotate 697 | * @param[in] r Radians to rotate 698 | * @param[in] bRightSide Whether to transform from the right side 699 | * @return Rotated Quaternion 700 | */ 701 | C3D_FQuat Quat_RotateY(C3D_FQuat q, float r, bool bRightSide); 702 | 703 | /** 704 | * @brief 3D Rotation about the Z axis 705 | * @param[in] q Quaternion to rotate 706 | * @param[in] r Radians to rotate 707 | * @param[in] bRightSide Whether to transform from the right side 708 | * @return Rotated Quaternion 709 | */ 710 | C3D_FQuat Quat_RotateZ(C3D_FQuat q, float r, bool bRightSide); 711 | 712 | /** 713 | * @brief Get Quaternion equivalent to 4x4 matrix 714 | * @note If the matrix is orthogonal or special orthogonal, where determinant(matrix) = +1.0f, then the matrix can be converted. 715 | * @param[in] m Input Matrix 716 | * @return Generated Quaternion 717 | */ 718 | C3D_FQuat Quat_FromMtx(const C3D_Mtx* m); 719 | 720 | /** 721 | * @brief Identity Quaternion 722 | * @return Identity Quaternion 723 | */ 724 | static inline C3D_FQuat Quat_Identity(void) 725 | { 726 | // r=1, i=j=k=0 727 | return Quat_New(0.0f, 0.0f, 0.0f, 1.0f); 728 | } 729 | 730 | /** 731 | * @brief Quaternion conjugate 732 | * @param[in] q Quaternion of which to get conjugate 733 | * @return q* 734 | */ 735 | static inline C3D_FQuat Quat_Conjugate(C3D_FQuat q) 736 | { 737 | // q* = q.r - q.i - q.j - q.k 738 | return Quat_New(-q.i, -q.j, -q.k, q.r); 739 | } 740 | 741 | /** 742 | * @brief Quaternion inverse 743 | * @note This is equivalent to `Quat_Pow(v, -1)` 744 | * @param[in] q Quaternion of which to get inverse 745 | * @return q-1 746 | */ 747 | static inline C3D_FQuat Quat_Inverse(C3D_FQuat q) 748 | { 749 | // q^-1 = (q.r - q.i - q.j - q.k) / (q.r^2 + q.i^2 + q.j^2 + q.k^2) 750 | // = q* / (q∙q) 751 | C3D_FQuat c = Quat_Conjugate(q); 752 | float d = Quat_Dot(q, q); 753 | return Quat_New(c.i/d, c.j/d, c.k/d, c.r/d); 754 | } 755 | 756 | /** 757 | * @brief Cross product of FVec3 and Quaternion 758 | * @param[in] v Base FVec3 759 | * @param[in] q Quaternion to cross 760 | * @return v×q 761 | */ 762 | static inline C3D_FVec FVec3_CrossQuat(C3D_FVec v, C3D_FQuat q) 763 | { 764 | // v×q = (q^-1)×v 765 | return Quat_CrossFVec3(Quat_Inverse(q), v); 766 | } 767 | 768 | /** 769 | * @brief Converting Pitch, Yaw, and Roll to Quaternion equivalent 770 | * @param[in] pitch The pitch angle in radians. 771 | * @param[in] yaw The yaw angle in radians. 772 | * @param[in] roll The roll angle in radians. 773 | * @param[in] bRightSide Whether to transform from the right side 774 | * @return C3D_FQuat The Quaternion equivalent with the pitch, yaw, and roll (in that order) orientations applied. 775 | */ 776 | C3D_FQuat Quat_FromPitchYawRoll(float pitch, float yaw, float roll, bool bRightSide); 777 | 778 | /** 779 | * @brief Quaternion Look-At 780 | * @param[in] source C3D_FVec Starting position. Origin of rotation. 781 | * @param[in] target C3D_FVec Target position to orient towards. 782 | * @param[in] forwardVector C3D_FVec The Up vector. 783 | * @param[in] upVector C3D_FVec The Up vector. 784 | * @return Quaternion rotation. 785 | */ 786 | C3D_FQuat Quat_LookAt(C3D_FVec source, C3D_FVec target, C3D_FVec forwardVector, C3D_FVec upVector); 787 | 788 | /** 789 | * @brief Quaternion, created from a given axis and angle in radians. 790 | * @param[in] axis C3D_FVec The axis to rotate around at. 791 | * @param[in] angle float The angle to rotate. Unit: Radians 792 | * @return Quaternion rotation based on the axis and angle. Axis doesn't have to be orthogonal. 793 | */ 794 | C3D_FQuat Quat_FromAxisAngle(C3D_FVec axis, float angle); 795 | /** @} */ 796 | /** @} */ 797 | --------------------------------------------------------------------------------