Initial commit

This commit is contained in:
fincs 2014-12-20 21:34:19 +01:00
commit e7d0632224
34 changed files with 1430 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*~
*.3dsx
*.elf
*.smdh
Thumbs.db
build/
lib/

135
Makefile Normal file
View File

@ -0,0 +1,135 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
#---------------------------------------------------------------------------------------

31
include/c3d/attribs.h Normal file
View File

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

22
include/c3d/base.h Normal file
View File

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

35
include/c3d/buffers.h Normal file
View File

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

11
include/c3d/effect.h Normal file
View File

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

73
include/c3d/maths.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include "types.h"
#include <string.h>
#include <math.h>
// 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);

23
include/c3d/mtxstack.h Normal file
View File

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

54
include/c3d/texenv.h Normal file
View File

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

20
include/c3d/texture.h Normal file
View File

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

28
include/c3d/types.h Normal file
View File

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

43
include/c3d/uniforms.h Normal file
View File

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

26
include/citro3d.h Normal file
View File

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

65
source/attribs.c Normal file
View File

@ -0,0 +1,65 @@
#include <c3d/attribs.h>
#include <string.h>
#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);
}

167
source/base.c Normal file
View File

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

63
source/context.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <c3d/base.h>
#include <c3d/uniforms.h>
#include <c3d/buffers.h>
#include <c3d/attribs.h>
#include <c3d/texenv.h>
#include <c3d/effect.h>
#include <c3d/texture.h>
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);

25
source/drawArray.c Normal file
View File

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

25
source/drawElements.c Normal file
View File

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

72
source/effect.c Normal file
View File

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

47
source/ibo.c Normal file
View File

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

View File

@ -0,0 +1,7 @@
#include <c3d/maths.h>
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;
}

View File

@ -0,0 +1,9 @@
#include <c3d/maths.h>
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];
}

View File

@ -0,0 +1,30 @@
#include <c3d/maths.h>
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);
}

View File

@ -0,0 +1,44 @@
#include <c3d/maths.h>
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);
}

View File

@ -0,0 +1,21 @@
#include <c3d/maths.h>
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);
}

View File

@ -0,0 +1,21 @@
#include <c3d/maths.h>
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);
}

View File

@ -0,0 +1,21 @@
#include <c3d/maths.h>
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);
}

12
source/maths/mtx_scale.c Normal file
View File

@ -0,0 +1,12 @@
#include <c3d/maths.h>
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;
}
}

View File

@ -0,0 +1,14 @@
#include <c3d/maths.h>
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);
}

44
source/mtxstack.c Normal file
View File

@ -0,0 +1,44 @@
#include <c3d/mtxstack.h>
#include <c3d/uniforms.h>
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;
}

43
source/texenv.c Normal file
View File

@ -0,0 +1,43 @@
#include <c3d/texenv.h>
#include <string.h>
#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));
}

80
source/texture.c Normal file
View File

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

63
source/uniforms.c Normal file
View File

@ -0,0 +1,63 @@
#include <c3d/uniforms.h>
//#include <stdio.h>
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;
}
}

49
source/vbo.c Normal file
View File

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