commit e7d06322245bb2ce23d97309ad030f131442a16d Author: fincs Date: Sat Dec 20 21:34:19 2014 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a8db49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*~ +*.3dsx +*.elf +*.smdh +Thumbs.db +build/ +lib/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e3c4a8f --- /dev/null +++ b/Makefile @@ -0,0 +1,135 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source \ + source/maths +DATA := data +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp + +CFLAGS := -g -Wall -O2 -mword-relocations \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCITRO3D_BUILD + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) $(DEFINES) + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/lib/lib$(TARGET).a + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +VSHFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.vsh))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) $(VSHFILES:.vsh=.shbin.o) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +lib: + @[ -d $@ ] || mkdir -p $@ + +$(BUILD): lib + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.shbin.o: %.vsh + @echo $(notdir $<) + $(eval CURBIN := $(patsubst %.vsh,%.shbin,$(notdir $<))) + $(eval CURH := $(patsubst %.vsh,%.vsh.h,$(notdir $<))) + @picasso $(CURBIN) $< $(CURH) + @bin2s $(CURBIN) | $(AS) -o $@ + @echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h + @echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h + @echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/include/c3d/attribs.h b/include/c3d/attribs.h new file mode 100644 index 0000000..c3f3e3e --- /dev/null +++ b/include/c3d/attribs.h @@ -0,0 +1,31 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 offset; + u32 flags[2]; +} C3D_AttrBufCfg; + +typedef struct +{ + // Base physical address intentionally left out + // Write to 0x0201 instead of 0x0200 + u32 flags[2]; + C3D_AttrBufCfg buffers[12]; +} C3D_AttrCfg; + +typedef struct +{ + C3D_AttrCfg cfg; + u64 permutation; + int attrCount; + int bufCount; +} C3D_AttrInfo; + +void AttrInfo_Init(C3D_AttrInfo* info); +bool AttrInfo_AddParam(C3D_AttrInfo* info, GPU_FORMATS format, int count); +bool AttrInfo_AddBuffer(C3D_AttrInfo* info, ptrdiff_t offset, ptrdiff_t stride, int attribCount, u64 permutation); + +C3D_AttrInfo* C3D_GetAttrInfo(void); +void C3D_SetAttrInfo(C3D_AttrInfo* info); diff --git a/include/c3d/base.h b/include/c3d/base.h new file mode 100644 index 0000000..b33841a --- /dev/null +++ b/include/c3d/base.h @@ -0,0 +1,22 @@ +#pragma once +#include "types.h" +#include "buffers.h" +#define C3D_DEFAULT_CMDBUF_SIZE 0x40000 + +bool C3D_Init(size_t cmdBufSize); +void C3D_FlushAsync(void); +void C3D_Fini(void); + +void C3D_DrawArray(C3D_VBO* vbo, GPU_Primitive_t primitive); +void C3D_DrawElements(C3D_IBO* ibo, GPU_Primitive_t primitive); + +static inline void C3D_FlushAwait(void) +{ + gspWaitForP3D(); +} + +static inline void C3D_Flush(void) +{ + C3D_FlushAsync(); + C3D_FlushAwait(); +} diff --git a/include/c3d/buffers.h b/include/c3d/buffers.h new file mode 100644 index 0000000..eb3fed3 --- /dev/null +++ b/include/c3d/buffers.h @@ -0,0 +1,35 @@ +#pragma once +#include "types.h" + +typedef struct +{ + void* data; + size_t capacity; + size_t size; + int vertexCount; +} C3D_VBO; + +bool C3D_VBOInit(C3D_VBO* vbo, size_t capacity); +bool C3D_VBOAddData(C3D_VBO* vbo, const void* data, size_t size, int vertexCount); +void C3D_VBOFlush(C3D_VBO* vbo); +void C3D_VBOBind(C3D_VBO* vbo); +void C3D_VBODelete(C3D_VBO* vbo); + +typedef struct +{ + void* data; + int capacity; + int indexCount; + int format; +} C3D_IBO; + +enum +{ + C3D_UNSIGNED_BYTE = 0, + C3D_UNSIGNED_SHORT = 1, +}; + +bool C3D_IBOInit(C3D_IBO* ibo, int capacity, int format); +bool C3D_IBOAddData(C3D_IBO* ibo, const void* data, int indexCount); +void C3D_IBOFlush(C3D_IBO* ibo); +void C3D_IBODelete(C3D_IBO* ibo); diff --git a/include/c3d/effect.h b/include/c3d/effect.h new file mode 100644 index 0000000..7bb2ba2 --- /dev/null +++ b/include/c3d/effect.h @@ -0,0 +1,11 @@ +#pragma once +#include "types.h" + +void C3D_DepthRange(float near, float far); +void C3D_CullFace(GPU_CULLMODE mode); +void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int mask, int replace); +void C3D_StencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass); +void C3D_BlendingColor(u32 color); +void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask); +void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref); +void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha); diff --git a/include/c3d/maths.h b/include/c3d/maths.h new file mode 100644 index 0000000..43af715 --- /dev/null +++ b/include/c3d/maths.h @@ -0,0 +1,73 @@ +#pragma once +#include "types.h" +#include +#include + +// See http://tauday.com/tau-manifesto +//#define M_TAU 6.28318530717958647693 +#define M_TAU (2*M_PI) + +#define C3D_Angle(_angle) ((_angle)*M_TAU) +#define C3D_AspectRatioTop (400.0f / 240.0f) +#define C3D_AspectRatioBot (320.0f / 240.0f) + +static inline float FVec_DP4(const C3D_FVec* a, const C3D_FVec* b) +{ + return a->x*b->x + a->y*b->y + a->z*b->z + a->w*b->w; +} + +static inline float FVec_Mod4(const C3D_FVec* a) +{ + return sqrtf(FVec_DP4(a,a)); +} + +static inline void FVec_Norm4(C3D_FVec* vec) +{ + float m = FVec_Mod4(vec); + vec->x /= m; + vec->y /= m; + vec->z /= m; + vec->w /= m; +} + +static inline float FVec_DP3(const C3D_FVec* a, const C3D_FVec* b) +{ + return a->x*b->x + a->y*b->y + a->z*b->z; +} + +static inline float FVec_Mod3(const C3D_FVec* a) +{ + return sqrtf(FVec_DP3(a,a)); +} + +static inline void FVec_Norm3(C3D_FVec* vec) +{ + float m = FVec_Mod3(vec); + vec->x /= m; + vec->y /= m; + vec->z /= m; + vec->w = 0.0f; +} + +static inline void Mtx_Zeros(C3D_Mtx* out) +{ + memset(out, 0, sizeof(*out)); +} + +static inline void Mtx_Copy(C3D_Mtx* out, const C3D_Mtx* in) +{ + memcpy(out, in, sizeof(*out)); +} + +void Mtx_Identity(C3D_Mtx* out); +void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b); + +void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z); +void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z); +void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide); +void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide); +void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide); + +// Special versions of the projection matrices that take the 3DS' screen orientation into account +void Mtx_OrthoTilt(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far); +void Mtx_PerspTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far); diff --git a/include/c3d/mtxstack.h b/include/c3d/mtxstack.h new file mode 100644 index 0000000..7d42ce9 --- /dev/null +++ b/include/c3d/mtxstack.h @@ -0,0 +1,23 @@ +#pragma once +#include "maths.h" +#define C3D_MTXSTACK_SIZE 8 + +typedef struct +{ + C3D_Mtx m[C3D_MTXSTACK_SIZE]; + int pos; + u8 unifPos, unifLen; + bool isDirty; +} C3D_MtxStack; + +static inline C3D_Mtx* MtxStack_Cur(C3D_MtxStack* stk) +{ + stk->isDirty = true; + return &stk->m[stk->pos]; +} + +void MtxStack_Init(C3D_MtxStack* stk); +void MtxStack_Bind(C3D_MtxStack* stk, int unifPos, int unifLen); +void MtxStack_Push(C3D_MtxStack* stk); +void MtxStack_Pop(C3D_MtxStack* stk); +void MtxStack_Update(C3D_MtxStack* stk); diff --git a/include/c3d/texenv.h b/include/c3d/texenv.h new file mode 100644 index 0000000..5fa0f53 --- /dev/null +++ b/include/c3d/texenv.h @@ -0,0 +1,54 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u16 srcRgb, srcAlpha; + u16 opRgb, opAlpha; + u16 funcRgb, funcAlpha; + u32 color; + u32 unknown; +} C3D_TexEnv; + +enum +{ + C3D_RGB = BIT(0), + C3D_Alpha = BIT(1), + C3D_Both = C3D_RGB | C3D_Alpha, +}; + +void TexEnv_Init(C3D_TexEnv* env); + +C3D_TexEnv* C3D_GetTexEnv(int id); +void C3D_SetTexEnv(int id, C3D_TexEnv* env); + +static inline void C3D_TexEnvSrc(C3D_TexEnv* env, int mode, int s1, int s2, int s3) +{ + int param = GPU_TEVSOURCES(s1, s2, s3); + if (mode & C3D_RGB) + env->srcRgb = param; + if (mode & C3D_Alpha) + env->srcAlpha = param; +} + +static inline void C3D_TexEnvOp(C3D_TexEnv* env, int mode, int o1, int o2, int o3) +{ + int param = GPU_TEVOPERANDS(o1, o2, o3); + if (mode & C3D_RGB) + env->opRgb = param; + if (mode & C3D_Alpha) + env->opAlpha = param; +} + +static inline void C3D_TexEnvFunc(C3D_TexEnv* env, int mode, int param) +{ + if (mode & C3D_RGB) + env->funcRgb = param; + if (mode & C3D_Alpha) + env->funcAlpha = param; +} + +static inline void C3D_TexEnvColor(C3D_TexEnv* env, u32 color) +{ + env->color = color; +} diff --git a/include/c3d/texture.h b/include/c3d/texture.h new file mode 100644 index 0000000..139d720 --- /dev/null +++ b/include/c3d/texture.h @@ -0,0 +1,20 @@ +#pragma once +#include "types.h" + +typedef struct +{ + void* data; + size_t size; + + u16 width, height; + u32 param; + GPU_TEXCOLOR fmt; +} C3D_Tex; + +bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); +void C3D_TexUpload(C3D_Tex* tex, const void* data); +void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter); +void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT); +void C3D_TexBind(int unitId, C3D_Tex* tex); +void C3D_TexFlush(C3D_Tex* tex); +void C3D_TexDelete(C3D_Tex* tex); diff --git a/include/c3d/types.h b/include/c3d/types.h new file mode 100644 index 0000000..6575056 --- /dev/null +++ b/include/c3d/types.h @@ -0,0 +1,28 @@ +#pragma once +#include <3ds.h> + +typedef u32 C3D_IVec; + +typedef struct +{ + union + { + struct { float w, z, y, x; }; + float c[4]; + }; +} C3D_FVec; + +// Row-major 4x4 matrix +typedef struct +{ + union + { + C3D_FVec r[4]; // Rows are vectors + float m[4*4]; + }; +} C3D_Mtx; + +static inline C3D_IVec IVec_Pack(u8 x, u8 y, u8 z, u8 w) +{ + return (u32)x | ((u32)y << 8) | ((u32)z << 16) | ((u32)w << 24); +} diff --git a/include/c3d/uniforms.h b/include/c3d/uniforms.h new file mode 100644 index 0000000..0cc9cf4 --- /dev/null +++ b/include/c3d/uniforms.h @@ -0,0 +1,43 @@ +#pragma once +#include "types.h" +#define C3D_FVUNIF_COUNT 96 +#define C3D_IVUNIF_COUNT 4 + +extern C3D_FVec C3D_FVUnif[C3D_FVUNIF_COUNT]; +extern C3D_IVec C3D_IVUnif[C3D_IVUNIF_COUNT]; +extern u16 C3D_BoolUnifs; + +extern bool C3D_FVUnifDirty[C3D_FVUNIF_COUNT]; +extern bool C3D_IVUnifDirty[C3D_IVUNIF_COUNT]; +extern bool C3D_BoolUnifsDirty; + +static inline C3D_FVec* C3D_FVUnifWritePtr(int id, int size) +{ + int i; + for (i = 0; i < size; i ++) + C3D_FVUnifDirty[id+i] = true; + return &C3D_FVUnif[id]; +} + +static inline C3D_IVec* C3D_IVUnifWritePtr(int id) +{ + C3D_IVUnifDirty[id] = true; + return &C3D_IVUnif[id]; +} + +static inline void C3D_FVUnifSet(int id, float x, float y, float z, float w) +{ + C3D_FVec* ptr = C3D_FVUnifWritePtr(id, 1); + ptr->x = x; + ptr->y = y; + ptr->z = z; + ptr->w = w; +} + +static inline void C3D_IVUnifSet(int id, int x, int y, int z, int w) +{ + C3D_IVec* ptr = C3D_IVUnifWritePtr(id); + *ptr = IVec_Pack(x, y, z, w); +} + +void C3D_UpdateUniforms(void); diff --git a/include/citro3d.h b/include/citro3d.h new file mode 100644 index 0000000..34c73a0 --- /dev/null +++ b/include/citro3d.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef CITRO3D_BUILD +#error "This header file is only for external users of citro3d." +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "c3d/types.h" +#include "c3d/maths.h" +#include "c3d/mtxstack.h" + +#include "c3d/uniforms.h" +#include "c3d/attribs.h" +#include "c3d/buffers.h" +#include "c3d/base.h" + +#include "c3d/texenv.h" +#include "c3d/effect.h" +#include "c3d/texture.h" + +#ifdef __cplusplus +} +#endif diff --git a/source/attribs.c b/source/attribs.c new file mode 100644 index 0000000..c66c5df --- /dev/null +++ b/source/attribs.c @@ -0,0 +1,65 @@ +#include +#include +#include "context.h" + +void AttrInfo_Init(C3D_AttrInfo* info) +{ + memset(info, 0, sizeof(*info)); + info->cfg.flags[1] = 0xFFF << 16; +} + +bool AttrInfo_AddParam(C3D_AttrInfo* info, GPU_FORMATS format, int count) +{ + if (info->attrCount == 12) return false; + int id = info->attrCount++; + if (id < 8) + info->cfg.flags[0] |= GPU_ATTRIBFMT(id, count, format); + else + info->cfg.flags[1] |= GPU_ATTRIBFMT(id-8, count, format); + info->cfg.flags[1] &= ~BIT(id+16); + info->cfg.flags[1] = (info->cfg.flags[1] &~ 0xF0000000) | (id << 28); + info->permutation |= id << (id*4); + return true; +} + +bool AttrInfo_AddBuffer(C3D_AttrInfo* info, ptrdiff_t offset, ptrdiff_t stride, int attribCount, u64 permutation) +{ + if (info->bufCount == 12) return false; + int id = info->bufCount++; + C3D_AttrBufCfg* buf = &info->cfg.buffers[id]; + buf->offset = offset; + buf->flags[0] = permutation & 0xFFFFFFFF; + buf->flags[1] = (permutation >> 32) | (stride << 16) | (attribCount << 28); + return true; +} + +C3D_AttrInfo* C3D_GetAttrInfo(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return NULL; + + ctx->flags |= C3DiF_AttrBuf; + return &ctx->attrInfo; +} + +void C3D_SetAttrInfo(C3D_AttrInfo* info) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + memcpy(&ctx->attrInfo, info, sizeof(*info)); + ctx->flags |= C3DiF_AttrBuf; +} + +void C3Di_AttrInfoBind(C3D_AttrInfo* info) +{ + GPUCMD_Add(0x800F0201, (u32*)&info->cfg, sizeof(C3D_AttrCfg)/sizeof(u32)); + GPUCMD_AddSingleParam(0x000B02B9, 0xA0000000 | (info->attrCount - 1)); + GPUCMD_AddSingleParam(0x000F0242, info->attrCount - 1); + GPUCMD_AddSingleParam(0x000F02BB, info->permutation & 0xFFFFFFFF); + GPUCMD_AddSingleParam(0x000F02BC, info->permutation >> 32); +} diff --git a/source/base.c b/source/base.c new file mode 100644 index 0000000..fcf584f --- /dev/null +++ b/source/base.c @@ -0,0 +1,167 @@ +#include "context.h" + +C3D_Context __C3D_Context; + +u32 C3Di_Float24(float f) +{ + if (!f) return 0; + union { float t; u32 v; } u; + u.t = f; + u32 s = u.v >> 31; + u32 exp = ((u.v >> 23) & 0xFF) - 0x40; + u32 man = (u.v >> 7) & 0xFFFF; + + return (exp >= 0) ? (man | (exp << 16) | (s << 23)) : (s << 23); +} + +static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) +{ + u32 reg[4]; + reg[0] = tex->fmt; + reg[1] = osConvertVirtToPhys((u32)tex->data) >> 3; + reg[2] = (u32)tex->width | ((u32)tex->height << 16); + reg[3] = tex->param; + + switch (unit) + { + case GPU_TEXUNIT0: + GPUCMD_AddSingleParam(0x000F008E, reg[0]); + GPUCMD_AddSingleParam(0x000F0085, reg[1]); + GPUCMD_AddSingleParam(0x000F0082, reg[2]); + GPUCMD_AddSingleParam(0x000F0083, reg[3]); + break; + case GPU_TEXUNIT1: + GPUCMD_AddSingleParam(0x000F0096, reg[0]); + GPUCMD_AddSingleParam(0x000F0095, reg[1]); + GPUCMD_AddSingleParam(0x000F0092, reg[2]); + GPUCMD_AddSingleParam(0x000F0093, reg[3]); + break; + case GPU_TEXUNIT2: + GPUCMD_AddSingleParam(0x000F009E, reg[0]); + GPUCMD_AddSingleParam(0x000F009D, reg[1]); + GPUCMD_AddSingleParam(0x000F009A, reg[2]); + GPUCMD_AddSingleParam(0x000F009B, reg[3]); + break; + } +} + +bool C3D_Init(size_t cmdBufSize) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_Active) + return false; + + ctx->cmdBufSize = cmdBufSize; + ctx->cmdBuf = linearAlloc(cmdBufSize); + if (!ctx->cmdBuf) return false; + + GPU_Reset(NULL, ctx->cmdBuf, ctx->cmdBufSize); + GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0); + + ctx->flags = C3DiF_Active | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexAll; + + // TODO: replace with direct struct access + C3D_DepthRange(-1.0f, 0.0f); + C3D_CullFace(GPU_CULL_BACK_CCW); + C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); + C3D_StencilOp(GPU_KEEP, GPU_KEEP, GPU_KEEP); + C3D_BlendingColor(0); + C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); + C3D_AlphaTest(false, GPU_ALWAYS, 0x00); + C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + + for (i = 0; i < 3; i ++) + ctx->tex[i] = NULL; + + for (i = 0; i < 6; i ++) + TexEnv_Init(&ctx->texEnv[i]); + + return true; +} + +void C3Di_UpdateContext(void) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_NeedFinishDrawing) + { + ctx->flags &= ~C3DiF_NeedFinishDrawing; + //GPU_FinishDrawing(); + } + + if (ctx->flags & C3DiF_AttrBuf) + { + ctx->flags &= ~C3DiF_AttrBuf; + C3Di_AttrInfoBind(&ctx->attrInfo); + } + + if (ctx->flags & C3DiF_Effect) + { + ctx->flags &= ~C3DiF_Effect; + C3Di_EffectBind(&ctx->effect); + } + + if (ctx->flags & C3DiF_TexAll) + { + GPU_TEXUNIT units = 0; + + for (i = 0; i < 3; i ++) + { + static const u8 parm[] = { GPU_TEXUNIT0, GPU_TEXUNIT1, GPU_TEXUNIT2 }; + + if (ctx->tex[i]) + { + units |= parm[i]; + if (ctx->flags & C3DiF_Tex(i)) + C3Di_SetTex(parm[i], ctx->tex[i]); + } + } + + ctx->flags &= ~C3DiF_TexAll; + GPU_SetTextureEnable(units); + } + + if (ctx->flags & C3DiF_TexEnvAll) + { + for (i = 0; i < 6; i ++) + { + if (!(ctx->flags & C3DiF_TexEnv(i))) continue; + C3Di_TexEnvBind(i, &ctx->texEnv[i]); + } + ctx->flags &= ~C3DiF_TexEnvAll; + } + + C3D_UpdateUniforms(); +} + +void C3D_FlushAsync(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (ctx->flags & C3DiF_NeedFinishDrawing) + { + ctx->flags &= ~C3DiF_NeedFinishDrawing; + GPU_FinishDrawing(); + } + + GPUCMD_Finalize(); + GPUCMD_FlushAndRun(NULL); + GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0); +} + +void C3D_Fini(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + linearFree(ctx->cmdBuf); + ctx->flags = 0; +} diff --git a/source/context.h b/source/context.h new file mode 100644 index 0000000..99df6a8 --- /dev/null +++ b/source/context.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + u32 drNear, drFar; + GPU_CULLMODE cullMode; + + u32 alphaTest; + u32 stencilMode, stencilOp; + u32 depthTest; + + u32 blendClr; + u32 alphaBlend; +} C3D_Effect; + +typedef struct +{ + void* cmdBuf; + size_t cmdBufSize; + + u32 flags; + + u32 vboPos; + C3D_AttrInfo attrInfo; + C3D_Effect effect; + + C3D_Tex* tex[3]; + C3D_TexEnv texEnv[6]; + +} C3D_Context; + +enum +{ + C3DiF_Active = BIT(0), + C3DiF_NeedFinishDrawing = BIT(1), + C3DiF_AttrBuf = BIT(2), + C3DiF_Effect = BIT(3), + +#define C3DiF_Tex(n) BIT(23+(n)) + C3DiF_TexAll = 7 << 23, +#define C3DiF_TexEnv(n) BIT(26+(n)) + C3DiF_TexEnvAll = 0x3F << 26, +}; + +static inline C3D_Context* C3Di_GetContext(void) +{ + extern C3D_Context __C3D_Context; + return &__C3D_Context; +} + +u32 C3Di_Float24(float f); +void C3Di_UpdateContext(void); +void C3Di_IBOBind(C3D_IBO* ibo); +void C3Di_AttrInfoBind(C3D_AttrInfo* info); +void C3Di_TexEnvBind(int id, C3D_TexEnv* env); +void C3Di_EffectBind(C3D_Effect* effect); diff --git a/source/drawArray.c b/source/drawArray.c new file mode 100644 index 0000000..10188e2 --- /dev/null +++ b/source/drawArray.c @@ -0,0 +1,25 @@ +#include "context.h" + +void C3D_DrawArray(C3D_VBO* vbo, GPU_Primitive_t primitive) +{ + C3D_VBOBind(vbo); + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddSingleParam(0x0002025E, primitive); + GPUCMD_AddSingleParam(0x0002025F, 0x00000001); + // The index buffer is not used, but 0x000F0227 is still required + GPUCMD_AddSingleParam(0x000F0227, 0x80000000); + // Number of vertices + GPUCMD_AddSingleParam(0x000F0228, vbo->vertexCount); + + // Unknown commands + GPUCMD_AddSingleParam(0x00010253, 0x00000001); + + GPUCMD_AddSingleParam(0x00010245, 0x00000000); + GPUCMD_AddSingleParam(0x000F022E, 0x00000001); + GPUCMD_AddSingleParam(0x00010245, 0x00000001); + GPUCMD_AddSingleParam(0x000F0231, 0x00000001); + + C3Di_GetContext()->flags |= C3DiF_NeedFinishDrawing; +} diff --git a/source/drawElements.c b/source/drawElements.c new file mode 100644 index 0000000..99596e0 --- /dev/null +++ b/source/drawElements.c @@ -0,0 +1,25 @@ +#include "context.h" + +void C3D_DrawElements(C3D_IBO* ibo, GPU_Primitive_t primitive) +{ + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddSingleParam(0x0002025E, primitive); + GPUCMD_AddSingleParam(0x0002025F, 0x00000001); + // Bind the IBO + C3Di_IBOBind(ibo); + // Number of vertices + GPUCMD_AddSingleParam(0x000F0228, ibo->indexCount); + + // Unknown commands + GPUCMD_AddSingleParam(0x00020229, 0x00000100); + GPUCMD_AddSingleParam(0x00020253, 0x00000100); + + GPUCMD_AddSingleParam(0x00010245, 0x00000000); + GPUCMD_AddSingleParam(0x000F022F, 0x00000001); + GPUCMD_AddSingleParam(0x00010245, 0x00000001); + GPUCMD_AddSingleParam(0x000F0231, 0x00000001); + + C3Di_GetContext()->flags |= C3DiF_NeedFinishDrawing; +} diff --git a/source/effect.c b/source/effect.c new file mode 100644 index 0000000..5049c91 --- /dev/null +++ b/source/effect.c @@ -0,0 +1,72 @@ +#include "context.h" + +static inline C3D_Effect* getEffect() +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Effect; + return &ctx->effect; +} + +void C3D_DepthRange(float near, float far) +{ + C3D_Effect* e = getEffect(); + e->drNear = C3Di_Float24(near); + e->drFar = C3Di_Float24(far); +} + +void C3D_CullFace(GPU_CULLMODE mode) +{ + C3D_Effect* e = getEffect(); + e->cullMode = mode; +} + +void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int mask, int replace) +{ + C3D_Effect* e = getEffect(); + e->stencilMode = (!!enable) | ((function & 7) << 4) | (replace << 8) | (ref << 16) | (mask << 24); +} + +void C3D_StencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass) +{ + C3D_Effect* e = getEffect(); + e->stencilOp = sfail | (dfail << 4) | (pass << 8); +} + +void C3D_BlendingColor(u32 color) +{ + C3D_Effect* e = getEffect(); + e->blendClr = color; +} + +void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask) +{ + C3D_Effect* e = getEffect(); + e->depthTest = (!!enable) | ((function & 7) << 4) | (writemask << 8); +} + +void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref) +{ + C3D_Effect* e = getEffect(); + e->alphaTest = (!!enable) | ((function & 7) << 4) | (ref << 8); +} + +void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha) +{ + C3D_Effect* e = getEffect(); + e->alphaBlend = colorEq | (alphaEq << 8) | (srcClr << 16) | (dstClr << 20) | (srcAlpha << 24) | (dstAlpha << 28); +} + +void C3Di_EffectBind(C3D_Effect* e) +{ + GPUCMD_AddSingleParam(0x000F006D, 0x00000001); + GPUCMD_AddSingleParam(0x000F0040, e->cullMode & 0x3); + GPUCMD_Add(0x800F004D, (u32*)&e->drNear, 2); + GPUCMD_Add(0x800F0104, (u32*)&e->alphaTest, 4); + GPUCMD_AddSingleParam(0x000F0103, e->blendClr); + GPUCMD_AddSingleParam(0x000F0101, e->alphaBlend); + GPUCMD_AddSingleParam(0x00020100, 0x00000100); + + // Wat + GPUCMD_AddSingleParam(0x00010062, 0); + GPUCMD_AddSingleParam(0x000F0118, 0); +} diff --git a/source/ibo.c b/source/ibo.c new file mode 100644 index 0000000..fff4a18 --- /dev/null +++ b/source/ibo.c @@ -0,0 +1,47 @@ +#include +#include +#include "context.h" + +bool C3D_IBOCreate(C3D_IBO* ibo, int capacity, int format) +{ + if (ibo->data) return false; + + ibo->data = linearAlloc(capacity * (format+1)); + if (!ibo->data) return false; + ibo->capacity = capacity; + ibo->indexCount = 0; + ibo->format = format; + return true; +} + +bool C3D_IBOAddData(C3D_IBO* ibo, const void* data, int indexCount) +{ + int remaining = ibo->capacity - ibo->indexCount; + if (remaining < indexCount) return false; + int stride = ibo->format+1; + memcpy((u8*)ibo->data + ibo->indexCount*stride, data, indexCount*stride); + ibo->indexCount += indexCount; + return true; +} + +void C3D_IBOFlush(C3D_IBO* ibo) +{ + int stride = ibo->format+1; + GSPGPU_FlushDataCache(NULL, ibo->data, ibo->indexCount*stride); +} + +void C3Di_IBOBind(C3D_IBO* ibo) +{ + C3D_Context* ctx = C3Di_GetContext(); + + u32 pa = osConvertVirtToPhys((u32)ibo->data); + u32 offset = (pa - ctx->vboPos) &~ BIT(31); + GPUCMD_AddSingleParam(0x000F0227, offset | (ibo->format << 31)); +} + +void C3D_IBODelete(C3D_IBO* ibo) +{ + if (!ibo->data) return; + linearFree(ibo->data); + ibo->data = NULL; +} diff --git a/source/maths/mtx_identity.c b/source/maths/mtx_identity.c new file mode 100644 index 0000000..08aef16 --- /dev/null +++ b/source/maths/mtx_identity.c @@ -0,0 +1,7 @@ +#include + +void Mtx_Identity(C3D_Mtx* out) +{ + Mtx_Zeros(out); + out->r[0].x = out->r[1].y = out->r[2].z = out->r[3].w = 1.0f; +} diff --git a/source/maths/mtx_multiply.c b/source/maths/mtx_multiply.c new file mode 100644 index 0000000..2804bb1 --- /dev/null +++ b/source/maths/mtx_multiply.c @@ -0,0 +1,9 @@ +#include + +void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b) +{ + int i, j; + for (i = 0; i < 4; i ++) + for (j = 0; j < 4; j ++) + 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]; +} diff --git a/source/maths/mtx_orthotilt.c b/source/maths/mtx_orthotilt.c new file mode 100644 index 0000000..cc23cd3 --- /dev/null +++ b/source/maths/mtx_orthotilt.c @@ -0,0 +1,30 @@ +#include + +void Mtx_OrthoTilt(C3D_Mtx* mtx, float top, float bottom, float left, float right, float near, float far) +{ + // Notes: + // The 3DS screens are sideways and so are the top/bottom/left/right parameters + // that the caller passed. Hence why the parameter names are swapped in the implementation. + + C3D_Mtx mp; + Mtx_Zeros(&mp); + + // Build standard orthogonal projection matrix + mp.r[0].x = 2.0f / (right - left); + mp.r[0].w = (left + right) / (left - right); + mp.r[1].y = 2.0f / (top - bottom); + mp.r[1].w = (bottom + top) / (bottom - top); + mp.r[2].z = 2.0f / (far - near); + mp.r[2].w = (near + far) / (near - far); + mp.r[3].w = 1.0f; + + // Fix depth range to [-1, 0] + C3D_Mtx mp2; + Mtx_Identity(&mp2); + mp2.r[2].z = 0.5; + mp2.r[2].w = -0.5; + Mtx_Multiply(mtx, &mp2, &mp); + + // Rotate the matrix one quarter of a turn CCW in order to fix the 3DS screens' orientation + Mtx_RotateZ(mtx, M_TAU/4, true); +} diff --git a/source/maths/mtx_persptilt.c b/source/maths/mtx_persptilt.c new file mode 100644 index 0000000..208f625 --- /dev/null +++ b/source/maths/mtx_persptilt.c @@ -0,0 +1,44 @@ +#include + +void Mtx_PerspTilt(C3D_Mtx* mtx, float fovx, float invaspect, float near, float far) +{ + // Notes: + // We are passed "fovy" and the "aspect ratio". However, the 3DS screens are sideways, + // and so are these parameters -- in fact, they are actually the fovx and the inverse + // of the aspect ratio. Therefore the formula for the perspective projection matrix + // had to be modified to be expressed in these terms instead. + + // Notes: + // fovx = 2 atan(tan(fovy/2)*w/h) + // fovy = 2 atan(tan(fovx/2)*h/w) + // invaspect = h/w + + // a0,0 = h / (w*tan(fovy/2)) = + // = h / (w*tan(2 atan(tan(fovx/2)*h/w) / 2)) = + // = h / (w*tan( atan(tan(fovx/2)*h/w) )) = + // = h / (w * tan(fovx/2)*h/w) = + // = 1 / tan(fovx/2) + + // a1,1 = 1 / tan(fovy/2) = (...) = w / (h*tan(fovx/2)) + + float fovx_tan = tanf(fovx/2); + C3D_Mtx mp; + Mtx_Zeros(&mp); + + // Build standard perspective projection matrix + mp.r[0].x = 1.0f / fovx_tan; + mp.r[1].y = 1.0f / (fovx_tan*invaspect); + mp.r[2].z = (near + far) / (near - far); + mp.r[2].w = (2 * near * far) / (near - far); + mp.r[3].z = -1.0f; + + // Fix depth range to [-1, 0] + C3D_Mtx mp2; + Mtx_Identity(&mp2); + mp2.r[2].z = 0.5; + mp2.r[2].w = -0.5; + Mtx_Multiply(mtx, &mp2, &mp); + + // Rotate the matrix one quarter of a turn CCW in order to fix the 3DS screens' orientation + Mtx_RotateZ(mtx, M_TAU/4, true); +} diff --git a/source/maths/mtx_rotatex.c b/source/maths/mtx_rotatex.c new file mode 100644 index 0000000..ca18aea --- /dev/null +++ b/source/maths/mtx_rotatex.c @@ -0,0 +1,21 @@ +#include + +void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + C3D_Mtx rm, om; + + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + + Mtx_Zeros(&rm); + rm.r[0].x = 1.0f; + rm.r[1].y = cosAngle; + rm.r[1].z = sinAngle; + rm.r[2].y = -sinAngle; + rm.r[2].z = cosAngle; + rm.r[3].w = 1.0f; + + if (bRightSide) Mtx_Multiply(&om, mtx, &rm); + else Mtx_Multiply(&om, &rm, mtx); + Mtx_Copy(mtx, &om); +} diff --git a/source/maths/mtx_rotatey.c b/source/maths/mtx_rotatey.c new file mode 100644 index 0000000..2f7d07b --- /dev/null +++ b/source/maths/mtx_rotatey.c @@ -0,0 +1,21 @@ +#include + +void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + C3D_Mtx rm, om; + + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + + Mtx_Zeros(&rm); + rm.r[0].x = cosAngle; + rm.r[0].z = sinAngle; + rm.r[1].y = 1.0f; + rm.r[2].x = -sinAngle; + rm.r[2].z = cosAngle; + rm.r[3].w = 1.0f; + + if (bRightSide) Mtx_Multiply(&om, mtx, &rm); + else Mtx_Multiply(&om, &rm, mtx); + Mtx_Copy(mtx, &om); +} diff --git a/source/maths/mtx_rotatez.c b/source/maths/mtx_rotatez.c new file mode 100644 index 0000000..59ca617 --- /dev/null +++ b/source/maths/mtx_rotatez.c @@ -0,0 +1,21 @@ +#include + +void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + C3D_Mtx rm, om; + + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + + Mtx_Zeros(&rm); + rm.r[0].x = cosAngle; + rm.r[0].y = sinAngle; + rm.r[1].x = -sinAngle; + rm.r[1].y = cosAngle; + rm.r[2].z = 1.0f; + rm.r[3].w = 1.0f; + + if (bRightSide) Mtx_Multiply(&om, mtx, &rm); + else Mtx_Multiply(&om, &rm, mtx); + Mtx_Copy(mtx, &om); +} diff --git a/source/maths/mtx_scale.c b/source/maths/mtx_scale.c new file mode 100644 index 0000000..33d8872 --- /dev/null +++ b/source/maths/mtx_scale.c @@ -0,0 +1,12 @@ +#include + +void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z) +{ + int i; + for (i = 0; i < 4; i ++) + { + mtx->r[i].x *= x; + mtx->r[i].y *= y; + mtx->r[i].z *= z; + } +} diff --git a/source/maths/mtx_translate.c b/source/maths/mtx_translate.c new file mode 100644 index 0000000..3f04302 --- /dev/null +++ b/source/maths/mtx_translate.c @@ -0,0 +1,14 @@ +#include + +void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z) +{ + C3D_Mtx tm, om; + + Mtx_Identity(&tm); + tm.r[0].w = x; + tm.r[1].w = y; + tm.r[2].w = z; + + Mtx_Multiply(&om, &tm, mtx); + Mtx_Copy(mtx, &om); +} diff --git a/source/mtxstack.c b/source/mtxstack.c new file mode 100644 index 0000000..8acabaf --- /dev/null +++ b/source/mtxstack.c @@ -0,0 +1,44 @@ +#include +#include + +void MtxStack_Init(C3D_MtxStack* stk) +{ + stk->pos = 0; + stk->unifPos = 0xFF; + stk->isDirty = true; + Mtx_Identity(&stk->m[0]); +} + +void MtxStack_Bind(C3D_MtxStack* stk, int unifPos, int unifLen) +{ + stk->unifPos = unifPos; + stk->unifLen = unifLen; + stk->isDirty = true; +} + +void MtxStack_Push(C3D_MtxStack* stk) +{ + if (stk->pos == (C3D_MTXSTACK_SIZE-1)) return; + stk->pos ++; + Mtx_Copy(&stk->m[stk->pos], &stk->m[stk->pos-1]); +} + +void MtxStack_Pop(C3D_MtxStack* stk) +{ + if (stk->pos == 0) return; + stk->pos --; + stk->isDirty = true; +} + +void MtxStack_Update(C3D_MtxStack* stk) +{ + if (!stk->isDirty) return; + + if (stk->unifPos != 0xFF) + { + C3D_FVec* out = C3D_FVUnifWritePtr(stk->unifPos, stk->unifLen); + memcpy(out, &stk->m[stk->pos], (u32)stk->unifLen * sizeof(C3D_FVec)); + } + + stk->isDirty = false; +} diff --git a/source/texenv.c b/source/texenv.c new file mode 100644 index 0000000..a40fb14 --- /dev/null +++ b/source/texenv.c @@ -0,0 +1,43 @@ +#include +#include +#include "context.h" + +void TexEnv_Init(C3D_TexEnv* env) +{ + env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0); + env->srcAlpha = env->srcRgb; + env->opRgb = GPU_TEVOPERANDS(0,0,0); + env->opAlpha = env->opRgb; + env->funcRgb = GPU_REPLACE; + env->funcAlpha = env->funcRgb; + env->color = 0xFFFFFFFF; + env->unknown = 0; +} + +C3D_TexEnv* C3D_GetTexEnv(int id) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return NULL; + + ctx->flags |= C3DiF_TexEnv(id); + return &ctx->texEnv[id]; +} + +void C3D_SetTexEnv(int id, C3D_TexEnv* env) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + memcpy(&ctx->texEnv[id], env, sizeof(*env)); + ctx->flags |= C3DiF_TexEnv(id); +} + +void C3Di_TexEnvBind(int id, C3D_TexEnv* env) +{ + if (id >= 4) id += 2; + GPUCMD_Add(0x800F00C0 + id*8, (u32*)env, sizeof(C3D_TexEnv)/sizeof(u32)); +} diff --git a/source/texture.c b/source/texture.c new file mode 100644 index 0000000..f0c3abc --- /dev/null +++ b/source/texture.c @@ -0,0 +1,80 @@ +#include "context.h" +#include + +static inline size_t fmtSize(GPU_TEXCOLOR fmt) +{ + switch (fmt) + { + case GPU_RGBA8: + return 4; + case GPU_RGB8: + return 3; + case GPU_RGBA5551: + case GPU_RGB565: + case GPU_RGBA4: + return 2; + default: + return 0; + } +} + +bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format) +{ + if (tex->data) return false; + + tex->size = fmtSize(format); + if (!tex->size) return false; + tex->size *= width * height; + + tex->data = linearMemAlign(tex->size, 0x80); + if (!tex->data) return false; + + tex->width = width; + tex->height = height; + tex->param = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST); + tex->fmt = format; + return true; +} + +void C3D_TexUpload(C3D_Tex* tex, const void* data) +{ + if (tex->data) + memcpy(tex->data, data, tex->size); +} + +void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) +{ + tex->param &= ~(GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)); + tex->param |= GPU_TEXTURE_MAG_FILTER(magFilter) | GPU_TEXTURE_MIN_FILTER(minFilter); +} + +void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT) +{ + tex->param &= ~(GPU_TEXTURE_WRAP_S(3) | GPU_TEXTURE_WRAP_T(3)); + tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT); +} + +void C3D_TexBind(int unitId, C3D_Tex* tex) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->flags |= C3DiF_Tex(unitId); + ctx->tex[unitId] = tex; +} + +void C3D_TexFlush(C3D_Tex* tex) +{ + if (tex->data) + GSPGPU_FlushDataCache(NULL, tex->data, tex->size); +} + +void C3D_TexDelete(C3D_Tex* tex) +{ + if (!tex->data) return; + + linearFree(tex->data); + tex->data = NULL; +} diff --git a/source/uniforms.c b/source/uniforms.c new file mode 100644 index 0000000..3afea6b --- /dev/null +++ b/source/uniforms.c @@ -0,0 +1,63 @@ +#include +//#include + +C3D_FVec C3D_FVUnif[C3D_FVUNIF_COUNT]; +C3D_IVec C3D_IVUnif[C3D_IVUNIF_COUNT]; +u16 C3D_BoolUnifs; + +bool C3D_FVUnifDirty[C3D_FVUNIF_COUNT]; +bool C3D_IVUnifDirty[C3D_IVUNIF_COUNT]; +bool C3D_BoolUnifsDirty; + +void C3D_UpdateUniforms(void) +{ + // Update FVec uniforms + int i = 0; + while (i < C3D_FVUNIF_COUNT) + { + if (!C3D_FVUnifDirty[i]) + { + i ++; + continue; + } + + // Find the number of consecutive dirty uniforms + int j; + for (j = i; j < C3D_FVUNIF_COUNT && C3D_FVUnifDirty[j]; j ++); + + /* + consoleClear(); + printf("FVEC Uniform %02X--%02X\n", i, j-1); + int pp; + for (pp = i; pp < j; pp ++) + printf("%02X: (%f, %f, %f, %f)\n", pp, C3D_FVUnif[pp].x, C3D_FVUnif[pp].y, C3D_FVUnif[pp].z, C3D_FVUnif[pp].w); + */ + + // Upload the uniforms + GPU_SetUniform(i, (u32*)&C3D_FVUnif[i], j-i); + + // Clear the dirty flag + int k; + for (k = i; k < j; k ++) + C3D_FVUnifDirty[k] = false; + + // Advance + i += j; + } + + // Update IVec uniforms + for (i = 0; i < C3D_IVUNIF_COUNT; i ++) + { + if (!C3D_IVUnifDirty[i]) continue; + + GPUCMD_AddSingleParam(0x000F02B1+i, C3D_IVUnif[i]); + C3D_IVUnifDirty[i] = false; + } + + // Update bool uniforms + if (C3D_BoolUnifsDirty) + { + GPUCMD_AddSingleParam(0x000F02B0, 0x7FFF0000 | (u32)C3D_BoolUnifs); + C3D_BoolUnifsDirty = false; + } +} diff --git a/source/vbo.c b/source/vbo.c new file mode 100644 index 0000000..eb2b7d0 --- /dev/null +++ b/source/vbo.c @@ -0,0 +1,49 @@ +#include +#include +#include "context.h" + +bool C3D_VBOInit(C3D_VBO* vbo, size_t capacity) +{ + if (vbo->data) return false; + + vbo->data = linearAlloc(capacity); + if (!vbo->data) return false; + vbo->capacity = capacity; + vbo->size = 0; + vbo->vertexCount = 0; + return true; +} + +bool C3D_VBOAddData(C3D_VBO* vbo, const void* data, size_t size, int vertexCount) +{ + size_t remaining = vbo->capacity - vbo->size; + if (remaining < size) return false; + memcpy((u8*)vbo->data + vbo->size, data, size); + vbo->size += size; + vbo->vertexCount += vertexCount; + return true; +} + +void C3D_VBOFlush(C3D_VBO* vbo) +{ + if (vbo->data) + GSPGPU_FlushDataCache(NULL, vbo->data, vbo->size); +} + +void C3D_VBOBind(C3D_VBO* vbo) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->vboPos = osConvertVirtToPhys((u32)vbo->data); + GPUCMD_AddSingleParam(0x000F0200, ctx->vboPos >> 3); +} + +void C3D_VBODelete(C3D_VBO* vbo) +{ + if (!vbo->data) return; + linearFree(vbo->data); + vbo->data = NULL; +}