From 49c87e552659c48998c8a043526b43c753e776f1 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Fri, 5 Aug 2016 17:30:24 +0200 Subject: [PATCH] Updated all the libs, added citro3d, added ctr.swkbd (WIP, untested) --- Makefile | 15 +- libs/citro3d/.gitignore | 10 + libs/citro3d/Makefile | 141 +++ libs/citro3d/README.md | 28 + libs/citro3d/include/c3d/attribs.h | 16 + libs/citro3d/include/c3d/base.h | 63 ++ libs/citro3d/include/c3d/buffers.h | 21 + libs/citro3d/include/c3d/effect.h | 13 + libs/citro3d/include/c3d/light.h | 136 +++ libs/citro3d/include/c3d/lightlut.h | 29 + libs/citro3d/include/c3d/maths.h | 671 +++++++++++++ libs/citro3d/include/c3d/mtxstack.h | 23 + libs/citro3d/include/c3d/renderbuffer.h | 28 + libs/citro3d/include/c3d/renderqueue.h | 46 + libs/citro3d/include/c3d/texenv.h | 65 ++ libs/citro3d/include/c3d/texture.h | 22 + libs/citro3d/include/c3d/types.h | 32 + libs/citro3d/include/c3d/uniforms.h | 77 ++ libs/citro3d/include/citro3d.h | 30 + libs/citro3d/source/attribs.c | 66 ++ libs/citro3d/source/base.c | 365 +++++++ libs/citro3d/source/buffers.c | 55 + libs/citro3d/source/context.h | 100 ++ libs/citro3d/source/drawArrays.c | 31 + libs/citro3d/source/drawElements.c | 44 + libs/citro3d/source/effect.c | 89 ++ libs/citro3d/source/immediate.c | 67 ++ libs/citro3d/source/light.c | 157 +++ libs/citro3d/source/lightenv.c | 270 +++++ libs/citro3d/source/lightlut.c | 66 ++ libs/citro3d/source/maths/.gitignore | 2 + libs/citro3d/source/maths/mtx_fromquat.c | 34 + libs/citro3d/source/maths/mtx_identity.c | 10 + libs/citro3d/source/maths/mtx_inverse.c | 135 +++ libs/citro3d/source/maths/mtx_lookat.c | 37 + libs/citro3d/source/maths/mtx_multiply.c | 19 + libs/citro3d/source/maths/mtx_multiplyfvec3.c | 11 + libs/citro3d/source/maths/mtx_multiplyfvec4.c | 12 + libs/citro3d/source/maths/mtx_ortho.c | 20 + libs/citro3d/source/maths/mtx_orthotilt.c | 21 + libs/citro3d/source/maths/mtx_persp.c | 26 + libs/citro3d/source/maths/mtx_perspstereo.c | 28 + .../source/maths/mtx_perspstereotilt.c | 33 + libs/citro3d/source/maths/mtx_persptilt.c | 46 + libs/citro3d/source/maths/mtx_rotate.c | 72 ++ libs/citro3d/source/maths/mtx_rotatex.c | 30 + libs/citro3d/source/maths/mtx_rotatey.c | 30 + libs/citro3d/source/maths/mtx_rotatez.c | 30 + libs/citro3d/source/maths/mtx_scale.c | 12 + libs/citro3d/source/maths/mtx_translate.c | 21 + libs/citro3d/source/maths/quat_crossfvec3.c | 13 + libs/citro3d/source/maths/quat_multiply.c | 11 + libs/citro3d/source/maths/quat_pow.c | 23 + libs/citro3d/source/maths/quat_rotate.c | 16 + libs/citro3d/source/maths/quat_rotatex.c | 12 + libs/citro3d/source/maths/quat_rotatey.c | 12 + libs/citro3d/source/maths/quat_rotatez.c | 12 + libs/citro3d/source/mtxstack.c | 43 + libs/citro3d/source/renderbuffer.c | 110 ++ libs/citro3d/source/renderqueue.c | 404 ++++++++ libs/citro3d/source/texenv.c | 81 ++ libs/citro3d/source/texture.c | 115 +++ libs/citro3d/source/uniforms.c | 134 +++ libs/citro3d/test/3ds/Makefile | 249 +++++ libs/citro3d/test/3ds/logo48.png | Bin 0 -> 613 bytes libs/citro3d/test/3ds/logo64.png | Bin 0 -> 725 bytes libs/citro3d/test/3ds/romfs/logo.bin | Bin 0 -> 16384 bytes libs/citro3d/test/3ds/source/main.cpp | 927 +++++++++++++++++ libs/citro3d/test/3ds/source/vshader.v.pica | 89 ++ libs/citro3d/test/pc/.gitignore | 5 + libs/citro3d/test/pc/Makefile | 42 + libs/citro3d/test/pc/main.cpp | 939 ++++++++++++++++++ libs/sf2dlib/.gitignore | 13 +- libs/sf2dlib/libsf2d/Makefile | 49 +- libs/sf2dlib/libsf2d/data/shader.vsh | 30 - libs/sf2dlib/libsf2d/include/sf2d.h | 79 +- libs/sf2dlib/libsf2d/include/sf2d_private.h | 31 - libs/sf2dlib/libsf2d/source/sf2d.c | 267 ++--- libs/sf2dlib/libsf2d/source/sf2d_draw.c | 71 +- libs/sf2dlib/libsf2d/source/sf2d_private.c | 150 --- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 559 ++++------- libs/sf2dlib/sample/Makefile | 43 +- libs/sfillib/.gitignore | 2 - libs/sfillib/libsfil/Makefile | 15 +- libs/sfillib/libsfil/source/sfil_bmp.c | 12 +- libs/sfillib/libsfil/source/sfil_jpeg.c | 19 +- libs/sfillib/libsfil/source/sfil_png.c | 34 +- libs/sfillib/sample/Makefile | 2 +- libs/sftdlib/.gitignore | 2 - libs/sftdlib/libsftd/Makefile | 16 +- libs/sftdlib/libsftd/source/texture_atlas.c | 2 +- libs/sftdlib/sample/Makefile | 6 +- source/ctr.c | 8 + source/gfx.c | 19 +- source/swkbd.c | 204 ++++ source/texture.c | 8 +- source/thread.c | 2 + 97 files changed, 7341 insertions(+), 944 deletions(-) create mode 100644 libs/citro3d/.gitignore create mode 100644 libs/citro3d/Makefile create mode 100644 libs/citro3d/README.md create mode 100644 libs/citro3d/include/c3d/attribs.h create mode 100644 libs/citro3d/include/c3d/base.h create mode 100644 libs/citro3d/include/c3d/buffers.h create mode 100644 libs/citro3d/include/c3d/effect.h create mode 100644 libs/citro3d/include/c3d/light.h create mode 100644 libs/citro3d/include/c3d/lightlut.h create mode 100644 libs/citro3d/include/c3d/maths.h create mode 100644 libs/citro3d/include/c3d/mtxstack.h create mode 100644 libs/citro3d/include/c3d/renderbuffer.h create mode 100644 libs/citro3d/include/c3d/renderqueue.h create mode 100644 libs/citro3d/include/c3d/texenv.h create mode 100644 libs/citro3d/include/c3d/texture.h create mode 100644 libs/citro3d/include/c3d/types.h create mode 100644 libs/citro3d/include/c3d/uniforms.h create mode 100644 libs/citro3d/include/citro3d.h create mode 100644 libs/citro3d/source/attribs.c create mode 100644 libs/citro3d/source/base.c create mode 100644 libs/citro3d/source/buffers.c create mode 100644 libs/citro3d/source/context.h create mode 100644 libs/citro3d/source/drawArrays.c create mode 100644 libs/citro3d/source/drawElements.c create mode 100644 libs/citro3d/source/effect.c create mode 100644 libs/citro3d/source/immediate.c create mode 100644 libs/citro3d/source/light.c create mode 100644 libs/citro3d/source/lightenv.c create mode 100644 libs/citro3d/source/lightlut.c create mode 100644 libs/citro3d/source/maths/.gitignore create mode 100644 libs/citro3d/source/maths/mtx_fromquat.c create mode 100644 libs/citro3d/source/maths/mtx_identity.c create mode 100644 libs/citro3d/source/maths/mtx_inverse.c create mode 100644 libs/citro3d/source/maths/mtx_lookat.c create mode 100644 libs/citro3d/source/maths/mtx_multiply.c create mode 100644 libs/citro3d/source/maths/mtx_multiplyfvec3.c create mode 100644 libs/citro3d/source/maths/mtx_multiplyfvec4.c create mode 100644 libs/citro3d/source/maths/mtx_ortho.c create mode 100644 libs/citro3d/source/maths/mtx_orthotilt.c create mode 100644 libs/citro3d/source/maths/mtx_persp.c create mode 100644 libs/citro3d/source/maths/mtx_perspstereo.c create mode 100644 libs/citro3d/source/maths/mtx_perspstereotilt.c create mode 100644 libs/citro3d/source/maths/mtx_persptilt.c create mode 100644 libs/citro3d/source/maths/mtx_rotate.c create mode 100644 libs/citro3d/source/maths/mtx_rotatex.c create mode 100644 libs/citro3d/source/maths/mtx_rotatey.c create mode 100644 libs/citro3d/source/maths/mtx_rotatez.c create mode 100644 libs/citro3d/source/maths/mtx_scale.c create mode 100644 libs/citro3d/source/maths/mtx_translate.c create mode 100644 libs/citro3d/source/maths/quat_crossfvec3.c create mode 100644 libs/citro3d/source/maths/quat_multiply.c create mode 100644 libs/citro3d/source/maths/quat_pow.c create mode 100644 libs/citro3d/source/maths/quat_rotate.c create mode 100644 libs/citro3d/source/maths/quat_rotatex.c create mode 100644 libs/citro3d/source/maths/quat_rotatey.c create mode 100644 libs/citro3d/source/maths/quat_rotatez.c create mode 100644 libs/citro3d/source/mtxstack.c create mode 100644 libs/citro3d/source/renderbuffer.c create mode 100644 libs/citro3d/source/renderqueue.c create mode 100644 libs/citro3d/source/texenv.c create mode 100644 libs/citro3d/source/texture.c create mode 100644 libs/citro3d/source/uniforms.c create mode 100644 libs/citro3d/test/3ds/Makefile create mode 100644 libs/citro3d/test/3ds/logo48.png create mode 100644 libs/citro3d/test/3ds/logo64.png create mode 100644 libs/citro3d/test/3ds/romfs/logo.bin create mode 100644 libs/citro3d/test/3ds/source/main.cpp create mode 100644 libs/citro3d/test/3ds/source/vshader.v.pica create mode 100644 libs/citro3d/test/pc/.gitignore create mode 100644 libs/citro3d/test/pc/Makefile create mode 100644 libs/citro3d/test/pc/main.cpp delete mode 100644 libs/sf2dlib/libsf2d/data/shader.vsh delete mode 100644 libs/sf2dlib/libsf2d/include/sf2d_private.h delete mode 100644 libs/sf2dlib/libsf2d/source/sf2d_private.c delete mode 100644 libs/sfillib/.gitignore delete mode 100644 libs/sftdlib/.gitignore create mode 100644 source/swkbd.c diff --git a/Makefile b/Makefile index b4d10bb..2305f9f 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -logg -lm +LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lcitro3d -lctru -logg -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -71,7 +71,8 @@ LIBDIRS := $(CTRULIB) $(PORTLIBS) \ $(CURDIR)/libs/sf2dlib/libsf2d \ $(CURDIR)/libs/sftdlib/libsftd \ $(CURDIR)/libs/sfillib/libsfil \ - $(CURDIR)/libs/stb + $(CURDIR)/libs/stb \ + $(CURDIR)/libs/citro3d #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -149,6 +150,9 @@ $(BUILD): build-portlibs: @make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng libogg +build-citro3d: + @make -C libs/citro3d build + build-sf2dlib: @make -C libs/sf2dlib/libsf2d build @@ -161,6 +165,8 @@ build-sfillib: build-all: @echo Building 3ds_portlibs... @make build-portlibs + @echo Building citro3D + @make build-citro3d @echo Building sf2dlib... @make build-sf2dlib @echo Building sftdlib... @@ -189,6 +195,9 @@ clean: clean-portlibs: @make -C libs/3ds_portlibs clean +clean-citro3d: + @make -C libs/citro3d clean + clean-sf2dlib: @make -C libs/sf2dlib/libsf2d clean @@ -203,6 +212,8 @@ clean-all: @make clean-portlibs @echo Cleaning sf2dlib... @make clean-sf2dlib + @echo Cleaning citro3d... + @make clean-citro3d @echo Cleaning sftdlib... @make clean-sftdlib @echo Cleaning sfillib... diff --git a/libs/citro3d/.gitignore b/libs/citro3d/.gitignore new file mode 100644 index 0000000..2a2e8c7 --- /dev/null +++ b/libs/citro3d/.gitignore @@ -0,0 +1,10 @@ +*~ +*.3dsx +*.elf +*.exe +*.smdh +*.tar.bz2 +Thumbs.db +build/ +lib/ +bin/ diff --git a/libs/citro3d/Makefile b/libs/citro3d/Makefile new file mode 100644 index 0000000..0bf9221 --- /dev/null +++ b/libs/citro3d/Makefile @@ -0,0 +1,141 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +export CITRO3D_MAJOR := 1 +export CITRO3D_MINOR := 1 +export CITRO3D_PATCH := 0 + +VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH) + +#--------------------------------------------------------------------------------- +# 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=hard -mtp=soft + +CFLAGS := -g -Wall -Werror -O2 -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(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))) +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)) \ + $(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) + +dist-bin: all + @tar --exclude=*~ -cjf citro3d-$(VERSION).tar.bz2 include lib + +dist-src: + @tar --exclude=*~ -cjf citro3d-src-$(VERSION).tar.bz2 include source Makefile + +dist: dist-src dist-bin + +install: dist-bin + mkdir -p $(DEVKITPRO)/libctru + bzip2 -cd citro3d-$(VERSION).tar.bz2 | tar -xf - -C $(DEVKITPRO)/libctru + +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) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/libs/citro3d/README.md b/libs/citro3d/README.md new file mode 100644 index 0000000..ccd2a97 --- /dev/null +++ b/libs/citro3d/README.md @@ -0,0 +1,28 @@ +# citro3d + +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. + +# Setup + +citro3d can be built and installed using the following command: + + make install + +# License + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution. diff --git a/libs/citro3d/include/c3d/attribs.h b/libs/citro3d/include/c3d/attribs.h new file mode 100644 index 0000000..71d404d --- /dev/null +++ b/libs/citro3d/include/c3d/attribs.h @@ -0,0 +1,16 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 flags[2]; + u64 permutation; + int attrCount; +} C3D_AttrInfo; + +void AttrInfo_Init(C3D_AttrInfo* info); +int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count); +int AttrInfo_AddFixed(C3D_AttrInfo* info, int regId); + +C3D_AttrInfo* C3D_GetAttrInfo(void); +void C3D_SetAttrInfo(C3D_AttrInfo* info); diff --git a/libs/citro3d/include/c3d/base.h b/libs/citro3d/include/c3d/base.h new file mode 100644 index 0000000..2219283 --- /dev/null +++ b/libs/citro3d/include/c3d/base.h @@ -0,0 +1,63 @@ +#pragma once +#include "types.h" +#include "buffers.h" +#define C3D_DEFAULT_CMDBUF_SIZE 0x40000 + +enum +{ + C3D_UNSIGNED_BYTE = 0, + C3D_UNSIGNED_SHORT = 1, +}; + +bool C3D_Init(size_t cmdBufSize); +void C3D_FlushAsync(void); +void C3D_Fini(void); + +float C3D_GetCmdBufUsage(void); + +void C3D_BindProgram(shaderProgram_s* program); + +void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h); +void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom); + +void C3D_DrawArrays(GPU_Primitive_t primitive, int first, int size); +void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices); + +// Immediate-mode vertex submission +void C3D_ImmDrawBegin(GPU_Primitive_t primitive); +void C3D_ImmSendAttrib(float x, float y, float z, float w); +void C3D_ImmDrawEnd(void); + +static inline void C3D_ImmDrawRestartPrim(void) +{ + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); +} + +static inline void C3D_FlushAwait(void) +{ + gspWaitForP3D(); +} + +static inline void C3D_Flush(void) +{ + C3D_FlushAsync(); + C3D_FlushAwait(); +} + +static inline void C3D_VideoSync(void) +{ + gspWaitForEvent(GSPGPU_EVENT_VBlank0, false); + gfxSwapBuffersGpu(); +} + +// Fixed vertex attributes +C3D_FVec* C3D_FixedAttribGetWritePtr(int id); + +static inline void C3D_FixedAttribSet(int id, float x, float y, float z, float w) +{ + C3D_FVec* ptr = C3D_FixedAttribGetWritePtr(id); + ptr->x = x; + ptr->y = y; + ptr->z = z; + ptr->w = w; +} diff --git a/libs/citro3d/include/c3d/buffers.h b/libs/citro3d/include/c3d/buffers.h new file mode 100644 index 0000000..fe49dcb --- /dev/null +++ b/libs/citro3d/include/c3d/buffers.h @@ -0,0 +1,21 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 offset; + u32 flags[2]; +} C3D_BufCfg; + +typedef struct +{ + u32 base_paddr; + int bufCount; + C3D_BufCfg buffers[12]; +} C3D_BufInfo; + +void BufInfo_Init(C3D_BufInfo* info); +int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation); + +C3D_BufInfo* C3D_GetBufInfo(void); +void C3D_SetBufInfo(C3D_BufInfo* info); diff --git a/libs/citro3d/include/c3d/effect.h b/libs/citro3d/include/c3d/effect.h new file mode 100644 index 0000000..cd7b5fd --- /dev/null +++ b/libs/citro3d/include/c3d/effect.h @@ -0,0 +1,13 @@ +#pragma once +#include "types.h" + +void C3D_DepthMap(float zScale, float zOffset); +void C3D_CullFace(GPU_CULLMODE mode); +void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int inputMask, int writeMask); +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); +void C3D_ColorLogicOp(GPU_LOGICOP op); +void C3D_FragOpMode(GPU_FRAGOPMODE mode); diff --git a/libs/citro3d/include/c3d/light.h b/libs/citro3d/include/c3d/light.h new file mode 100644 index 0000000..67b492c --- /dev/null +++ b/libs/citro3d/include/c3d/light.h @@ -0,0 +1,136 @@ +#pragma once +#include "types.h" +#include "lightlut.h" + +//----------------------------------------------------------------------------- +// Material +//----------------------------------------------------------------------------- + +typedef struct +{ + float ambient[3]; + float diffuse[3]; + float specular0[3]; + float specular1[3]; + float emission[3]; +} C3D_Material; + +//----------------------------------------------------------------------------- +// Light environment +//----------------------------------------------------------------------------- + +// Forward declarations +typedef struct C3D_Light_t C3D_Light; +typedef struct C3D_LightEnv_t C3D_LightEnv; + +typedef struct +{ + u32 abs, select, scale; +} C3D_LightLutInputConf; + +typedef struct +{ + u32 ambient; + u32 numLights; + u32 config[2]; + C3D_LightLutInputConf lutInput; + u32 permutation; +} C3D_LightEnvConf; + +enum +{ + C3DF_LightEnv_Dirty = BIT(0), + C3DF_LightEnv_MtlDirty = BIT(1), + C3DF_LightEnv_LCDirty = BIT(2), + +#define C3DF_LightEnv_LutDirty(n) BIT(26+(n)) +#define C3DF_LightEnv_LutDirtyAll (0x3F<<26) +}; + +struct C3D_LightEnv_t +{ + void (* Update)(C3D_LightEnv* env); + void (* Dirty)(C3D_LightEnv* env); + u32 flags; + C3D_LightLut* luts[6]; + float ambient[3]; + C3D_Light* lights[8]; + C3D_LightEnvConf conf; + C3D_Material material; +}; + +void C3D_LightEnvInit(C3D_LightEnv* env); +void C3D_LightEnvBind(C3D_LightEnv* env); + +void C3D_LightEnvMaterial(C3D_LightEnv* env, const C3D_Material* mtl); +void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b); +void C3D_LightEnvLut(C3D_LightEnv* env, int lutId, int input, bool abs, C3D_LightLut* lut); + +enum +{ + GPU_SHADOW_PRIMARY = BIT(16), + GPU_SHADOW_SECONDARY = BIT(17), + GPU_INVERT_SHADOW = BIT(18), + GPU_SHADOW_ALPHA = BIT(19), +}; + +void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector); +void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode); +void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit); +void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode); +void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit); +void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp); + +//----------------------------------------------------------------------------- +// Light +//----------------------------------------------------------------------------- + +typedef struct +{ + u32 specular0, specular1, diffuse, ambient; +} C3D_LightMatConf; + +typedef struct +{ + C3D_LightMatConf material; + u16 position[3]; u16 padding0; + u16 spotDir[3]; u16 padding1; + u32 padding2; + u32 config; + u32 distAttnBias, distAttnScale; +} C3D_LightConf; + +enum +{ + C3DF_Light_Enabled = BIT(0), + C3DF_Light_Dirty = BIT(1), + C3DF_Light_MatDirty = BIT(2), + //C3DF_Light_Shadow = BIT(3), + //C3DF_Light_Spot = BIT(4), + //C3DF_Light_DistAttn = BIT(5), + + C3DF_Light_SPDirty = BIT(14), + C3DF_Light_DADirty = BIT(15), +}; + +struct C3D_Light_t +{ + u16 flags, id; + C3D_LightEnv* parent; + C3D_LightLut *lut_SP, *lut_DA; + float color[3]; + C3D_LightConf conf; +}; + +int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env); +void C3D_LightEnable(C3D_Light* light, bool enable); +void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable); +void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable); +void C3D_LightColor(C3D_Light* light, float r, float g, float b); +void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos); +void C3D_LightShadowEnable(C3D_Light* light, bool enable); +void C3D_LightSpotEnable(C3D_Light* light, bool enable); +void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z); +void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut); +void C3D_LightDistAttnEnable(C3D_Light* light, bool enable); +void C3D_LightDistAttn(C3D_Light* light, C3D_LightLutDA* lut); diff --git a/libs/citro3d/include/c3d/lightlut.h b/libs/citro3d/include/c3d/lightlut.h new file mode 100644 index 0000000..b9e0a5b --- /dev/null +++ b/libs/citro3d/include/c3d/lightlut.h @@ -0,0 +1,29 @@ +#pragma once +#include "types.h" +#include + +typedef struct +{ + u32 data[256]; +} C3D_LightLut; + +typedef struct +{ + C3D_LightLut lut; + float bias, scale; +} C3D_LightLutDA; + +typedef float (* C3D_LightLutFunc)(float x, float param); +typedef float (* C3D_LightLutFuncDA)(float dist, float arg0, float arg1); + +static inline float quadratic_dist_attn(float dist, float linear, float quad) +{ + return 1.0f / (1.0f + linear*dist + quad*dist*dist); +} + +void LightLut_FromArray(C3D_LightLut* lut, float* data); +void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative); +void LightLutDA_Create(C3D_LightLutDA* lut, C3D_LightLutFuncDA func, float from, float to, float arg0, float arg1); + +#define LightLut_Phong(lut, shininess) LightLut_FromFunc((lut), powf, (shininess), false) +#define LightLutDA_Quadratic(lut, from, to, linear, quad) LightLutDA_Create((lut), quadratic_dist_attn, (from), (to), (linear), (quad)) diff --git a/libs/citro3d/include/c3d/maths.h b/libs/citro3d/include/c3d/maths.h new file mode 100644 index 0000000..1587231 --- /dev/null +++ b/libs/citro3d/include/c3d/maths.h @@ -0,0 +1,671 @@ +#pragma once +#include "types.h" +#include +#include + +// See http://tauday.com/tau-manifesto +//#define M_TAU 6.28318530717958647693 +/// The one true circumference-to-radius ratio +#define M_TAU (2*M_PI) + +/** + * @brief Convert an angle from revolutions to radians + * @param[in] _angle Proportion of a full revolution + * @return Angle in radians + */ +#define C3D_Angle(_angle) ((_angle)*M_TAU) + +/** + * @brief Convert an angle from degrees to radians + * @param[in] _angle Angle in degrees + * @return Angle in radians + */ +#define C3D_AngleFromDegrees(_angle) ((_angle)*M_TAU/360.0f) + +#define C3D_AspectRatioTop (400.0f / 240.0f) ///< Aspect ratio for 3DS top screen +#define C3D_AspectRatioBot (320.0f / 240.0f) ///< Aspect ratio for 3DS bottom screen + +///@name Vector Math +///@{ +/** + * @brief Create a new FVec4 + * @param[in] x X-component + * @param[in] y Y-component + * @param[in] z Z-component + * @param[in] w W-component + * @return New FVec4 + */ +static inline C3D_FVec FVec4_New(float x, float y, float z, float w) +{ + return (C3D_FVec){{ w, z, y, x }}; +} + +/** + * @brief Add two FVec4s + * @param[in] lhs Augend + * @param[in] rhs Addend + * @return lhs+rhs (sum) + */ +static inline C3D_FVec FVec4_Add(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise addition + return FVec4_New(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z, lhs.w+rhs.w); +} + +/** + * @brief Subtract two FVec4s + * @param[in] lhs Minuend + * @param[in] rhs Subtrahend + * @return lhs-rhs (difference) + */ +static inline C3D_FVec FVec4_Subtract(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise subtraction + return FVec4_New(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); +} + +/** + * @brief Negate a FVec4 + * @note This is the same as scaling by -1 + * @param[in] v Vector to negate + * @return -v + */ +static inline C3D_FVec FVec4_Negate(C3D_FVec v) +{ + // component-wise negation + return FVec4_New(-v.x, -v.y, -v.z, -v.w); +} + +/** + * @brief Scale a FVec4 + * @param[in] v Vector to scale + * @param[in] s Scale factor + * @return v*s + */ +static inline C3D_FVec FVec4_Scale(C3D_FVec v, float s) +{ + // component-wise scaling + return FVec4_New(v.x*s, v.y*s, v.z*s, v.w*s); +} + +/** + * @brief Perspective divide + * @param[in] v Vector to divide + * @return v/v.w + */ +static inline C3D_FVec FVec4_PerspDivide(C3D_FVec v) +{ + // divide by w + return FVec4_New(v.x/v.w, v.y/v.w, v.z/v.w, 1.0f); +} + +/** + * @brief Dot product of two FVec4s + * @param[in] lhs Left-side FVec4 + * @param[in] rhs Right-side FVec4 + * @return lhs∙rhs + */ +static inline float FVec4_Dot(C3D_FVec lhs, C3D_FVec rhs) +{ + // A∙B = sum of component-wise products + return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w; +} + +/** + * @brief Magnitude of a FVec4 + * @param[in] v Vector + * @return ‖v‖ + */ +static inline float FVec4_Magnitude(C3D_FVec v) +{ + // ‖v‖ = √(v∙v) + return sqrtf(FVec4_Dot(v,v)); +} + +/** + * @brief Normalize a FVec4 + * @param[in] v FVec4 to normalize + * @return v/‖v‖ + */ +static inline C3D_FVec FVec4_Normalize(C3D_FVec v) +{ + // get vector magnitude + float m = FVec4_Magnitude(v); + + // scale by inverse magnitude to get a unit vector + return FVec4_New(v.x/m, v.y/m, v.z/m, v.w/m); +} + +/** + * @brief Create a new FVec3 + * @param[in] x X-component + * @param[in] y Y-component + * @param[in] z Z-component + * @return New FVec3 + */ +static inline C3D_FVec FVec3_New(float x, float y, float z) +{ + return FVec4_New(x, y, z, 0.0f); +} + +/** + * @brief Dot product of two FVec3s + * @param[in] lhs Left-side FVec3 + * @param[in] rhs Right-side FVec3 + * @return lhs∙rhs + */ +static inline float FVec3_Dot(C3D_FVec lhs, C3D_FVec rhs) +{ + // A∙B = sum of component-wise products + return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; +} + +/** + * @brief Magnitude of a FVec3 + * @param[in] v Vector + * @return ‖v‖ + */ +static inline float FVec3_Magnitude(C3D_FVec v) +{ + // ‖v‖ = √(v∙v) + return sqrtf(FVec3_Dot(v,v)); +} + +/** + * @brief Normalize a FVec3 + * @param[in] v FVec3 to normalize + * @return v/‖v‖ + */ +static inline C3D_FVec FVec3_Normalize(C3D_FVec v) +{ + // get vector magnitude + float m = FVec3_Magnitude(v); + + // scale by inverse magnitude to get a unit vector + return FVec3_New(v.x/m, v.y/m, v.z/m); +} + +/** + * @brief Add two FVec3s + * @param[in] lhs Augend + * @param[in] rhs Addend + * @return lhs+rhs (sum) + */ +static inline C3D_FVec FVec3_Add(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise addition + return FVec3_New(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); +} + +/** + * @brief Subtract two FVec3s + * @param[in] lhs Minuend + * @param[in] rhs Subtrahend + * @return lhs-rhs (difference) + */ +static inline C3D_FVec FVec3_Subtract(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise subtraction + return FVec3_New(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z); +} + +/** + * @brief Distance between two 3D points + * @param[in] lhs Relative origin + * @param[in] rhs Relative point of interest + * @return ‖lhs-rhs‖ + */ +static inline float FVec3_Distance(C3D_FVec lhs, C3D_FVec rhs) +{ + // distance = ‖lhs-rhs‖ + return FVec3_Magnitude(FVec3_Subtract(lhs, rhs)); +} + +/** + * @brief Scale a FVec4 + * @param[in] v Vector to scale + * @param[in] s Scale factor + * @return v*s + */ +static inline C3D_FVec FVec3_Scale(C3D_FVec v, float s) +{ + // component-wise scaling + return FVec3_New(v.x*s, v.y*s, v.z*s); +} + +/** + * @brief Negate a FVec4 + * @note This is the same as scaling by -1 + * @param[in] v Vector to negate + * @return -v + */ +static inline C3D_FVec FVec3_Negate(C3D_FVec v) +{ + // component-wise negation + return FVec3_New(-v.x, -v.y, -v.z); +} + +/** + * @brief Cross product of two FVec3s + * @note This returns a pseudo-vector which is perpendicular to the plane + * spanned by the two input vectors. + * @param[in] lhs Left-side FVec3 + * @param[in] rhs Right-side FVec3 + * @return lhs×rhs + */ +static inline C3D_FVec FVec3_Cross(C3D_FVec lhs, C3D_FVec rhs) +{ + // A×B = (AyBz - AzBy, AzBx - AxBz, AxBy - AyBx) + 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); +} +///@} + +///@name Matrix Math +///@note All matrices are 4x4 unless otherwise noted +///@{ + +/** + * @brief Zero matrix + * @param[out] out Matrix to zero + */ +static inline void Mtx_Zeros(C3D_Mtx* out) +{ + memset(out, 0, sizeof(*out)); +} + +/** + * @brief Copy a matrix + * @param[out] out Output matrix + * @param[in] in Input matrix + */ +static inline void Mtx_Copy(C3D_Mtx* out, const C3D_Mtx* in) +{ + *out = *in; +} + +/** + * @brief Identity matrix + * @param[out] out Matrix to fill + */ +void Mtx_Identity(C3D_Mtx* out); + +/** + * @brief Multiply two matrices + * @param[out] out Output matrix + * @param[in] a Multiplicand + * @param[in] b Multiplier + */ +void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b); + +/** + * @brief Inverse a matrix + * @note returns 0.0f if the matrix is degenerate; i.e. no inverse + * @param[in,out] out Matrix to inverse + * @return determinant + */ +float Mtx_Inverse(C3D_Mtx* out); + +/** + * @brief Multiply 3x3 matrix by a FVec3 + * @param[in] mtx Matrix + * @param[in] v Vector + * @return Product of mtx and v + */ +C3D_FVec Mtx_MultiplyFVec3(const C3D_Mtx* mtx, C3D_FVec v); + +/** + * @brief Multiply 4x4 matrix by a FVec4 + * @param[in] mtx Matrix + * @param[in] v Vector + * @return Product of mtx and v + */ +C3D_FVec Mtx_MultiplyFVec4(const C3D_Mtx* mtx, C3D_FVec v); + +/** + * @brief Multiply 4x3 matrix by a FVec3 + * @param[in] mtx Matrix + * @param[in] v Vector + * @return Product of mtx and v + */ +static inline C3D_FVec Mtx_MultiplyFVecH(const C3D_Mtx* mtx, C3D_FVec v) +{ + v.w = 1.0f; + + return Mtx_MultiplyFVec4(mtx, v); +} +///@} + +/** + * @name 3D Transformation Matrix Math + * @note bRightSide is used to determine which side to perform the transformation. + * With an input matrix A and a transformation matrix B, bRightSide being + * true yields AB, while being false yield BA. + */ +///@{ + +/** + * @brief 3D translation + * @param[in,out] mtx Matrix to translate + * @param[in] x X component to translate + * @param[in] y Y component to translate + * @param[in] z Z component to translate + * @param[in] bRightSide Whether to transform from the right side + */ +void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z, bool bRightSide); + +/** + * @brief 3D Scale + * @param[in,out] mtx Matrix to scale + * @param[in] x X component to scale + * @param[in] y Y component to scale + * @param[in] z Z component to scale + */ +void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z); + +/** + * @brief 3D Rotation + * @param[in,out] mtx Matrix to rotate + * @param[in] axis Axis about which to rotate + * @param[in] angle Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + */ +void Mtx_Rotate(C3D_Mtx* mtx, C3D_FVec axis, float angle, bool bRightSide); + +/** + * @brief 3D Rotation about the X axis + * @param[in,out] mtx Matrix to rotate + * @param[in] angle Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + */ +void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide); + +/** + * @brief 3D Rotation about the Y axis + * @param[in,out] mtx Matrix to rotate + * @param[in] angle Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + */ +void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide); + +/** + * @brief 3D Rotation about the Z axis + * @param[in,out] mtx Matrix to rotate + * @param[in] angle Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + */ +void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide); +///@} + +///@name 3D Projection Matrix Math +///@{ + +/** + * @brief Orthogonal projection + * @param[out] mtx Output matrix + * @param[in] left Left clip plane (X=left) + * @param[in] right Right clip plane (X=right) + * @param[in] bottom Bottom clip plane (Y=bottom) + * @param[in] top Top clip plane (Y=top) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_Ortho(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded); + +/** + * @brief Perspective projection + * @param[out] mtx Output matrix + * @param[in] fovy Vertical field of view in radians + * @param[in] aspect Aspect ration of projection plane (width/height) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_Persp(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded); + +/** + * @brief Stereo perspective projection + * @note Typically you will use iod to mean the distance between the eyes. Plug + * in -iod for the left eye and iod for the right eye. + * @note The focal length is defined by screen. If objects are further than this, + * they will appear to be inside the screen. If objects are closer than this, + * they will appear to pop out of the screen. Objects at this distance appear + * to be at the screen. + * @param[out] mtx Output matrix + * @param[in] fovy Vertical field of view in radians + * @param[in] aspect Aspect ration of projection plane (width/height) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] iod Interocular distance + * @param[in] screen Focal length + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_PerspStereo(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded); + +/** + * @brief Orthogonal projection, tilted to account for the 3DS screen rotation + * @param[in] left Left clip plane (X=left) + * @param[in] right Right clip plane (X=right) + * @param[in] bottom Bottom clip plane (Y=bottom) + * @param[in] top Top clip plane (Y=top) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_OrthoTilt(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded); + +/** + * @brief Perspective projection, tilted to account for the 3DS screen rotation + * @param[out] mtx Output matrix + * @param[in] fovy Vertical field of view in radians + * @param[in] aspect Aspect ration of projection plane (width/height) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_PerspTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded); + +/** + * @brief Stereo perspective projection, tilted to account for the 3DS screen rotation + * @note See the notes for Mtx_PerspStereo + * @param[out] mtx Output matrix + * @param[in] fovy Vertical field of view in radians + * @param[in] aspect Aspect ration of projection plane (width/height) + * @param[in] near Near clip plane (Z=near) + * @param[in] far Far clip plane (Z=far) + * @param[in] iod Interocular distance + * @param[in] screen Focal length + * @param[in] isLeftHanded Whether to build a LH projection + */ +void Mtx_PerspStereoTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded); + +/** + * @brief Left-handed Look-At matrix, using DirectX implementation + * @note See https://msdn.microsoft.com/en-us/library/windows/desktop/bb205342 + * @param[out] out Output matrix. + * @param[in] cameraPosition Position of the intended camera in 3D space. + * @param[in] cameraTarget Position of the intended target the camera is supposed to face in 3D space. + * @param[in] cameraUpVector The vector that points straight up depending on the camera's "Up" direction. + * @param[in] isLeftHanded If true, output matrix is left-handed. If false, output matrix is right-handed. + */ +void Mtx_LookAt(C3D_Mtx* out, C3D_FVec cameraPosition, C3D_FVec cameraTarget, C3D_FVec cameraUpVector, bool isLeftHanded); +///@} + +///@name Quaternion Math +///@{ +// +/** + * @brief Create a new Quaternion + * @param[in] i I-component + * @param[in] j J-component + * @param[in] k K-component + * @param[in] r R-component + * @return New Quaternion + */ +#define Quat_New(i,j,k,r) FVec4_New(i,j,k,r) + +/** + * @brief Negate a Quaternion + * @note This is the same as scaling by -1 + * @param[in] q Quaternion to negate + * @return -q + */ +#define Quat_Negate(q) FVec4_Negate(q) + +/** + * @brief Add two Quaternions + * @param[in] lhs Augend + * @param[in] rhs Addend + * @return lhs+rhs (sum) + */ +#define Quat_Add(lhs,rhs) FVec4_Add(lhs,rhs) + +/** + * @brief Subtract two Quaternions + * @param[in] lhs Minuend + * @param[in] rhs Subtrahend + * @return lhs-rhs (difference) + */ +#define Quat_Subtract(lhs,rhs) FVec4_Subtract(lhs,rhs) + +/** + * @brief Scale a Quaternion + * @param[in] q Quaternion to scale + * @param[in] s Scale factor + * @return q*s + */ +#define Quat_Scale(q,s) FVec4_Scale(q,s) + +/** + * @brief Normalize a Quaternion + * @param[in] q Quaternion to normalize + * @return q/‖q‖ + */ +#define Quat_Normalize(q) FVec4_Normalize(q) + +/** + * @brief Dot product of two Quaternions + * @param[in] lhs Left-side Quaternion + * @param[in] rhs Right-side Quaternion + * @return lhs∙rhs + */ +#define Quat_Dot(lhs,rhs) FVec4_Dot(lhs,rhs) + +/** + * @brief Multiply two Quaternions + * @param[in] lhs Multiplicand + * @param[in] rhs Multiplier + * @return lhs*rhs + */ +C3D_FQuat Quat_Multiply(C3D_FQuat lhs, C3D_FQuat rhs); + +/** + * @brief Raise Quaternion to a power + * @note If p is 0, this returns the identity Quaternion. + * If p is 1, this returns q. + * @param[in] q Base Quaternion + * @param[in] p Power + * @return q^p + */ +C3D_FQuat Quat_Pow(C3D_FQuat q, float p); + +/** + * @brief Cross product of Quaternion and FVec3 + * @param[in] lhs Left-side Quaternion + * @param[in] rhs Right-side FVec3 + * @return q×v + */ +C3D_FVec Quat_CrossFVec3(C3D_FQuat q, C3D_FVec v); + +/** + * @brief 3D Rotation + * @param[in] q Quaternion to rotate + * @param[in] axis Axis about which to rotate + * @param[in] r Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + * @return Rotated Quaternion + */ +C3D_FQuat Quat_Rotate(C3D_FQuat q, C3D_FVec axis, float r, bool bRightSide); + +/** + * @brief 3D Rotation about the X axis + * @param[in] q Quaternion to rotate + * @param[in] r Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + * @return Rotated Quaternion + */ +C3D_FQuat Quat_RotateX(C3D_FQuat q, float r, bool bRightSide); + +/** + * @brief 3D Rotation about the Y axis + * @param[in] q Quaternion to rotate + * @param[in] r Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + * @return Rotated Quaternion + */ +C3D_FQuat Quat_RotateY(C3D_FQuat q, float r, bool bRightSide); + +/** + * @brief 3D Rotation about the Z axis + * @param[in] q Quaternion to rotate + * @param[in] r Radians to rotate + * @param[in] bRightSide Whether to transform from the right side + * @return Rotated Quaternion + */ +C3D_FQuat Quat_RotateZ(C3D_FQuat q, float r, bool bRightSide); + +/** + * @brief Get 4x4 matrix equivalent to Quaternion + * @param[out] m Output matrix + * @param[in] q Input Quaternion + */ +void Mtx_FromQuat(C3D_Mtx* m, C3D_FQuat q); + +/** + * @brief Identity Quaternion + * @return Identity Quaternion + */ +static inline C3D_FQuat Quat_Identity(void) +{ + // r=1, i=j=k=0 + return Quat_New(0.0f, 0.0f, 0.0f, 1.0f); +} + +/** + * @brief Quaternion conjugate + * @param[in] q Quaternion of which to get conjugate + * @return Conjugate of q + */ +static inline C3D_FQuat Quat_Conjugate(C3D_FQuat q) +{ + // q* = q.r - q.i - q.j - q.k + return Quat_New(-q.i, -q.j, -q.k, q.r); +} + +/** + * @brief Quaternion inverse + * @note This is the same as raising to the power of -1 + * @param[in] q Quaternion of which to get inverse + * @return Inverse of q + */ +static inline C3D_FQuat Quat_Inverse(C3D_FQuat q) +{ + // q^-1 = (q.r - q.i - q.j - q.k) / (q.r^2 + q.i^2 + q.j^2 + q.k^2) + // = q* / (q∙q) + C3D_FQuat c = Quat_Conjugate(q); + float d = Quat_Dot(q, q); + return Quat_New(c.i/d, c.j/d, c.k/d, c.r/d); +} + +/** + * @brief Cross product of FVec3 and Quaternion + * @param[in] lhs Left-side FVec3 + * @param[in] rhs Right-side Quaternion + * @return v×q + */ +static inline C3D_FVec FVec3_CrossQuat(C3D_FVec v, C3D_FQuat q) +{ + // v×q = q^-1×v + return Quat_CrossFVec3(Quat_Inverse(q), v); +} +///@} diff --git a/libs/citro3d/include/c3d/mtxstack.h b/libs/citro3d/include/c3d/mtxstack.h new file mode 100644 index 0000000..3c86536 --- /dev/null +++ b/libs/citro3d/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 unifType, 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, GPU_SHADER_TYPE unifType, int unifPos, int unifLen); +C3D_Mtx* MtxStack_Push(C3D_MtxStack* stk); +C3D_Mtx* MtxStack_Pop(C3D_MtxStack* stk); +void MtxStack_Update(C3D_MtxStack* stk); diff --git a/libs/citro3d/include/c3d/renderbuffer.h b/libs/citro3d/include/c3d/renderbuffer.h new file mode 100644 index 0000000..40e6ff8 --- /dev/null +++ b/libs/citro3d/include/c3d/renderbuffer.h @@ -0,0 +1,28 @@ +#pragma once +#include "types.h" +#include "texture.h" + +typedef struct +{ + C3D_Tex colorBuf, depthBuf; + u32 clearColor, clearDepth; + int depthFmt; +} C3D_RenderBuf; + +bool C3D_RenderBufInit(C3D_RenderBuf* rb, int width, int height, int colorFmt, int depthFmt); +void C3D_RenderBufClearAsync(C3D_RenderBuf* rb); +void C3D_RenderBufTransferAsync(C3D_RenderBuf* rb, u32* frameBuf, u32 flags); +void C3D_RenderBufBind(C3D_RenderBuf* rb); +void C3D_RenderBufDelete(C3D_RenderBuf* rb); + +static inline void C3D_RenderBufClear(C3D_RenderBuf* rb) +{ + C3D_RenderBufClearAsync(rb); + gspWaitForPSC0(); +} + +static inline void C3D_RenderBufTransfer(C3D_RenderBuf* rb, u32* frameBuf, u32 flags) +{ + C3D_RenderBufTransferAsync(rb, frameBuf, flags); + gspWaitForPPF(); +} diff --git a/libs/citro3d/include/c3d/renderqueue.h b/libs/citro3d/include/c3d/renderqueue.h new file mode 100644 index 0000000..1865663 --- /dev/null +++ b/libs/citro3d/include/c3d/renderqueue.h @@ -0,0 +1,46 @@ +#pragma once +#include "renderbuffer.h" + +typedef struct C3D_RenderTarget_tag C3D_RenderTarget; + +struct C3D_RenderTarget_tag +{ + C3D_RenderTarget *next, *prev, *link, *frame[2]; + C3D_RenderBuf renderBuf; + u32 transferFlags; + + u8 clearBits; + bool drawOk, transferOk; + + bool linked; + gfxScreen_t screen; + gfx3dSide_t side; +}; + +// Flags for C3D_FrameBegin +enum +{ + C3D_FRAME_SYNCDRAW = BIT(0), // Do not render the frame until the previous has finished rendering + C3D_FRAME_NONBLOCK = BIT(1), // Return false instead of waiting for the GPU to finish rendering +}; + +bool C3D_FrameBegin(u8 flags); +bool C3D_FrameDrawOn(C3D_RenderTarget* target); +void C3D_FrameEnd(u8 flags); + +// Flags for C3D_RenderTargetSetClear (only C3D_CLEAR_ALL implemented atm) +enum +{ + C3D_CLEAR_COLOR = BIT(0), + C3D_CLEAR_DEPTH = BIT(1), + C3D_CLEAR_ALL = C3D_CLEAR_COLOR | C3D_CLEAR_DEPTH, +}; + +C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, int colorFmt, int depthFmt); +void C3D_RenderTargetDelete(C3D_RenderTarget* target); +void C3D_RenderTargetSetClear(C3D_RenderTarget* target, u32 clearBits, u32 clearColor, u32 clearDepth); +void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); + +void C3D_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags); +void C3D_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags); +void C3D_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1); diff --git a/libs/citro3d/include/c3d/texenv.h b/libs/citro3d/include/c3d/texenv.h new file mode 100644 index 0000000..2b2f4bd --- /dev/null +++ b/libs/citro3d/include/c3d/texenv.h @@ -0,0 +1,65 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u16 srcRgb, srcAlpha; + u16 opRgb, opAlpha; + u16 funcRgb, funcAlpha; + u32 color; + u16 scaleRgb, scaleAlpha; +} 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); + +void C3D_TexEnvBufUpdate(int mode, int mask); +void C3D_TexEnvBufColor(u32 color); + +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; +} + +static inline void C3D_TexEnvScale(C3D_TexEnv* env, int mode, int param) +{ + if (mode & C3D_RGB) + env->scaleRgb = param; + if (mode & C3D_Alpha) + env->scaleAlpha = param; +} diff --git a/libs/citro3d/include/c3d/texture.h b/libs/citro3d/include/c3d/texture.h new file mode 100644 index 0000000..a9d743c --- /dev/null +++ b/libs/citro3d/include/c3d/texture.h @@ -0,0 +1,22 @@ +#pragma once +#include "types.h" + +typedef struct +{ + void* data; + + GPU_TEXCOLOR fmt : 4; + size_t size : 28; + + u16 width, height; + u32 param; +} C3D_Tex; + +bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); +bool C3D_TexInitVRAM(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/libs/citro3d/include/c3d/types.h b/libs/citro3d/include/c3d/types.h new file mode 100644 index 0000000..243b37c --- /dev/null +++ b/libs/citro3d/include/c3d/types.h @@ -0,0 +1,32 @@ +#pragma once +#ifdef _3DS +#include <3ds.h> +#else +#include +#include +typedef uint8_t u8; +typedef uint32_t u32; +#endif + +typedef u32 C3D_IVec; + +typedef union +{ + struct { float w, z, y, x; }; + struct { float r, k, j, i; }; + float c[4]; +} C3D_FVec; + +typedef C3D_FVec C3D_FQuat; + +// Row-major 4x4 matrix +typedef 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/libs/citro3d/include/c3d/uniforms.h b/libs/citro3d/include/c3d/uniforms.h new file mode 100644 index 0000000..ddc06b4 --- /dev/null +++ b/libs/citro3d/include/c3d/uniforms.h @@ -0,0 +1,77 @@ +#pragma once +#include "types.h" +#define C3D_FVUNIF_COUNT 96 +#define C3D_IVUNIF_COUNT 4 + +extern C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; +extern C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; +extern u16 C3D_BoolUnifs[2]; + +extern bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; +extern bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; +extern bool C3D_BoolUnifsDirty[2]; + +static inline C3D_FVec* C3D_FVUnifWritePtr(GPU_SHADER_TYPE type, int id, int size) +{ + int i; + for (i = 0; i < size; i ++) + C3D_FVUnifDirty[type][id+i] = true; + return &C3D_FVUnif[type][id]; +} + +static inline C3D_IVec* C3D_IVUnifWritePtr(GPU_SHADER_TYPE type, int id) +{ + id -= 0x60; + C3D_IVUnifDirty[type][id] = true; + return &C3D_IVUnif[type][id]; +} + +static inline void C3D_FVUnifMtxNx4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx, int num) +{ + int i; + C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, num); + for (i = 0; i < num; i ++) + ptr[i] = mtx->r[i]; // Struct copy. +} + +static inline void C3D_FVUnifMtx4x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) +{ + C3D_FVUnifMtxNx4(type, id, mtx, 4); +} + +static inline void C3D_FVUnifMtx3x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) +{ + C3D_FVUnifMtxNx4(type, id, mtx, 3); +} + +static inline void C3D_FVUnifMtx2x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) +{ + C3D_FVUnifMtxNx4(type, id, mtx, 2); +} + +static inline void C3D_FVUnifSet(GPU_SHADER_TYPE type, int id, float x, float y, float z, float w) +{ + C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, 1); + ptr->x = x; + ptr->y = y; + ptr->z = z; + ptr->w = w; +} + +static inline void C3D_IVUnifSet(GPU_SHADER_TYPE type, int id, int x, int y, int z, int w) +{ + C3D_IVec* ptr = C3D_IVUnifWritePtr(type, id); + *ptr = IVec_Pack(x, y, z, w); +} + +static inline void C3D_BoolUnifSet(GPU_SHADER_TYPE type, int id, bool value) +{ + id -= 0x68; + C3D_BoolUnifsDirty[type] = true; + if (value) + C3D_BoolUnifs[type] |= BIT(id); + else + C3D_BoolUnifs[type] &= ~BIT(id); +} + +void C3D_UpdateUniforms(GPU_SHADER_TYPE type); diff --git a/libs/citro3d/include/citro3d.h b/libs/citro3d/include/citro3d.h new file mode 100644 index 0000000..a9f2215 --- /dev/null +++ b/libs/citro3d/include/citro3d.h @@ -0,0 +1,30 @@ +#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" +#include "c3d/light.h" + +#include "c3d/renderbuffer.h" +#include "c3d/renderqueue.h" + +#ifdef __cplusplus +} +#endif diff --git a/libs/citro3d/source/attribs.c b/libs/citro3d/source/attribs.c new file mode 100644 index 0000000..0a32256 --- /dev/null +++ b/libs/citro3d/source/attribs.c @@ -0,0 +1,66 @@ +#include +#include +#include "context.h" + +void AttrInfo_Init(C3D_AttrInfo* info) +{ + memset(info, 0, sizeof(*info)); + info->flags[1] = 0xFFF << 16; +} + +int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count) +{ + if (info->attrCount == 12) return -1; + int id = info->attrCount++; + if (regId < 0) regId = id; + if (id < 8) + info->flags[0] |= GPU_ATTRIBFMT(id, count, format); + else + info->flags[1] |= GPU_ATTRIBFMT(id-8, count, format); + + info->flags[1] = (info->flags[1] &~ (0xF0000000 | BIT(id+16))) | (id << 28); + info->permutation |= regId << (id*4); + return id; +} + +int AttrInfo_AddFixed(C3D_AttrInfo* info, int regId) +{ + if (info->attrCount == 12) return -1; + int id = info->attrCount++; + if (regId < 0) regId = id; + + info->flags[1] = (info->flags[1] &~ 0xF0000000) | (id << 28); + info->permutation |= regId << (id*4); + return id; +} + +C3D_AttrInfo* C3D_GetAttrInfo(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return NULL; + + ctx->flags |= C3DiF_AttrInfo; + return &ctx->attrInfo; +} + +void C3D_SetAttrInfo(C3D_AttrInfo* info) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (info != &ctx->attrInfo) + memcpy(&ctx->attrInfo, info, sizeof(*info)); + ctx->flags |= C3DiF_AttrInfo; +} + +void C3Di_AttrInfoBind(C3D_AttrInfo* info) +{ + GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFERS_FORMAT_LOW, (u32*)info->flags, sizeof(info->flags)/sizeof(u32)); + GPUCMD_AddMaskedWrite(GPUREG_VSH_INPUTBUFFER_CONFIG, 0xB, 0xA0000000 | (info->attrCount - 1)); + GPUCMD_AddWrite(GPUREG_VSH_NUM_ATTR, info->attrCount - 1); + GPUCMD_AddIncrementalWrites(GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW, (u32*)&info->permutation, 2); +} diff --git a/libs/citro3d/source/base.c b/libs/citro3d/source/base.c new file mode 100644 index 0000000..28aac3e --- /dev/null +++ b/libs/citro3d/source/base.c @@ -0,0 +1,365 @@ +#include "context.h" + +C3D_Context __C3D_Context; + +static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) +{ + u32 reg[4]; + reg[0] = tex->fmt; + reg[1] = osConvertVirtToPhys(tex->data) >> 3; + reg[2] = (u32)tex->height | ((u32)tex->width << 16); + reg[3] = tex->param; + + switch (unit) + { + case GPU_TEXUNIT0: + GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, reg[0]); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_ADDR1, reg[1]); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_DIM, reg[2]); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_PARAM, reg[3]); + break; + case GPU_TEXUNIT1: + GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, reg[0]); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_ADDR, reg[1]); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_DIM, reg[2]); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_PARAM, reg[3]); + break; + case GPU_TEXUNIT2: + GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, reg[0]); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_ADDR, reg[1]); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_DIM, reg[2]); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_PARAM, reg[3]); + break; + } +} + +static aptHookCookie hookCookie; + +static void C3Di_AptEventHook(APT_HookType hookType, void* param) +{ + C3D_Context* ctx = C3Di_GetContext(); + + switch (hookType) + { + case APTHOOK_ONSUSPEND: + { + if (ctx->renderQueueWaitDone) + ctx->renderQueueWaitDone(); + break; + } + case APTHOOK_ONRESTORE: + { + ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_RenderBuf + | C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode + | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv; + + C3Di_DirtyUniforms(GPU_VERTEX_SHADER); + C3Di_DirtyUniforms(GPU_GEOMETRY_SHADER); + + ctx->fixedAttribDirty |= ctx->fixedAttribEverDirty; + + C3D_LightEnv* env = ctx->lightEnv; + if (env) + env->Dirty(env); + break; + } + default: + break; + } +} + +bool C3D_Init(size_t cmdBufSize) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_Active) + return false; + + ctx->cmdBufSize = cmdBufSize/8; // Half of the size of the cmdbuf, in words + ctx->cmdBuf = (u32*)linearAlloc(cmdBufSize); + ctx->cmdBufUsage = 0; + if (!ctx->cmdBuf) return false; + + GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0); + + ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexAll; + ctx->renderQueueExit = NULL; + + // TODO: replace with direct struct access + C3D_DepthMap(-1.0f, 0.0f); + C3D_CullFace(GPU_CULL_BACK_CCW); + C3D_StencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); + C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_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); + C3D_FragOpMode(GPU_FRAGOPMODE_GL); + + ctx->texEnvBuf = 0; + ctx->texEnvBufClr = 0xFFFFFFFF; + + for (i = 0; i < 3; i ++) + ctx->tex[i] = NULL; + + for (i = 0; i < 6; i ++) + TexEnv_Init(&ctx->texEnv[i]); + + ctx->fixedAttribDirty = 0; + ctx->fixedAttribEverDirty = 0; + + aptHook(&hookCookie, C3Di_AptEventHook, NULL); + + return true; +} + +void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h) +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Viewport | C3DiF_Scissor; + ctx->viewport[0] = f32tof24(w / 2.0f); + ctx->viewport[1] = f32tof31(2.0f / w) << 1; + ctx->viewport[2] = f32tof24(h / 2.0f); + ctx->viewport[3] = f32tof31(2.0f / h) << 1; + ctx->viewport[4] = (y << 16) | (x & 0xFFFF); + ctx->scissor[0] = GPU_SCISSOR_DISABLE; +} + +void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom) +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Scissor; + ctx->scissor[0] = mode; + if (mode == GPU_SCISSOR_DISABLE) return; + ctx->scissor[1] = (top << 16) | (left & 0xFFFF); + ctx->scissor[2] = ((bottom-1) << 16) | ((right-1) & 0xFFFF); +} + +void C3Di_UpdateContext(void) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_Program) + { + shaderProgramConfigure(ctx->program, (ctx->flags & C3DiF_VshCode) != 0, (ctx->flags & C3DiF_GshCode) != 0); + ctx->flags &= ~(C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode); + } + + if (ctx->flags & C3DiF_RenderBuf) + { + ctx->flags &= ~C3DiF_RenderBuf; + if (ctx->flags & C3DiF_DrawUsed) + { + ctx->flags &= ~C3DiF_DrawUsed; + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); + } + C3Di_RenderBufBind(ctx->rb); + } + + if (ctx->flags & C3DiF_Viewport) + { + ctx->flags &= ~C3DiF_Viewport; + GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, ctx->viewport, 4); + GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, ctx->viewport[4]); + } + + if (ctx->flags & C3DiF_Scissor) + { + ctx->flags &= ~C3DiF_Scissor; + GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, ctx->scissor, 3); + } + + if (ctx->flags & C3DiF_AttrInfo) + { + ctx->flags &= ~C3DiF_AttrInfo; + C3Di_AttrInfoBind(&ctx->attrInfo); + } + + if (ctx->flags & C3DiF_BufInfo) + { + ctx->flags &= ~C3DiF_BufInfo; + C3Di_BufInfoBind(&ctx->bufInfo); + } + + 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; + GPUCMD_AddWrite(GPUREG_TEXUNIT_CONFIG, 0x00011000|units); // Enable texture units + } + + if (ctx->flags & C3DiF_TexEnvBuf) + { + ctx->flags &= ~C3DiF_TexEnvBuf; + GPUCMD_AddMaskedWrite(GPUREG_TEXENV_UPDATE_BUFFER, 0x2, ctx->texEnvBuf); + GPUCMD_AddWrite(GPUREG_TEXENV_BUFFER_COLOR, ctx->texEnvBufClr); + } + + 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_LightEnv* env = ctx->lightEnv; + + if (ctx->flags & C3DiF_LightEnv) + { + u32 enable = env != NULL; + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE0, enable); + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE1, !enable); + ctx->flags &= ~C3DiF_LightEnv; + } + + if (env) + env->Update(env); + + if (ctx->fixedAttribDirty) + { + for (i = 0; i < 12; i ++) + { + if (!(ctx->fixedAttribDirty & BIT(i))) continue; + C3D_FVec* v = &ctx->fixedAttribs[i]; + + GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, i); + C3D_ImmSendAttrib(v->x, v->y, v->z, v->w); + } + ctx->fixedAttribDirty = 0; + } + + C3D_UpdateUniforms(GPU_VERTEX_SHADER); + C3D_UpdateUniforms(GPU_GEOMETRY_SHADER); +} + +void C3Di_FinalizeFrame(u32** pBuf, u32* pSize) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_DrawUsed) + { + ctx->flags &= ~C3DiF_DrawUsed; + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); + } + + GPUCMD_Finalize(); + GPUCMD_GetBuffer(pBuf, NULL, pSize); + ctx->cmdBufUsage = (float)(*pSize) / ctx->cmdBufSize; + *pSize *= 4; + + ctx->flags ^= C3DiF_CmdBuffer; + u32* buf = ctx->cmdBuf; + if (ctx->flags & C3DiF_CmdBuffer) + buf += ctx->cmdBufSize; + GPUCMD_SetBuffer(buf, ctx->cmdBufSize, 0); +} + +void C3D_FlushAsync(void) +{ + if (!(C3Di_GetContext()->flags & C3DiF_Active)) + return; + + u32* cmdBuf; + u32 cmdBufSize; + C3Di_FinalizeFrame(&cmdBuf, &cmdBufSize); + + //take advantage of GX_FlushCacheRegions to flush gsp heap + extern u32 __ctru_linear_heap; + extern u32 __ctru_linear_heap_size; + GX_FlushCacheRegions(cmdBuf, cmdBufSize, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0); + GX_ProcessCommandList(cmdBuf, cmdBufSize, 0x0); +} + +float C3D_GetCmdBufUsage(void) +{ + return C3Di_GetContext()->cmdBufUsage; +} + +void C3D_Fini(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (ctx->renderQueueExit) + ctx->renderQueueExit(); + + aptUnhook(&hookCookie); + linearFree(ctx->cmdBuf); + ctx->flags = 0; +} + +void C3D_BindProgram(shaderProgram_s* program) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + shaderProgram_s* oldProg = ctx->program; + shaderInstance_s* newGsh = program->geometryShader; + if (oldProg != program) + { + ctx->program = program; + ctx->flags |= C3DiF_Program; + + if (oldProg) + { + if (oldProg->vertexShader->dvle->dvlp != program->vertexShader->dvle->dvlp) + ctx->flags |= C3DiF_VshCode; + shaderInstance_s* oldGsh = oldProg->geometryShader; + if (newGsh && (!oldGsh || oldGsh->dvle->dvlp != newGsh->dvle->dvlp)) + ctx->flags |= C3DiF_GshCode; + } else + ctx->flags |= C3DiF_VshCode | C3DiF_GshCode; + } + + C3Di_LoadShaderUniforms(program->vertexShader); + if (newGsh) + C3Di_LoadShaderUniforms(newGsh); + else + C3Di_ClearShaderUniforms(GPU_GEOMETRY_SHADER); +} + +C3D_FVec* C3D_FixedAttribGetWritePtr(int id) +{ + if (id < 0 || id >= 12) + return NULL; + + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return NULL; + + ctx->fixedAttribDirty |= BIT(id); + ctx->fixedAttribEverDirty |= BIT(id); + return &ctx->fixedAttribs[id]; +} diff --git a/libs/citro3d/source/buffers.c b/libs/citro3d/source/buffers.c new file mode 100644 index 0000000..a7c54b2 --- /dev/null +++ b/libs/citro3d/source/buffers.c @@ -0,0 +1,55 @@ +#include +#include +#include "context.h" + +#define BUFFER_BASE_PADDR 0x18000000 + +void BufInfo_Init(C3D_BufInfo* info) +{ + memset(info, 0, sizeof(*info)); + info->base_paddr = BUFFER_BASE_PADDR; +} + +int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation) +{ + if (info->bufCount == 12) return -1; + int id = info->bufCount++; + + u32 pa = osConvertVirtToPhys(data); + if (pa < info->base_paddr) return -2; + + C3D_BufCfg* buf = &info->buffers[id]; + buf->offset = pa - info->base_paddr; + buf->flags[0] = permutation & 0xFFFFFFFF; + buf->flags[1] = (permutation >> 32) | (stride << 16) | (attribCount << 28); + return id; +} + +C3D_BufInfo* C3D_GetBufInfo(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return NULL; + + ctx->flags |= C3DiF_BufInfo; + return &ctx->bufInfo; +} + +void C3D_SetBufInfo(C3D_BufInfo* info) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (info != &ctx->bufInfo) + memcpy(&ctx->bufInfo, info, sizeof(*info)); + ctx->flags |= C3DiF_BufInfo; +} + +void C3Di_BufInfoBind(C3D_BufInfo* info) +{ + GPUCMD_AddWrite(GPUREG_ATTRIBBUFFERS_LOC, info->base_paddr >> 3); + GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFER0_OFFSET, (u32*)info->buffers, sizeof(info->buffers)/sizeof(u32)); +} diff --git a/libs/citro3d/source/context.h b/libs/citro3d/source/context.h new file mode 100644 index 0000000..48fa053 --- /dev/null +++ b/libs/citro3d/source/context.h @@ -0,0 +1,100 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + u32 fragOpMode; + u32 zScale, zOffset; + GPU_CULLMODE cullMode; + + u32 alphaTest; + u32 stencilMode, stencilOp; + u32 depthTest; + + u32 blendClr; + u32 alphaBlend; + GPU_LOGICOP clrLogicOp; +} C3D_Effect; + +typedef struct +{ + u32* cmdBuf; + size_t cmdBufSize; + float cmdBufUsage; + + u32 flags; + shaderProgram_s* program; + + C3D_AttrInfo attrInfo; + C3D_BufInfo bufInfo; + C3D_Effect effect; + C3D_LightEnv* lightEnv; + + C3D_Tex* tex[3]; + C3D_TexEnv texEnv[6]; + + u32 texEnvBuf, texEnvBufClr; + + C3D_RenderBuf* rb; + u32 viewport[5]; + u32 scissor[3]; + + u16 fixedAttribDirty, fixedAttribEverDirty; + C3D_FVec fixedAttribs[12]; + + void (* renderQueueWaitDone)(void); + void (* renderQueueExit)(void); + +} C3D_Context; + +enum +{ + C3DiF_Active = BIT(0), + C3DiF_DrawUsed = BIT(1), + C3DiF_AttrInfo = BIT(2), + C3DiF_BufInfo = BIT(3), + C3DiF_Effect = BIT(4), + C3DiF_RenderBuf = BIT(5), + C3DiF_Viewport = BIT(6), + C3DiF_Scissor = BIT(7), + C3DiF_Program = BIT(8), + C3DiF_TexEnvBuf = BIT(9), + C3DiF_LightEnv = BIT(10), + C3DiF_VshCode = BIT(11), + C3DiF_GshCode = BIT(12), + C3DiF_CmdBuffer = BIT(13), + +#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; +} + +void C3Di_UpdateContext(void); +void C3Di_AttrInfoBind(C3D_AttrInfo* info); +void C3Di_BufInfoBind(C3D_BufInfo* info); +void C3Di_TexEnvBind(int id, C3D_TexEnv* env); +void C3Di_EffectBind(C3D_Effect* effect); +void C3Di_RenderBufBind(C3D_RenderBuf* rb); + +void C3Di_LightMtlBlend(C3D_Light* light); + +void C3Di_DirtyUniforms(GPU_SHADER_TYPE type); +void C3Di_LoadShaderUniforms(shaderInstance_s* si); +void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type); + +void C3Di_FinalizeFrame(u32** pBuf, u32* pSize); diff --git a/libs/citro3d/source/drawArrays.c b/libs/citro3d/source/drawArrays.c new file mode 100644 index 0000000..07b7720 --- /dev/null +++ b/libs/citro3d/source/drawArrays.c @@ -0,0 +1,31 @@ +#include "context.h" + +void C3D_DrawArrays(GPU_Primitive_t primitive, int first, int size) +{ + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); + // Start a new primitive (breaks off a triangle strip/fan) + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); + // The index buffer is not used, but this command is still required + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); + // Number of vertices + GPUCMD_AddWrite(GPUREG_NUMVERTICES, size); + // First vertex + GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, first); + // Enable array drawing mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 1); + // Enable drawing mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); + // Trigger array drawing + GPUCMD_AddWrite(GPUREG_DRAWARRAYS, 1); + // Go back to configuration mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); + // Disable array drawing mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 0); + // Clear the post-vertex cache + GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); + + C3Di_GetContext()->flags |= C3DiF_DrawUsed; +} diff --git a/libs/citro3d/source/drawElements.c b/libs/citro3d/source/drawElements.c new file mode 100644 index 0000000..ed06d87 --- /dev/null +++ b/libs/citro3d/source/drawElements.c @@ -0,0 +1,44 @@ +#include "context.h" + +void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices) +{ + C3D_Context* ctx = C3Di_GetContext(); + u32 pa = osConvertVirtToPhys(indices); + u32 base = ctx->bufInfo.base_paddr; + if (pa < base) return; + + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); + // Start a new primitive (breaks off a triangle strip/fan) + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); + // Configure the index buffer + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, (pa - base) | (type << 31)); + // Number of vertices + GPUCMD_AddWrite(GPUREG_NUMVERTICES, count); + // First vertex + GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, 0); + // Enable triangle element drawing mode if necessary + if (primitive == GPU_TRIANGLES) + { + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0x100); + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0x100); + } + // Enable drawing mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); + // Trigger element drawing + GPUCMD_AddWrite(GPUREG_DRAWELEMENTS, 1); + // Go back to configuration mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); + // Disable triangle element drawing mode if necessary + if (primitive == GPU_TRIANGLES) + { + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0); + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0); + } + // Clear the post-vertex cache + GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); + + C3Di_GetContext()->flags |= C3DiF_DrawUsed; +} diff --git a/libs/citro3d/source/effect.c b/libs/citro3d/source/effect.c new file mode 100644 index 0000000..edb84f7 --- /dev/null +++ b/libs/citro3d/source/effect.c @@ -0,0 +1,89 @@ +#include "context.h" + +static inline C3D_Effect* getEffect() +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Effect; + return &ctx->effect; +} + +void C3D_DepthMap(float zScale, float zOffset) +{ + C3D_Effect* e = getEffect(); + e->zScale = f32tof24(zScale); + e->zOffset = f32tof24(zOffset); +} + +void C3D_CullFace(GPU_CULLMODE mode) +{ + C3D_Effect* e = getEffect(); + e->cullMode = mode; +} + +void C3D_StencilTest(bool enable, GPU_TESTFUNC function, int ref, int inputMask, int writeMask) +{ + C3D_Effect* e = getEffect(); + e->stencilMode = (!!enable) | ((function & 7) << 4) | (writeMask << 8) | (ref << 16) | (inputMask << 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); + e->fragOpMode &= ~0xFF00; + e->fragOpMode |= 0x0100; +} + +void C3D_ColorLogicOp(GPU_LOGICOP op) +{ + C3D_Effect* e = getEffect(); + e->fragOpMode &= ~0xFF00; + e->clrLogicOp = op; +} + +void C3D_FragOpMode(GPU_FRAGOPMODE mode) +{ + C3D_Effect* e = getEffect(); + e->fragOpMode &= ~0xFF00FF; + e->fragOpMode |= 0xE40000 | mode; +} + +void C3Di_EffectBind(C3D_Effect* e) +{ + GPUCMD_AddWrite(GPUREG_DEPTHMAP_ENABLE, 1); + GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, e->cullMode & 0x3); + GPUCMD_AddIncrementalWrites(GPUREG_DEPTHMAP_SCALE, (u32*)&e->zScale, 2); + GPUCMD_AddIncrementalWrites(GPUREG_FRAGOP_ALPHA_TEST, (u32*)&e->alphaTest, 4); + GPUCMD_AddWrite(GPUREG_BLEND_COLOR, e->blendClr); + GPUCMD_AddWrite(GPUREG_BLEND_FUNC, e->alphaBlend); + GPUCMD_AddWrite(GPUREG_LOGIC_OP, e->clrLogicOp); + GPUCMD_AddMaskedWrite(GPUREG_COLOR_OPERATION, 7, e->fragOpMode); + + // Disable early depth test? + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 1, 0); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0); +} diff --git a/libs/citro3d/source/immediate.c b/libs/citro3d/source/immediate.c new file mode 100644 index 0000000..6ba39ab --- /dev/null +++ b/libs/citro3d/source/immediate.c @@ -0,0 +1,67 @@ +#include "context.h" + +void C3D_ImmDrawBegin(GPU_Primitive_t primitive) +{ + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); + // Start a new primitive (breaks off a triangle strip/fan) + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); + // Not sure if this command is necessary + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); + // Enable vertex submission mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 1); + // Enable drawing mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); + // Begin immediate-mode vertex submission + GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, 0xF); +} + +static inline void write24(u8* p, u32 val) +{ + p[0] = val; + p[1] = val>>8; + p[2] = val>>16; +} + +void C3D_ImmSendAttrib(float x, float y, float z, float w) +{ + union + { + u32 packed[3]; + struct + { + u8 x[3]; + u8 y[3]; + u8 z[3]; + u8 w[3]; + }; + } param; + + // Convert the values to float24 + write24(param.x, f32tof24(x)); + write24(param.y, f32tof24(y)); + write24(param.z, f32tof24(z)); + write24(param.w, f32tof24(w)); + + // Reverse the packed words + u32 p = param.packed[0]; + param.packed[0] = param.packed[2]; + param.packed[2] = p; + + // Send the attribute + GPUCMD_AddIncrementalWrites(GPUREG_FIXEDATTRIB_DATA0, param.packed, 3); +} + +void C3D_ImmDrawEnd(void) +{ + // Go back to configuration mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); + // Disable vertex submission mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 0); + // Clear the post-vertex cache + GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); + + C3Di_GetContext()->flags |= C3DiF_DrawUsed; +} diff --git a/libs/citro3d/source/light.c b/libs/citro3d/source/light.c new file mode 100644 index 0000000..e1d921a --- /dev/null +++ b/libs/citro3d/source/light.c @@ -0,0 +1,157 @@ +#include +#include "context.h" +#include + +void C3Di_LightMtlBlend(C3D_Light* light) +{ + int i; + C3D_Material* mtl = &light->parent->material; + C3D_LightMatConf* conf = &light->conf.material; + memset(conf, 0, sizeof(*conf)); + + for (i = 0; i < 3; i ++) + { + conf->specular0 |= ((u32)(255*(mtl->specular0[i]*light->color[i]))) << (i*10); + conf->specular1 |= ((u32)(255*(mtl->specular1[i]*light->color[i]))) << (i*10); + conf->diffuse |= ((u32)(255*(mtl->diffuse[i] *light->color[i]))) << (i*10); + conf->ambient |= ((u32)(255*(mtl->ambient[i] *light->color[i]))) << (i*10); + } +} + +int C3D_LightInit(C3D_Light* light, C3D_LightEnv* env) +{ + int i; + memset(light, 0, sizeof(*light)); + + for (i = 0; i < 8; i ++) + if (!env->lights[i]) + break; + if (i == 8) return -1; + + env->lights[i] = light; + light->flags = C3DF_Light_Enabled | C3DF_Light_Dirty | C3DF_Light_MatDirty; + light->id = i; + light->parent = env; + + env->flags |= C3DF_LightEnv_LCDirty; + return i; +} + +void C3D_LightEnable(C3D_Light* light, bool enable) +{ + if ((light->flags & C3DF_Light_Enabled) == (enable?C3DF_Light_Enabled:0)) + return; + + if (enable) + light->flags |= C3DF_Light_Enabled; + else + light->flags &= ~C3DF_Light_Enabled; + + light->parent->flags |= C3DF_LightEnv_LCDirty; +} + +void C3D_LightTwoSideDiffuse(C3D_Light* light, bool enable) +{ + if (enable) + light->conf.config |= BIT(1); + else + light->conf.config &= ~BIT(1); + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightGeoFactor(C3D_Light* light, int id, bool enable) +{ + id = 2 + (id&1); + if (enable) + light->conf.config |= BIT(id); + else + light->conf.config &= ~BIT(id); + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightColor(C3D_Light* light, float r, float g, float b) +{ + light->color[0] = b; + light->color[1] = g; + light->color[2] = r; + light->flags |= C3DF_Light_MatDirty; +} + +void C3D_LightPosition(C3D_Light* light, C3D_FVec* pos) +{ + // Enable/disable positional light depending on W coordinate + light->conf.config &= ~BIT(0); + light->conf.config |= (pos->w == 0.0); + light->conf.position[0] = f32tof16(pos->x); + light->conf.position[1] = f32tof16(pos->y); + light->conf.position[2] = f32tof16(pos->z); + light->flags |= C3DF_Light_Dirty; +} + +static void C3Di_EnableCommon(C3D_Light* light, bool enable, u32 bit) +{ + C3D_LightEnv* env = light->parent; + u32* var = &env->conf.config[1]; + + if (enable == !(*var & bit)) + return; + + if (!enable) + *var |= bit; + else + *var &= ~bit; + + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightShadowEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_SHADOWBIT(light->id)); +} + +void C3D_LightSpotEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_SPOTBIT(light->id)); +} + +static inline u16 floattofix2_11(float x) +{ + return (u16)((s32)(x * (1U<<11)) & 0x1FFF); +} + +void C3D_LightSpotDir(C3D_Light* light, float x, float y, float z) +{ + C3Di_EnableCommon(light, true, GPU_LC1_SPOTBIT(light->id)); + C3D_FVec vec = FVec3_New(-x, -y, -z); + vec = FVec3_Normalize(vec); + light->conf.spotDir[0] = floattofix2_11(vec.x); + light->conf.spotDir[1] = floattofix2_11(vec.y); + light->conf.spotDir[2] = floattofix2_11(vec.z); + light->flags |= C3DF_Light_Dirty; +} + +void C3D_LightSpotLut(C3D_Light* light, C3D_LightLut* lut) +{ + bool hasLut = lut != NULL; + C3Di_EnableCommon(light, hasLut, GPU_LC1_SPOTBIT(light->id)); + light->lut_SP = lut; + if (hasLut) + light->flags |= C3DF_Light_SPDirty; +} + +void C3D_LightDistAttnEnable(C3D_Light* light, bool enable) +{ + C3Di_EnableCommon(light, enable, GPU_LC1_ATTNBIT(light->id)); +} + +void C3D_LightDistAttn(C3D_Light* light, C3D_LightLutDA* lut) +{ + bool hasLut = lut != NULL; + C3Di_EnableCommon(light, hasLut, GPU_LC1_ATTNBIT(light->id)); + if (!hasLut) return; + + light->conf.distAttnBias = f32tof20(lut->bias); + light->conf.distAttnScale = f32tof20(lut->scale); + light->lut_DA = &lut->lut; + light->flags |= C3DF_Light_Dirty | C3DF_Light_DADirty; +} diff --git a/libs/citro3d/source/lightenv.c b/libs/citro3d/source/lightenv.c new file mode 100644 index 0000000..182766d --- /dev/null +++ b/libs/citro3d/source/lightenv.c @@ -0,0 +1,270 @@ +#include +#include "context.h" + +static void C3Di_LightEnvMtlBlend(C3D_LightEnv* env) +{ + int i; + C3D_Material* mtl = &env->material; + u32 color = 0; + for (i = 0; i < 3; i ++) + { + int v = 255*(mtl->emission[i] + mtl->ambient[i]*env->ambient[i]); + if (v < 0) v = 0; + else if (v > 255) v = 255; + color |= v << (i*10); + } + env->conf.ambient = color; +} + +static void C3Di_LightLutUpload(u32 config, C3D_LightLut* lut) +{ + int i; + GPUCMD_AddWrite(GPUREG_LIGHTING_LUT_INDEX, config); + for (i = 0; i < 256; i += 8) + GPUCMD_AddWrites(GPUREG_LIGHTING_LUT_DATA0, &lut->data[i], 8); +} + +static void C3Di_LightEnvSelectLayer(C3D_LightEnv* env) +{ + static const u8 layer_enabled[] = + { + BIT(GPU_LUT_D0) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_SP) | BIT(GPU_LUT_DA), + BIT(GPU_LUT_FR) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_SP) | BIT(GPU_LUT_DA), + BIT(GPU_LUT_D0) | BIT(GPU_LUT_D1) | BIT(GPU_LUT_RR) | BIT(GPU_LUT_DA), + BIT(GPU_LUT_D0) | BIT(GPU_LUT_D1) | BIT(GPU_LUT_FR) | BIT(GPU_LUT_DA), + 0xFF &~ BIT(GPU_LUT_FR), + 0xFF &~ BIT(GPU_LUT_D1), + 0xFF &~ (BIT(GPU_LUT_RB) | BIT(GPU_LUT_RG)), + }; + + u32 reg = ~env->conf.config[1]; + if (reg & (0xFF<< 8)) reg |= GPU_LC1_LUTBIT(GPU_LUT_SP); + if (reg & (0xFF<<24)) reg |= GPU_LC1_LUTBIT(GPU_LUT_DA); + reg = (reg >> 16) & 0xFF; + int i; + for (i = 0; i < 7; i ++) + if ((layer_enabled[i] & reg) == reg) // Check if the layer supports all LUTs we need + break; + env->conf.config[0] = (env->conf.config[0] &~ (0xF<<4)) | (GPU_LIGHT_ENV_LAYER_CONFIG(i)<<4); +} + +static void C3Di_LightEnvUpdate(C3D_LightEnv* env) +{ + int i; + C3D_LightEnvConf* conf = &env->conf; + + if (env->flags & C3DF_LightEnv_LCDirty) + { + conf->numLights = 0; + conf->permutation = 0; + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + if (!(light->flags & C3DF_Light_Enabled)) continue; + conf->permutation |= GPU_LIGHTPERM(conf->numLights++, i); + } + if (conf->numLights > 0) conf->numLights --; + env->flags &= ~C3DF_LightEnv_LCDirty; + env->flags |= C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_LightEnv_MtlDirty) + { + C3Di_LightEnvMtlBlend(env); + env->flags &= ~C3DF_LightEnv_MtlDirty; + env->flags |= C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_LightEnv_Dirty) + { + C3Di_LightEnvSelectLayer(env); + GPUCMD_AddWrite(GPUREG_LIGHTING_AMBIENT, conf->ambient); + GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_NUM_LIGHTS, (u32*)&conf->numLights, 3); + GPUCMD_AddIncrementalWrites(GPUREG_LIGHTING_LUTINPUT_ABS, (u32*)&conf->lutInput, 3); + GPUCMD_AddWrite(GPUREG_LIGHTING_LIGHT_PERMUTATION, conf->permutation); + env->flags &= ~C3DF_LightEnv_Dirty; + } + + if (env->flags & C3DF_LightEnv_LutDirtyAll) + { + for (i = 0; i < 6; i ++) + { + static const u8 lutIds[] = { 0, 1, 3, 4, 5, 6 }; + if (!(env->flags & C3DF_LightEnv_LutDirty(i))) continue; + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_COMMON, (u32)lutIds[i], 0), env->luts[i]); + } + + env->flags &= ~C3DF_LightEnv_LutDirtyAll; + } + + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + + if (light->flags & C3DF_Light_MatDirty) + { + C3Di_LightMtlBlend(light); + light->flags &= ~C3DF_Light_MatDirty; + light->flags |= C3DF_Light_Dirty; + } + + if (light->flags & C3DF_Light_Dirty) + { + GPUCMD_AddIncrementalWrites(GPUREG_LIGHT0_SPECULAR0 + i*0x10, (u32*)&light->conf, 12); + light->flags &= ~C3DF_Light_Dirty; + } + + if (light->flags & C3DF_Light_SPDirty) + { + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_SP, i, 0), light->lut_SP); + light->flags &= ~C3DF_Light_SPDirty; + } + + if (light->flags & C3DF_Light_DADirty) + { + C3Di_LightLutUpload(GPU_LIGHTLUTIDX(GPU_LUTSELECT_DA, i, 0), light->lut_DA); + light->flags &= ~C3DF_Light_DADirty; + } + } +} + +static void C3Di_LightEnvDirty(C3D_LightEnv* env) +{ + env->flags |= C3DF_LightEnv_Dirty; + int i; + for (i = 0; i < 6; i ++) + if (env->luts[i]) + env->flags |= C3DF_LightEnv_LutDirty(i); + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (!light) continue; + + light->flags |= C3DF_Light_Dirty; + if (light->lut_SP) + light->flags |= C3DF_Light_SPDirty; + if (light->lut_DA) + light->flags |= C3DF_Light_DADirty; + } +} + +void C3D_LightEnvInit(C3D_LightEnv* env) +{ + memset(env, 0, sizeof(*env)); + env->Update = C3Di_LightEnvUpdate; + env->Dirty = C3Di_LightEnvDirty; + + env->flags = C3DF_LightEnv_Dirty; + env->conf.config[0] = (4<<8) | BIT(27) | BIT(31); + env->conf.config[1] = ~0; + env->conf.lutInput.abs = 0x2222222; +} + +void C3D_LightEnvBind(C3D_LightEnv* env) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (ctx->lightEnv == env) + return; + + ctx->flags |= C3DiF_LightEnv; + ctx->lightEnv = env; +} + +void C3D_LightEnvMaterial(C3D_LightEnv* env, const C3D_Material* mtl) +{ + int i; + memcpy(&env->material, mtl, sizeof(*mtl)); + env->flags |= C3DF_LightEnv_MtlDirty; + for (i = 0; i < 8; i ++) + { + C3D_Light* light = env->lights[i]; + if (light) light->flags |= C3DF_Light_MatDirty; + } +} + +void C3D_LightEnvAmbient(C3D_LightEnv* env, float r, float g, float b) +{ + env->ambient[0] = b; + env->ambient[1] = g; + env->ambient[2] = r; + env->flags |= C3DF_LightEnv_MtlDirty; +} + +void C3D_LightEnvLut(C3D_LightEnv* env, int lutId, int input, bool abs, C3D_LightLut* lut) +{ + static const s8 ids[] = { 0, 1, -1, 2, 3, 4, 5, -1 }; + int id = ids[lutId]; + if (id >= 0) + { + env->luts[id] = lut; + if (lut) + { + env->conf.config[1] &= ~GPU_LC1_LUTBIT(lutId); + env->flags |= C3DF_LightEnv_LutDirty(id); + } else + env->luts[id] = NULL; + } + + env->conf.lutInput.select &= ~GPU_LIGHTLUTINPUT(lutId, 0xF); + env->conf.lutInput.select |= GPU_LIGHTLUTINPUT(lutId, input); + + u32 absbit = 1 << (lutId*4 + 1); + env->conf.lutInput.abs &= ~absbit; + if (!abs) + env->conf.lutInput.abs |= absbit; + + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector) +{ + env->conf.config[0] &= ~(3<<2); + env->conf.config[0] |= (selector&3)<<2; + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode) +{ + env->conf.config[0] &= ~(3<<28); + env->conf.config[0] |= (mode&3)<<28; + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit) +{ + env->conf.config[0] &= ~(3<<22); + env->conf.config[0] |= (texUnit&3)<<22; + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode) +{ + mode &= 0xF<<16; + if (mode & (GPU_SHADOW_PRIMARY | GPU_INVERT_SHADOW | GPU_SHADOW_ALPHA)) + mode |= BIT(0); + env->conf.config[0] &= ~((3<<28) | BIT(0)); + env->conf.config[0] |= mode; + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit) +{ + env->conf.config[0] &= ~(3<<24); + env->conf.config[0] |= (texUnit&3)<<24; + env->flags |= C3DF_LightEnv_Dirty; +} + +void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp) +{ + if (clamp) + env->conf.config[0] |= BIT(27); + else + env->conf.config[0] &= ~BIT(27); + env->flags |= C3DF_LightEnv_Dirty; +} diff --git a/libs/citro3d/source/lightlut.c b/libs/citro3d/source/lightlut.c new file mode 100644 index 0000000..b49534a --- /dev/null +++ b/libs/citro3d/source/lightlut.c @@ -0,0 +1,66 @@ +#include +#include "context.h" + +void LightLut_FromArray(C3D_LightLut* lut, float* data) +{ + int i; + for (i = 0; i < 256; i ++) + { + float in = data[i], diff = data[i+256]; + + u32 val = 0; + if (in > 0.0) + { + in *= 0x1000; + val = (in < 0x1000) ? (u32)in : 0xFFF; + } + + u32 val2 = 0; + if (diff != 0.0) + { + if (diff < 0) + { + diff = -diff; + val2 = 0x800; + } + diff *= 0x800; + val2 |= (diff < 0x800) ? (u32)diff : 0x7FF; + } + + lut->data[i] = val | (val2 << 12); + } +} + +void LightLut_FromFunc(C3D_LightLut* lut, C3D_LightLutFunc func, float param, bool negative) +{ + int i; + float data[512]; + memset(data, 0, sizeof(data)); + int start = negative ? (-127) : 0; + for (i = start; i < 128; i ++) + { + data[i & 0xFF] = func((float)i / 127.0f, param); + if (i != start) data[(i & 0xFF) + 255] = data[i & 0xFF] - data[(i-1) & 0xFF]; + } + LightLut_FromArray(lut, data); +} + +void LightLutDA_Create(C3D_LightLutDA* lut, C3D_LightLutFuncDA func, float from, float to, float arg0, float arg1) +{ + int i; + float data[512]; + data[511] = 0; + + lut->scale = 1.0f / (to-from); + lut->bias = -from*lut->scale; + + for (i = 0; i < 256; i ++) + { + float dist = ((float)i/255 - lut->bias) / lut->scale; + data[i] = func(dist, arg0, arg1); + if (i > 0) + data[i+255] = data[i]-data[i-1]; + } + + LightLut_FromArray(&lut->lut, data); +} diff --git a/libs/citro3d/source/maths/.gitignore b/libs/citro3d/source/maths/.gitignore new file mode 100644 index 0000000..31dc307 --- /dev/null +++ b/libs/citro3d/source/maths/.gitignore @@ -0,0 +1,2 @@ +*.d +*.o diff --git a/libs/citro3d/source/maths/mtx_fromquat.c b/libs/citro3d/source/maths/mtx_fromquat.c new file mode 100644 index 0000000..0568a9c --- /dev/null +++ b/libs/citro3d/source/maths/mtx_fromquat.c @@ -0,0 +1,34 @@ +#include + +void Mtx_FromQuat(C3D_Mtx* m, C3D_FQuat q) +{ + float ii = q.i*q.i; + float ij = q.i*q.j; + float ik = q.i*q.k; + float jj = q.j*q.j; + float jk = q.j*q.k; + float kk = q.k*q.k; + float ri = q.r*q.i; + float rj = q.r*q.j; + float rk = q.r*q.k; + + m->r[0].x = 1.0f - (2.0f * (jj + kk)); + m->r[1].x = 2.0f * (ij + rk); + m->r[2].x = 2.0f * (ik - rj); + m->r[3].x = 0.0f; + + m->r[0].y = 2.0f * (ij - rk); + m->r[1].y = 1.0f - (2.0f * (ii + kk)); + m->r[2].y = 2.0f * (jk + ri); + m->r[3].y = 0.0f; + + m->r[0].z = 2.0f * (ik + rj); + m->r[1].z = 2.0f * (jk - ri); + m->r[2].z = 1.0f - (2.0f * (ii + jj)); + m->r[3].z = 0.0f; + + m->r[0].w = 0.0f; + m->r[1].w = 0.0f; + m->r[2].w = 0.0f; + m->r[3].w = 1.0f; +} diff --git a/libs/citro3d/source/maths/mtx_identity.c b/libs/citro3d/source/maths/mtx_identity.c new file mode 100644 index 0000000..2ce7461 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_identity.c @@ -0,0 +1,10 @@ +#include + +void Mtx_Identity(C3D_Mtx* out) +{ + // http://www.wolframalpha.com/input/?i={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}} + int i; + for (i = 0; i < 16; ++i) + out->m[i] = 0.0f; + out->r[0].x = out->r[1].y = out->r[2].z = out->r[3].w = 1.0f; +} diff --git a/libs/citro3d/source/maths/mtx_inverse.c b/libs/citro3d/source/maths/mtx_inverse.c new file mode 100644 index 0000000..42a0f29 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_inverse.c @@ -0,0 +1,135 @@ +#include +#include + +float Mtx_Inverse(C3D_Mtx* out) +{ + //Mtx_Inverse can be used to calculate the determinant and the inverse of the matrix. + + C3D_Mtx inv; + float det; + int i; + + inv.r[0].x = out->r[1].y * out->r[2].z * out->r[3].w - + out->r[1].y * out->r[2].w * out->r[3].z - + out->r[2].y * out->r[1].z * out->r[3].w + + out->r[2].y * out->r[1].w * out->r[3].z + + out->r[3].y * out->r[1].z * out->r[2].w - + out->r[3].y * out->r[1].w * out->r[2].z; + + inv.r[1].x = -out->r[1].x * out->r[2].z * out->r[3].w + + out->r[1].x * out->r[2].w * out->r[3].z + + out->r[2].x * out->r[1].z * out->r[3].w - + out->r[2].x * out->r[1].w * out->r[3].z - + out->r[3].x * out->r[1].z * out->r[2].w + + out->r[3].x * out->r[1].w * out->r[2].z; + + inv.r[2].x = out->r[1].x * out->r[2].y * out->r[3].w - + out->r[1].x * out->r[2].w * out->r[3].y - + out->r[2].x * out->r[1].y * out->r[3].w + + out->r[2].x * out->r[1].w * out->r[3].y + + out->r[3].x * out->r[1].y * out->r[2].w - + out->r[3].x * out->r[1].w * out->r[2].y; + + inv.r[3].x = -out->r[1].x * out->r[2].y * out->r[3].z + + out->r[1].x * out->r[2].z * out->r[3].y + + out->r[2].x * out->r[1].y * out->r[3].z - + out->r[2].x * out->r[1].z * out->r[3].y - + out->r[3].x * out->r[1].y * out->r[2].z + + out->r[3].x * out->r[1].z * out->r[2].y; + + det = out->r[0].x * inv.r[0].x + out->r[0].y * inv.r[1].x + + out->r[0].z * inv.r[2].x + out->r[0].w * inv.r[3].x; + + if (fabsf(det) < FLT_EPSILON) + //Returns 0.0f if we find the determinant is less than +/- FLT_EPSILON. + return 0.0f; + + inv.r[0].y = -out->r[0].y * out->r[2].z * out->r[3].w + + out->r[0].y * out->r[2].w * out->r[3].z + + out->r[2].y * out->r[0].z * out->r[3].w - + out->r[2].y * out->r[0].w * out->r[3].z - + out->r[3].y * out->r[0].z * out->r[2].w + + out->r[3].y * out->r[0].w * out->r[2].z; + + inv.r[1].y = out->r[0].x * out->r[2].z * out->r[3].w - + out->r[0].x * out->r[2].w * out->r[3].z - + out->r[2].x * out->r[0].z * out->r[3].w + + out->r[2].x * out->r[0].w * out->r[3].z + + out->r[3].x * out->r[0].z * out->r[2].w - + out->r[3].x * out->r[0].w * out->r[2].z; + + inv.r[2].y = -out->r[0].x * out->r[2].y * out->r[3].w + + out->r[0].x * out->r[2].w * out->r[3].y + + out->r[2].x * out->r[0].y * out->r[3].w - + out->r[2].x * out->r[0].w * out->r[3].y - + out->r[3].x * out->r[0].y * out->r[2].w + + out->r[3].x * out->r[0].w * out->r[2].y; + + inv.r[3].y = out->r[0].x * out->r[2].y * out->r[3].z - + out->r[0].x * out->r[2].z * out->r[3].y - + out->r[2].x * out->r[0].y * out->r[3].z + + out->r[2].x * out->r[0].z * out->r[3].y + + out->r[3].x * out->r[0].y * out->r[2].z - + out->r[3].x * out->r[0].z * out->r[2].y; + + inv.r[0].z = out->r[0].y * out->r[1].z * out->r[3].w - + out->r[0].y * out->r[1].w * out->r[3].z - + out->r[1].y * out->r[0].z * out->r[3].w + + out->r[1].y * out->r[0].w * out->r[3].z + + out->r[3].y * out->r[0].z * out->r[1].w - + out->r[3].y * out->r[0].w * out->r[1].z; + + inv.r[1].z = -out->r[0].x * out->r[1].z * out->r[3].w + + out->r[0].x * out->r[1].w * out->r[3].z + + out->r[1].x * out->r[0].z * out->r[3].w - + out->r[1].x * out->r[0].w * out->r[3].z - + out->r[3].x * out->r[0].z * out->r[1].w + + out->r[3].x * out->r[0].w * out->r[1].z; + + inv.r[2].z = out->r[0].x * out->r[1].y * out->r[3].w - + out->r[0].x * out->r[1].w * out->r[3].y - + out->r[1].x * out->r[0].y * out->r[3].w + + out->r[1].x * out->r[0].w * out->r[3].y + + out->r[3].x * out->r[0].y * out->r[1].w - + out->r[3].x * out->r[0].w * out->r[1].y; + + inv.r[3].z = -out->r[0].x * out->r[1].y * out->r[3].z + + out->r[0].x * out->r[1].z * out->r[3].y + + out->r[1].x * out->r[0].y * out->r[3].z - + out->r[1].x * out->r[0].z * out->r[3].y - + out->r[3].x * out->r[0].y * out->r[1].z + + out->r[3].x * out->r[0].z * out->r[1].y; + + inv.r[0].w = -out->r[0].y * out->r[1].z * out->r[2].w + + out->r[0].y * out->r[1].w * out->r[2].z + + out->r[1].y * out->r[0].z * out->r[2].w - + out->r[1].y * out->r[0].w * out->r[2].z - + out->r[2].y * out->r[0].z * out->r[1].w + + out->r[2].y * out->r[0].w * out->r[1].z; + + inv.r[1].w = out->r[0].x * out->r[1].z * out->r[2].w - + out->r[0].x * out->r[1].w * out->r[2].z - + out->r[1].x * out->r[0].z * out->r[2].w + + out->r[1].x * out->r[0].w * out->r[2].z + + out->r[2].x * out->r[0].z * out->r[1].w - + out->r[2].x * out->r[0].w * out->r[1].z; + + inv.r[2].w = -out->r[0].x * out->r[1].y * out->r[2].w + + out->r[0].x * out->r[1].w * out->r[2].y + + out->r[1].x * out->r[0].y * out->r[2].w - + out->r[1].x * out->r[0].w * out->r[2].y - + out->r[2].x * out->r[0].y * out->r[1].w + + out->r[2].x * out->r[0].w * out->r[1].y; + + inv.r[3].w = out->r[0].x * out->r[1].y * out->r[2].z - + out->r[0].x * out->r[1].z * out->r[2].y - + out->r[1].x * out->r[0].y * out->r[2].z + + out->r[1].x * out->r[0].z * out->r[2].y + + out->r[2].x * out->r[0].y * out->r[1].z - + out->r[2].x * out->r[0].z * out->r[1].y; + + for (i = 0; i < 16; i++) + out->m[i] = inv.m[i] / det; + + return det; +} diff --git a/libs/citro3d/source/maths/mtx_lookat.c b/libs/citro3d/source/maths/mtx_lookat.c new file mode 100644 index 0000000..40108d4 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_lookat.c @@ -0,0 +1,37 @@ +#include + +void Mtx_LookAt(C3D_Mtx* out, C3D_FVec cameraPosition, C3D_FVec cameraTarget, C3D_FVec cameraUpVector, bool isLeftHanded) +{ + //Left-handed and Right-handed Look-At matrix functions are DirectX implementations. + //Left-handed: https://msdn.microsoft.com/en-us/library/windows/desktop/bb281710 + //Right-handed: https://msdn.microsoft.com/en-us/library/windows/desktop/bb281711 + C3D_FVec xaxis, yaxis, zaxis; + + //Order of operations is crucial. + if (isLeftHanded) + zaxis = FVec3_Normalize(FVec3_Subtract(cameraTarget, cameraPosition)); + else + zaxis = FVec3_Normalize(FVec3_Subtract(cameraPosition, cameraTarget)); + xaxis = FVec3_Normalize(FVec3_Cross(cameraUpVector, zaxis)); + yaxis = FVec3_Cross(zaxis, xaxis); + + out->r[0].x = xaxis.x; + out->r[0].y = xaxis.y; + out->r[0].z = xaxis.z; + out->r[0].w = -FVec3_Dot(xaxis, cameraPosition); + + out->r[1].x = yaxis.x; + out->r[1].y = yaxis.y; + out->r[1].z = yaxis.z; + out->r[1].w = -FVec3_Dot(yaxis, cameraPosition); + + out->r[2].x = zaxis.x; + out->r[2].y = zaxis.y; + out->r[2].z = zaxis.z; + out->r[2].w = -FVec3_Dot(zaxis, cameraPosition); + + out->r[3].x = 0.0f; + out->r[3].y = 0.0f; + out->r[3].z = 0.0f; + out->r[3].w = 1.0f; +} diff --git a/libs/citro3d/source/maths/mtx_multiply.c b/libs/citro3d/source/maths/mtx_multiply.c new file mode 100644 index 0000000..f7d5b54 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_multiply.c @@ -0,0 +1,19 @@ +#include + +void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b) +{ + // if out is a or b, then we need to avoid overwriting them + if(out == a || out == b) + { + C3D_Mtx tmp; + Mtx_Multiply(&tmp, a, b); + Mtx_Copy(out, &tmp); + return; + } + + // http://www.wolframalpha.com/input/?i={{a,b,c,d},{e,f,g,h},{i,j,k,l},{m,n,o,p}}{{α,β,γ,δ},{ε,θ,ι,κ},{λ,μ,ν,ξ},{ο,π,ρ,σ}} + int i, j; + for (j = 0; j < 4; ++j) + for (i = 0; i < 4; ++i) + 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/libs/citro3d/source/maths/mtx_multiplyfvec3.c b/libs/citro3d/source/maths/mtx_multiplyfvec3.c new file mode 100644 index 0000000..cbb984c --- /dev/null +++ b/libs/citro3d/source/maths/mtx_multiplyfvec3.c @@ -0,0 +1,11 @@ +#include + +C3D_FVec Mtx_MultiplyFVec3(const C3D_Mtx* mtx, C3D_FVec v) +{ + // http://www.wolframalpha.com/input/?i={{a,b,c},{d,e,f},{g,h,i}}{x,y,z} + float x = FVec3_Dot(mtx->r[0], v); + float y = FVec3_Dot(mtx->r[1], v); + float z = FVec3_Dot(mtx->r[2], v); + + return FVec3_New(x, y, z); +} diff --git a/libs/citro3d/source/maths/mtx_multiplyfvec4.c b/libs/citro3d/source/maths/mtx_multiplyfvec4.c new file mode 100644 index 0000000..1520d38 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_multiplyfvec4.c @@ -0,0 +1,12 @@ +#include + +C3D_FVec Mtx_MultiplyFVec4(const C3D_Mtx* mtx, C3D_FVec v) +{ + // 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} + float x = FVec4_Dot(mtx->r[0], v); + float y = FVec4_Dot(mtx->r[1], v); + float z = FVec4_Dot(mtx->r[2], v); + float w = FVec4_Dot(mtx->r[3], v); + + return FVec4_New(x, y, z, w); +} diff --git a/libs/citro3d/source/maths/mtx_ortho.c b/libs/citro3d/source/maths/mtx_ortho.c new file mode 100644 index 0000000..ba27fb5 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_ortho.c @@ -0,0 +1,20 @@ +#include + +void Mtx_Ortho(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded) +{ + Mtx_Zeros(mtx); + + // Standard orthogonal projection matrix, with a fixed depth range of [-1,0] (required by PICA) + // 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}} + + mtx->r[0].x = 2.0f / (right - left); + mtx->r[0].w = (left + right) / (left - right); + mtx->r[1].y = 2.0f / (top - bottom); + mtx->r[1].w = (bottom + top) / (bottom - top); + if (isLeftHanded) + mtx->r[2].z = 1.0f / (far - near); + else + mtx->r[2].z = 1.0f / (near - far); + mtx->r[2].w = 0.5f*(near + far) / (near - far) - 0.5f; + mtx->r[3].w = 1.0f; +} diff --git a/libs/citro3d/source/maths/mtx_orthotilt.c b/libs/citro3d/source/maths/mtx_orthotilt.c new file mode 100644 index 0000000..8ebcf3c --- /dev/null +++ b/libs/citro3d/source/maths/mtx_orthotilt.c @@ -0,0 +1,21 @@ +#include + +void Mtx_OrthoTilt(C3D_Mtx* mtx, float left, float right, float bottom, float top, float near, float far, bool isLeftHanded) +{ + Mtx_Zeros(mtx); + + // 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) + // 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}} + // 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}} + + mtx->r[0].y = 2.0f / (top - bottom); + mtx->r[0].w = (bottom + top) / (bottom - top); + mtx->r[1].x = 2.0f / (left - right); + mtx->r[1].w = (left + right) / (right - left); + if (isLeftHanded) + mtx->r[2].z = 1.0f / (far - near); + else + mtx->r[2].z = 1.0f / (near - far); + mtx->r[2].w = 0.5f*(near + far) / (near - far) - 0.5f; + mtx->r[3].w = 1.0f; +} diff --git a/libs/citro3d/source/maths/mtx_persp.c b/libs/citro3d/source/maths/mtx_persp.c new file mode 100644 index 0000000..a07a6c2 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_persp.c @@ -0,0 +1,26 @@ +#include + +void Mtx_Persp(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, bool isLeftHanded) +{ + float fovy_tan = tanf(fovy/2.0f); + + Mtx_Zeros(mtx); + + // Standard perspective projection matrix, with a fixed depth range of [-1,0] (required by PICA) + // http://www.wolframalpha.com/input/?i={{1,0,0,0},{0,1,0,0},{0,0,0.5,-0.5},{0,0,0,1}}{{1/(a*tan(v)),0,0,0},{0,1/tan(v),0,0},{0,0,(n%2Bf)/(n-f),(fn)/(n-f)},{0,0,0,-1}} + + mtx->r[0].x = 1.0f / (aspect * fovy_tan); + mtx->r[1].y = 1.0f / fovy_tan; + mtx->r[2].w = far*near / (near - far); + + if (isLeftHanded) + { + mtx->r[2].z = 0.5f*(far + near) / (far - near) - 0.5f; + mtx->r[3].z = 1.0f; + } + else + { + mtx->r[2].z = 0.5f*(far + near) / (near - far) + 0.5f; + mtx->r[3].z = -1.0f; + } +} diff --git a/libs/citro3d/source/maths/mtx_perspstereo.c b/libs/citro3d/source/maths/mtx_perspstereo.c new file mode 100644 index 0000000..74e6f77 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_perspstereo.c @@ -0,0 +1,28 @@ +#include + +void Mtx_PerspStereo(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded) +{ + float fovy_tan = tanf(fovy/2.0f); + float fovy_tan_aspect = fovy_tan*aspect; + float shift = iod / (2.0f*screen); // 'near' not in the numerator because it cancels out in mp.r[1].z + + Mtx_Zeros(mtx); + + mtx->r[0].x = 1.0f / fovy_tan_aspect; + mtx->r[0].w = -iod / 2.0f; + mtx->r[1].y = 1.0f / fovy_tan; + mtx->r[2].w = near * far / (near - far); + + if (isLeftHanded) + { + mtx->r[0].z = shift / fovy_tan_aspect; + mtx->r[2].z = 0.5f*(near + far) / (far - near) - 0.5f; + mtx->r[3].z = 1.0f; + } + else + { + mtx->r[0].z = -shift / fovy_tan_aspect; + mtx->r[2].z = 0.5f*(near + far) / (near - far) + 0.5f; + mtx->r[3].z = -1.0f; + } +} diff --git a/libs/citro3d/source/maths/mtx_perspstereotilt.c b/libs/citro3d/source/maths/mtx_perspstereotilt.c new file mode 100644 index 0000000..b74da90 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_perspstereotilt.c @@ -0,0 +1,33 @@ +#include + +void Mtx_PerspStereoTilt(C3D_Mtx* mtx, float fovx, float invaspect, float near, float far, float iod, float screen, bool isLeftHanded) +{ + // Notes: + // Once again, we are passed "fovy" and the "aspect ratio"; however the 3DS screens are sideways, + // and the formula had to be tweaked. With stereo, left/right separation becomes top/bottom separation. + // The detailed mathematical explanation is in mtx_persptilt.c. + + float fovx_tan = tanf(fovx/2.0f); + float fovx_tan_invaspect = fovx_tan*invaspect; + float shift = iod / (2.0f*screen); // 'near' not in the numerator because it cancels out in mp.r[1].z + + Mtx_Zeros(mtx); + + mtx->r[0].y = 1.0f / fovx_tan; + mtx->r[1].x = -1.0f / fovx_tan_invaspect; + mtx->r[1].w = iod / 2.0f; + mtx->r[2].w = near * far / (near - far); + + if (isLeftHanded) + { + mtx->r[1].z = -shift / fovx_tan_invaspect; + mtx->r[2].z = 0.5f*(near + far) / (far - near) - 0.5f; + mtx->r[3].z = 1.0f; + } + else + { + mtx->r[1].z = shift / fovx_tan_invaspect; + mtx->r[2].z = 0.5f*(near + far) / (near - far) + 0.5f; + mtx->r[3].z = -1.0f; + } +} diff --git a/libs/citro3d/source/maths/mtx_persptilt.c b/libs/citro3d/source/maths/mtx_persptilt.c new file mode 100644 index 0000000..b3ea9e9 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_persptilt.c @@ -0,0 +1,46 @@ +#include + +void Mtx_PerspTilt(C3D_Mtx* mtx, float fovx, float invaspect, float near, float far, bool isLeftHanded) +{ + // 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: + // Includes adjusting depth range from [-1,1] to [-1,0] + // Includes rotation of the matrix one quarter of a turn clockwise in order to fix the 3DS screens' orientation + + // 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.0f); + + Mtx_Zeros(mtx); + + mtx->r[0].y = 1.0f / fovx_tan; + mtx->r[1].x = -1.0f / (fovx_tan*invaspect); + mtx->r[2].w = far*near / (near - far); + + if (isLeftHanded) + { + mtx->r[2].z = 0.5f*(far + near) / (far - near) - 0.5f; + mtx->r[3].z = 1.0f; + } + else + { + mtx->r[2].z = 0.5f*(far + near) / (near - far) + 0.5f; + mtx->r[3].z = -1.0f; + } +} diff --git a/libs/citro3d/source/maths/mtx_rotate.c b/libs/citro3d/source/maths/mtx_rotate.c new file mode 100644 index 0000000..95aea2f --- /dev/null +++ b/libs/citro3d/source/maths/mtx_rotate.c @@ -0,0 +1,72 @@ +#include + +void Mtx_Rotate(C3D_Mtx* mtx, C3D_FVec axis, float angle, bool bRightSide) +{ + size_t i; + C3D_Mtx om; + + float s = sinf(angle); + float c = cosf(angle); + float t = 1.0f - c; + + axis = FVec3_Normalize(axis); + + float x = axis.x; + float y = axis.y; + float z = axis.z; + float w; + + om.r[0].x = t*x*x + c; + om.r[1].x = t*x*y + s*z; + om.r[2].x = t*x*z - s*y; + //om.r[3].x = 0.0f; //optimized out + + om.r[0].y = t*y*x - s*z; + om.r[1].y = t*y*y + c; + om.r[2].y = t*y*z + s*x; + //om.r[3].y = 0.0f; //optimized out + + om.r[0].z = t*z*x + s*y; + om.r[1].z = t*z*y - s*x; + om.r[2].z = t*z*z + c; + //om.r[3].z = 0.0f; //optimized out + + /* optimized out + om.r[0].w = 0.0f; + om.r[1].w = 0.0f; + om.r[2].w = 0.0f; + om.r[3].w = 1.0f; + */ + + if (bRightSide) + { + for (i = 0; i < 4; ++i) + { + 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; + 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; + 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; + + mtx->r[i].x = x; + mtx->r[i].y = y; + mtx->r[i].z = z; + } + } + else + { + for (i = 0; i < 3; ++i) + { + 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; + 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; + 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; + 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; + + om.r[i].x = x; + om.r[i].y = y; + om.r[i].z = z; + om.r[i].w = w; + } + + for (i = 0; i < 3; ++i) + mtx->r[i] = om.r[i]; + } +} diff --git a/libs/citro3d/source/maths/mtx_rotatex.c b/libs/citro3d/source/maths/mtx_rotatex.c new file mode 100644 index 0000000..712aaec --- /dev/null +++ b/libs/citro3d/source/maths/mtx_rotatex.c @@ -0,0 +1,30 @@ +#include + +void Mtx_RotateX(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + float a, b; + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + size_t i; + + if (bRightSide) + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[i].y*cosAngle + mtx->r[i].z*sinAngle; + b = mtx->r[i].z*cosAngle - mtx->r[i].y*sinAngle; + mtx->r[i].y = a; + mtx->r[i].z = b; + } + } + else + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[1].c[i]*cosAngle - mtx->r[2].c[i]*sinAngle; + b = mtx->r[2].c[i]*cosAngle + mtx->r[1].c[i]*sinAngle; + mtx->r[1].c[i] = a; + mtx->r[2].c[i] = b; + } + } +} diff --git a/libs/citro3d/source/maths/mtx_rotatey.c b/libs/citro3d/source/maths/mtx_rotatey.c new file mode 100644 index 0000000..7ee9598 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_rotatey.c @@ -0,0 +1,30 @@ +#include + +void Mtx_RotateY(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + float a, b; + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + size_t i; + + if (bRightSide) + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[i].x*cosAngle - mtx->r[i].z*sinAngle; + b = mtx->r[i].z*cosAngle + mtx->r[i].x*sinAngle; + mtx->r[i].x = a; + mtx->r[i].z = b; + } + } + else + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[0].c[i]*cosAngle + mtx->r[2].c[i]*sinAngle; + b = mtx->r[2].c[i]*cosAngle - mtx->r[0].c[i]*sinAngle; + mtx->r[0].c[i] = a; + mtx->r[2].c[i] = b; + } + } +} diff --git a/libs/citro3d/source/maths/mtx_rotatez.c b/libs/citro3d/source/maths/mtx_rotatez.c new file mode 100644 index 0000000..42963a8 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_rotatez.c @@ -0,0 +1,30 @@ +#include + +void Mtx_RotateZ(C3D_Mtx* mtx, float angle, bool bRightSide) +{ + float a, b; + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + size_t i; + + if (bRightSide) + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[i].x*cosAngle + mtx->r[i].y*sinAngle; + b = mtx->r[i].y*cosAngle - mtx->r[i].x*sinAngle; + mtx->r[i].x = a; + mtx->r[i].y = b; + } + } + else + { + for (i = 0; i < 4; ++i) + { + a = mtx->r[0].c[i]*cosAngle - mtx->r[1].c[i]*sinAngle; + b = mtx->r[1].c[i]*cosAngle + mtx->r[0].c[i]*sinAngle; + mtx->r[0].c[i] = a; + mtx->r[1].c[i] = b; + } + } +} diff --git a/libs/citro3d/source/maths/mtx_scale.c b/libs/citro3d/source/maths/mtx_scale.c new file mode 100644 index 0000000..5d7ce60 --- /dev/null +++ b/libs/citro3d/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/libs/citro3d/source/maths/mtx_translate.c b/libs/citro3d/source/maths/mtx_translate.c new file mode 100644 index 0000000..30098a3 --- /dev/null +++ b/libs/citro3d/source/maths/mtx_translate.c @@ -0,0 +1,21 @@ +#include + +void Mtx_Translate(C3D_Mtx* mtx, float x, float y, float z, bool bRightSide) +{ + + C3D_FVec v = FVec4_New(x, y, z, 1.0f); + int i, j; + + if (bRightSide) + { + for (i = 0; i < 4; ++i) + mtx->r[i].w = FVec4_Dot(mtx->r[i], v); + } + else + { + for (j = 0; j < 3; ++j) + for (i = 0; i < 4; ++i) + mtx->r[j].c[i] += mtx->r[3].c[i] * v.c[3-j]; + } + +} diff --git a/libs/citro3d/source/maths/quat_crossfvec3.c b/libs/citro3d/source/maths/quat_crossfvec3.c new file mode 100644 index 0000000..2265f39 --- /dev/null +++ b/libs/citro3d/source/maths/quat_crossfvec3.c @@ -0,0 +1,13 @@ +#include + +C3D_FVec Quat_CrossFVec3(C3D_FQuat q, C3D_FVec v) +{ + C3D_FVec qv = FVec3_New(q.i, q.j, q.k); + C3D_FVec uv = FVec3_Cross(qv, v); + C3D_FVec uuv = FVec3_Cross(qv, uv); + + uv = FVec3_Scale(uv, 2.0f * q.r); + uuv = FVec3_Scale(uuv, 2.0f); + + return FVec3_Add(v, FVec3_Add(uv, uuv)); +} diff --git a/libs/citro3d/source/maths/quat_multiply.c b/libs/citro3d/source/maths/quat_multiply.c new file mode 100644 index 0000000..532b26f --- /dev/null +++ b/libs/citro3d/source/maths/quat_multiply.c @@ -0,0 +1,11 @@ +#include + +C3D_FQuat Quat_Multiply(C3D_FQuat lhs, C3D_FQuat rhs) +{ + float i = lhs.r*rhs.i + lhs.i*rhs.r + lhs.j*rhs.k - lhs.k*rhs.j; + float j = lhs.r*rhs.j + lhs.j*rhs.r + lhs.k*rhs.i - lhs.i*rhs.k; + float k = lhs.r*rhs.k + lhs.k*rhs.r + lhs.i*rhs.j - lhs.j*rhs.i; + float r = lhs.r*rhs.r - lhs.i*rhs.i - lhs.j*rhs.j - lhs.k*rhs.k; + + return Quat_New(i, j, k, r); +} diff --git a/libs/citro3d/source/maths/quat_pow.c b/libs/citro3d/source/maths/quat_pow.c new file mode 100644 index 0000000..0e1f264 --- /dev/null +++ b/libs/citro3d/source/maths/quat_pow.c @@ -0,0 +1,23 @@ +#include +#include + +C3D_FQuat Quat_Pow(C3D_FQuat q, float p) +{ + // if the power is very near to zero, return the identity quaternion to avoid blowing up with division + if (p > -FLT_EPSILON && p < FLT_EPSILON) + return Quat_Identity(); + + float mag = FVec4_Magnitude(q); + + // if the magnitude is very near to one, this is equivalent to raising the real component by the power + // also, acosf(1) == 0 and sinf(0) == 0 so you would get a divide-by-zero anyway + if (fabsf(q.r / mag) > 1.0f - FLT_EPSILON && fabsf(q.r / mag) < 1.0f + FLT_EPSILON) + return Quat_New(0.0f, 0.0f, 0.0f, powf(q.r, p)); + + float angle = acosf(q.r / mag); + float newAngle = angle * p; + float div = sinf(newAngle) / sinf(angle); + float Mag = powf(mag, p - 1.0f); + + return Quat_New(q.i*div*Mag, q.j*div*Mag, q.k*div*Mag, cosf(newAngle)*mag*Mag); +} diff --git a/libs/citro3d/source/maths/quat_rotate.c b/libs/citro3d/source/maths/quat_rotate.c new file mode 100644 index 0000000..ba70880 --- /dev/null +++ b/libs/citro3d/source/maths/quat_rotate.c @@ -0,0 +1,16 @@ +#include + +C3D_FQuat Quat_Rotate(C3D_FQuat q, C3D_FVec axis, float r, bool bRightSide) +{ + float halfAngle = r/2.0f; + float s = sinf(halfAngle); + + axis = FVec3_Normalize(axis); + + C3D_FQuat tmp = Quat_New(axis.x*s, axis.y*s, axis.z*s, cosf(halfAngle)); + + if (bRightSide) + return Quat_Multiply(tmp, q); + else + return Quat_Multiply(q, tmp); +} diff --git a/libs/citro3d/source/maths/quat_rotatex.c b/libs/citro3d/source/maths/quat_rotatex.c new file mode 100644 index 0000000..4b0feed --- /dev/null +++ b/libs/citro3d/source/maths/quat_rotatex.c @@ -0,0 +1,12 @@ +#include + +C3D_FQuat Quat_RotateX(C3D_FQuat q, float r, bool bRightSide) +{ + float c = cosf(r/2.0f); + float s = sinf(r/2.0f); + + if (bRightSide) + 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); + else + 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); +} diff --git a/libs/citro3d/source/maths/quat_rotatey.c b/libs/citro3d/source/maths/quat_rotatey.c new file mode 100644 index 0000000..c3a7993 --- /dev/null +++ b/libs/citro3d/source/maths/quat_rotatey.c @@ -0,0 +1,12 @@ +#include + +C3D_FQuat Quat_RotateY(C3D_FQuat q, float r, bool bRightSide) +{ + float c = cosf(r/2.0f); + float s = sinf(r/2.0f); + + if (bRightSide) + 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); + else + 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); +} diff --git a/libs/citro3d/source/maths/quat_rotatez.c b/libs/citro3d/source/maths/quat_rotatez.c new file mode 100644 index 0000000..09c92ec --- /dev/null +++ b/libs/citro3d/source/maths/quat_rotatez.c @@ -0,0 +1,12 @@ +#include + +C3D_FQuat Quat_RotateZ(C3D_FQuat q, float r, bool bRightSide) +{ + float c = cosf(r/2.0f); + float s = sinf(r/2.0f); + + if (bRightSide) + 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); + else + 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); +} diff --git a/libs/citro3d/source/mtxstack.c b/libs/citro3d/source/mtxstack.c new file mode 100644 index 0000000..87b9a06 --- /dev/null +++ b/libs/citro3d/source/mtxstack.c @@ -0,0 +1,43 @@ +#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, GPU_SHADER_TYPE unifType, int unifPos, int unifLen) +{ + stk->unifType = unifType; + stk->unifPos = unifPos; + stk->unifLen = unifLen; + stk->isDirty = true; +} + +C3D_Mtx* MtxStack_Push(C3D_MtxStack* stk) +{ + if (stk->pos == (C3D_MTXSTACK_SIZE-1)) return NULL; + stk->pos ++; + Mtx_Copy(&stk->m[stk->pos], &stk->m[stk->pos-1]); + return MtxStack_Cur(stk); +} + +C3D_Mtx* MtxStack_Pop(C3D_MtxStack* stk) +{ + if (stk->pos == 0) return NULL; + stk->pos --; + return MtxStack_Cur(stk); +} + +void MtxStack_Update(C3D_MtxStack* stk) +{ + if (!stk->isDirty) return; + + if (stk->unifPos != 0xFF) + C3D_FVUnifMtxNx4(stk->unifType, stk->unifPos, &stk->m[stk->pos], stk->unifLen); + + stk->isDirty = false; +} diff --git a/libs/citro3d/source/renderbuffer.c b/libs/citro3d/source/renderbuffer.c new file mode 100644 index 0000000..30db0f8 --- /dev/null +++ b/libs/citro3d/source/renderbuffer.c @@ -0,0 +1,110 @@ +#include "context.h" +#include + +static const u8 colorFmtSizes[] = {2,1,0,0,0}; +static const u8 depthFmtSizes[] = {0,0,1,2}; + +static inline u16 getColorBufFillFlag(int fmt) +{ + if (fmt < 0) return 0; + return BIT(0) | ((u32)colorFmtSizes[fmt] << 8); +} + +static inline u16 getDepthBufFillFlag(int fmt) +{ + if (fmt < 0) return 0; + return BIT(0) | ((u32)depthFmtSizes[fmt] << 8); +} + +static inline u32 getColorBufFormatReg(int fmt) +{ + return (fmt << 16) | colorFmtSizes[fmt]; +} + +static inline GPU_TEXCOLOR colorFmtFromDepthFmt(int fmt) +{ + switch (fmt) + { + case GPU_RB_DEPTH16: + return GPU_RGB565; + case GPU_RB_DEPTH24: + return GPU_RGB8; + case GPU_RB_DEPTH24_STENCIL8: + return GPU_RGBA8; + default: + return 0; + } +} + +bool C3D_RenderBufInit(C3D_RenderBuf* rb, int width, int height, int colorFmt, int depthFmt) +{ + memset(rb, 0, sizeof(*rb)); + + rb->depthFmt = depthFmt; + rb->clearColor = rb->clearDepth = 0; + + if (colorFmt < 0) + return false; + if (!C3D_TexInitVRAM(&rb->colorBuf, width, height, colorFmt)) + return false; + + if (depthFmt >= 0) + { + if (!C3D_TexInitVRAM(&rb->depthBuf, width, height, colorFmtFromDepthFmt(depthFmt))) + { + C3D_TexDelete(&rb->colorBuf); + return false; + } + } + + return true; +} + +void C3D_RenderBufClearAsync(C3D_RenderBuf* rb) +{ + GX_MemoryFill( + (u32*)rb->colorBuf.data, rb->clearColor, (u32*)((u8*)rb->colorBuf.data+rb->colorBuf.size), getColorBufFillFlag(rb->colorBuf.fmt), + (u32*)rb->depthBuf.data, rb->clearDepth, (u32*)((u8*)rb->depthBuf.data+rb->depthBuf.size), getDepthBufFillFlag(rb->depthFmt)); +} + +void C3D_RenderBufTransferAsync(C3D_RenderBuf* rb, u32* frameBuf, u32 flags) +{ + u32 dim = GX_BUFFER_DIM((u32)rb->colorBuf.width, (u32)rb->colorBuf.height); + GX_DisplayTransfer((u32*)rb->colorBuf.data, dim, frameBuf, dim, flags); +} + +void C3D_RenderBufBind(C3D_RenderBuf* rb) +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_RenderBuf; + ctx->rb = rb; + C3D_SetViewport(0, 0, rb->colorBuf.width, rb->colorBuf.height); +} + +void C3Di_RenderBufBind(C3D_RenderBuf* rb) +{ + u32 param[4] = { 0, 0, 0, 0 }; + + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); + + param[0] = osConvertVirtToPhys(rb->depthBuf.data) >> 3; + param[1] = osConvertVirtToPhys(rb->colorBuf.data) >> 3; + param[2] = 0x01000000 | (((u32)(rb->colorBuf.height-1) & 0xFFF) << 12) | (rb->colorBuf.width & 0xFFF); + GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, param, 3); + + GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, param[2]); + GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, rb->depthFmt >= 0 ? rb->depthFmt : GPU_RB_DEPTH16); + GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, getColorBufFormatReg(rb->colorBuf.fmt)); + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, 0x00000000); //? + + // Enable or disable color/depth buffers + param[0] = param[1] = rb->colorBuf.data ? 0xF : 0; + param[2] = param[3] = rb->depthBuf.data ? 0x2 : 0; + GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, param, 4); +} + +void C3D_RenderBufDelete(C3D_RenderBuf* rb) +{ + C3D_TexDelete(&rb->colorBuf); + C3D_TexDelete(&rb->depthBuf); +} diff --git a/libs/citro3d/source/renderqueue.c b/libs/citro3d/source/renderqueue.c new file mode 100644 index 0000000..67dd13b --- /dev/null +++ b/libs/citro3d/source/renderqueue.c @@ -0,0 +1,404 @@ +#include "context.h" +#include +#include +#include + +static C3D_RenderTarget *firstTarget, *lastTarget; +static C3D_RenderTarget *linkedTarget[3]; +static C3D_RenderTarget *transferQueue, *clearQueue; + +static struct +{ + C3D_RenderTarget* targetList; + u32* cmdBuf; + u32 cmdBufSize; + u8 flags; +} queuedFrame[2]; +static u8 queueSwap, queuedCount, queuedState; + +static bool inFrame, inSafeTransfer, inSafeClear; + +static void onRenderFinish(void* unused); +static void onTransferFinish(void* unused); +static void onClearDone(void* unused); + +static void performDraw(void) +{ + gspSetEventCallback(GSPGPU_EVENT_P3D, onRenderFinish, NULL, true); + GX_ProcessCommandList(queuedFrame[queueSwap].cmdBuf, queuedFrame[queueSwap].cmdBufSize, queuedFrame[queueSwap].flags); +} + +static void performTransfer(void) +{ + if (inSafeTransfer) return; // Let the safe transfer finish handler retry this + C3D_RenderBuf* renderBuf = &transferQueue->renderBuf; + u32* frameBuf = (u32*)gfxGetFramebuffer(transferQueue->screen, transferQueue->side, NULL, NULL); + if (transferQueue->side == GFX_LEFT) + gfxConfigScreen(transferQueue->screen, false); + gspSetEventCallback(GSPGPU_EVENT_PPF, onTransferFinish, NULL, true); + C3D_RenderBufTransferAsync(renderBuf, frameBuf, transferQueue->transferFlags); +} + +static void performClear(void) +{ + if (inSafeClear) return; // Let the safe clear finish handler retry this + C3D_RenderBuf* renderBuf = &clearQueue->renderBuf; + // TODO: obey renderBuf->clearBits + gspSetEventCallback(renderBuf->colorBuf.data ? GSPGPU_EVENT_PSC0 : GSPGPU_EVENT_PSC1, onClearDone, NULL, true); + C3D_RenderBufClearAsync(renderBuf); +} + +static void updateFrameQueue(void) +{ + C3D_RenderTarget* a; + if (queuedState>0) return; // Still rendering + + // Check that all targets are OK to be drawn on + for (a = queuedFrame[queueSwap].targetList; a; a = a->frame[queueSwap]) + if (!a->drawOk) + return; // Nope, we can't start rendering yet + + // Start rendering the frame + queuedState=1; + for (a = queuedFrame[queueSwap].targetList; a; a = a->frame[queueSwap]) + a->drawOk = false; + performDraw(); +} + +static void transferTarget(C3D_RenderTarget* target) +{ + C3D_RenderTarget* a; + target->transferOk = false; + target->link = NULL; + if (!transferQueue) + { + transferQueue = target; + performTransfer(); + return; + } + for (a = transferQueue; a->link; a = a->link); + a->link = target; +} + +static void clearTarget(C3D_RenderTarget* target) +{ + C3D_RenderTarget* a; + target->link = NULL; + if (!clearQueue) + { + clearQueue = target; + performClear(); + return; + } + for (a = clearQueue; a->link; a = a->link); + a->link = target; +} + +static void onVBlank0(void* unused) +{ + if (!linkedTarget[0]) return; + + if (gfxIs3D()) + { + if (linkedTarget[1] && linkedTarget[1]->transferOk) + transferTarget(linkedTarget[1]); + else if (linkedTarget[0]->transferOk) + { + // Use a temporary copy of the left framebuffer to fill in the missing right image. + static C3D_RenderTarget temp; + memcpy(&temp, linkedTarget[0], sizeof(temp)); + temp.side = GFX_RIGHT; + temp.clearBits = false; + transferTarget(&temp); + } + } + if (linkedTarget[0]->transferOk) + transferTarget(linkedTarget[0]); +} + +static void onVBlank1(void* unused) +{ + if (linkedTarget[2] && linkedTarget[2]->transferOk) + transferTarget(linkedTarget[2]); +} + +void onRenderFinish(void* unused) +{ + C3D_RenderTarget *a, *next; + + // The following check should never trigger + if (queuedState!=1) svcBreak(USERBREAK_PANIC); + + for (a = queuedFrame[queueSwap].targetList; a; a = next) + { + next = a->frame[queueSwap]; + a->frame[queueSwap] = NULL; + if (a->linked) + a->transferOk = true; + else if (a->clearBits) + clearTarget(a); + else + a->drawOk = true; + } + + // Consume the frame that has been just rendered + memset(&queuedFrame[queueSwap], 0, sizeof(queuedFrame[queueSwap])); + queueSwap ^= 1; + queuedCount--; + queuedState = 0; + + // Update the frame queue if there are still frames to render + if (queuedCount>0) + updateFrameQueue(); +} + +void onTransferFinish(void* unused) +{ + C3D_RenderTarget* target = transferQueue; + if (inSafeTransfer) + { + inSafeTransfer = false; + // Try again if there are queued transfers + if (target) + performTransfer(); + return; + } + transferQueue = target->link; + if (target->clearBits) + clearTarget(target); + else + target->drawOk = true; + if (transferQueue) + performTransfer(); + if (target->drawOk && queuedCount>0 && queuedState==0) + updateFrameQueue(); +} + +void onClearDone(void* unused) +{ + C3D_RenderTarget* target = clearQueue; + if (inSafeClear) + { + inSafeClear = false; + // Try again if there are queued clears + if (target) + performClear(); + return; + } + clearQueue = target->link; + target->drawOk = true; + if (clearQueue) + performClear(); + if (queuedCount>0 && queuedState==0) + updateFrameQueue(); +} + +static void C3Di_RenderQueueInit(void) +{ + gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false); + gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false); +} + +static void C3Di_RenderQueueExit(void) +{ + int i; + C3D_RenderTarget *a, *next; + + for (a = firstTarget; a; a = next) + { + next = a->next; + C3D_RenderTargetDelete(a); + } + + gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false); + gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false); + + for (i = 0; i < 3; i ++) + linkedTarget[i] = NULL; + + memset(queuedFrame, 0, sizeof(queuedFrame)); + queueSwap = 0; + queuedCount = 0; + queuedState = 0; +} + +static void C3Di_RenderQueueWaitDone(void) +{ + while (queuedCount || transferQueue || clearQueue) + gspWaitForAnyEvent(); +} + +bool checkRenderQueueInit(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return false; + + if (!ctx->renderQueueExit) + { + C3Di_RenderQueueInit(); + ctx->renderQueueWaitDone = C3Di_RenderQueueWaitDone; + ctx->renderQueueExit = C3Di_RenderQueueExit; + } + + return true; +} + +bool C3D_FrameBegin(u8 flags) +{ + if (inFrame) return false; + int maxCount = (flags & C3D_FRAME_SYNCDRAW) ? 1 : 2; + while (queuedCount >= maxCount) + { + if (flags & C3D_FRAME_NONBLOCK) + return false; + gspWaitForP3D(); + } + inFrame = true; + return true; +} + +bool C3D_FrameDrawOn(C3D_RenderTarget* target) +{ + if (!inFrame) return false; + + // Queue the target in the frame if it hasn't already been. + int pos = queueSwap^queuedCount; + if (!target->frame[pos]) + { + if (!queuedFrame[pos].targetList) + queuedFrame[pos].targetList = target; + else + { + C3D_RenderTarget* a; + for (a = queuedFrame[pos].targetList; a->frame[pos]; a = a->frame[pos]); + a->frame[pos] = target; + } + } + + C3D_RenderBufBind(&target->renderBuf); + return true; +} + +void C3D_FrameEnd(u8 flags) +{ + if (!inFrame) return; + inFrame = false; + + int pos = queueSwap^queuedCount; + if (!queuedFrame[pos].targetList) return; + + // Add the frame to the queue + queuedCount++; + C3Di_FinalizeFrame(&queuedFrame[pos].cmdBuf, &queuedFrame[pos].cmdBufSize); + queuedFrame[pos].flags = flags; + + // Flush the entire linear memory if the user did not explicitly mandate to flush the command list + if (!(flags & GX_CMDLIST_FLUSH)) + { + // Take advantage of GX_FlushCacheRegions to flush gsp heap + extern u32 __ctru_linear_heap; + extern u32 __ctru_linear_heap_size; + GX_FlushCacheRegions(queuedFrame[queueSwap].cmdBuf, queuedFrame[queueSwap].cmdBufSize, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0); + } + + // Update the frame queue + updateFrameQueue(); +} + +C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, int colorFmt, int depthFmt) +{ + if (!checkRenderQueueInit()) return NULL; + C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget)); + if (!target) return NULL; + memset(target, 0, sizeof(C3D_RenderTarget)); + if (!C3D_RenderBufInit(&target->renderBuf, width, height, colorFmt, depthFmt)) + { + free(target); + return NULL; + } + + target->drawOk = true; + target->prev = lastTarget; + target->next = NULL; + if (lastTarget) + lastTarget->next = target; + if (!firstTarget) + firstTarget = target; + lastTarget = target; + + return target; +} + +void C3D_RenderTargetDelete(C3D_RenderTarget* target) +{ + target->clearBits = 0; + target->linked = false; + while (!target->drawOk) + gspWaitForAnyEvent(); + C3D_RenderBufDelete(&target->renderBuf); + C3D_RenderTarget** prevNext = target->prev ? &target->prev->next : &firstTarget; + C3D_RenderTarget** nextPrev = target->next ? &target->next->prev : &lastTarget; + *prevNext = target->next; + *nextPrev = target->prev; + free(target); +} + +void C3D_RenderTargetSetClear(C3D_RenderTarget* target, u32 clearBits, u32 clearColor, u32 clearDepth) +{ + if (target->renderBuf.colorBuf.data==NULL) clearBits &= ~C3D_CLEAR_COLOR; + if (target->renderBuf.depthBuf.data==NULL) clearBits &= ~C3D_CLEAR_DEPTH; + + u32 oldClearBits = target->clearBits; + target->clearBits = clearBits & 0xFF; + target->renderBuf.clearColor = clearColor; + target->renderBuf.clearDepth = clearDepth; + + if (clearBits &~ oldClearBits && target->drawOk) + { + target->drawOk = false; + clearTarget(target); + } +} + +void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags) +{ + int id = 0; + if (screen==GFX_BOTTOM) id = 2; + else if (side==GFX_RIGHT) id = 1; + if (linkedTarget[id]) + linkedTarget[id]->linked = false; + linkedTarget[id] = target; + target->linked = true; + target->transferFlags = transferFlags; + target->screen = screen; + target->side = side; +} + +void C3D_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags) +{ + while (transferQueue || inSafeTransfer) + gspWaitForPPF(); + inSafeTransfer = true; + gspSetEventCallback(GSPGPU_EVENT_PPF, onTransferFinish, NULL, true); + GX_DisplayTransfer(inadr, indim, outadr, outdim, flags); +} + +void C3D_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags) +{ + while (transferQueue || inSafeTransfer) + gspWaitForPPF(); + inSafeTransfer = true; + gspSetEventCallback(GSPGPU_EVENT_PPF, onTransferFinish, NULL, true); + GX_TextureCopy(inadr, indim, outadr, outdim, size, flags); +} + +void C3D_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1) +{ + while (clearQueue || inSafeClear) + gspWaitForAnyEvent(); + inSafeClear = true; + gspSetEventCallback(buf0a ? GSPGPU_EVENT_PSC0 : GSPGPU_EVENT_PSC1, onClearDone, NULL, true); + GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1); +} diff --git a/libs/citro3d/source/texenv.c b/libs/citro3d/source/texenv.c new file mode 100644 index 0000000..d897d7a --- /dev/null +++ b/libs/citro3d/source/texenv.c @@ -0,0 +1,81 @@ +#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->scaleRgb = GPU_TEVSCALE_1; + env->scaleAlpha = GPU_TEVSCALE_1; +} + +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_AddIncrementalWrites(GPUREG_TEXENV0_SOURCE + id*8, (u32*)env, sizeof(C3D_TexEnv)/sizeof(u32)); +} + +void C3D_TexEnvBufUpdate(int mode, int mask) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + u32 val = ctx->texEnvBuf; + mask &= 0xF; + + if (mode & C3D_RGB) + { + val &= ~(0xF << 8); + val |= mask << 8; + } + + if (mode & C3D_Alpha) + { + val &= ~(0xF << 12); + val |= mask << 12; + } + + ctx->texEnvBuf = val; + ctx->flags |= C3DiF_TexEnvBuf; +} + +void C3D_TexEnvBufColor(u32 color) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->texEnvBufClr = color; + ctx->flags |= C3DiF_TexEnvBuf; +} diff --git a/libs/citro3d/source/texture.c b/libs/citro3d/source/texture.c new file mode 100644 index 0000000..c03374a --- /dev/null +++ b/libs/citro3d/source/texture.c @@ -0,0 +1,115 @@ +#include "context.h" +#include + +// Return bits per pixel +static inline size_t fmtSize(GPU_TEXCOLOR fmt) +{ + switch (fmt) + { + case GPU_RGBA8: + return 32; + case GPU_RGB8: + return 24; + case GPU_RGBA5551: + case GPU_RGB565: + case GPU_RGBA4: + case GPU_LA8: + case GPU_HILO8: + return 16; + case GPU_L8: + case GPU_A8: + case GPU_LA4: + case GPU_ETC1A4: + return 8; + case GPU_L4: + case GPU_A4: + case GPU_ETC1: + return 4; + default: + return 0; + } +} + +static inline bool addrIsVRAM(const void* addr) +{ + u32 vaddr = (u32)addr; + return vaddr >= 0x1F000000 && vaddr < 0x1F600000; +} + +static bool C3Di_TexInitCommon(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format, void* (*texAlloc)(size_t)) +{ + if (tex->data) return false; + + u32 size = fmtSize(format); + if (!size) return false; + size *= width * height / 8; + + tex->data = texAlloc(size); + 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); + if (format == GPU_ETC1) + tex->param |= GPU_TEXTURE_ETC1_PARAM; + tex->fmt = format; + tex->size = size; + return true; +} + +bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format) +{ + return C3Di_TexInitCommon(tex, width, height, format, linearAlloc); +} + +bool C3D_TexInitVRAM(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format) +{ + return C3Di_TexInitCommon(tex, width, height, format, vramAlloc); +} + +void C3D_TexUpload(C3D_Tex* tex, const void* data) +{ + if (tex->data && !addrIsVRAM(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 && !addrIsVRAM(tex->data)) + GSPGPU_FlushDataCache(tex->data, tex->size); +} + +void C3D_TexDelete(C3D_Tex* tex) +{ + if (!tex->data) return; + + if (addrIsVRAM(tex->data)) + vramFree(tex->data); + else + linearFree(tex->data); + + tex->data = NULL; +} diff --git a/libs/citro3d/source/uniforms.c b/libs/citro3d/source/uniforms.c new file mode 100644 index 0000000..10876cc --- /dev/null +++ b/libs/citro3d/source/uniforms.c @@ -0,0 +1,134 @@ +#include +//#include + +C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; +C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; +u16 C3D_BoolUnifs[2]; + +bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; +bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; +bool C3D_BoolUnifsDirty[2]; + +static struct +{ + bool dirty; + int count; + float24Uniform_s* data; +} C3Di_ShaderFVecData[2]; + +static bool C3Di_FVUnifEverDirty[2][C3D_FVUNIF_COUNT]; +static bool C3Di_IVUnifEverDirty[2][C3D_IVUNIF_COUNT]; + +void C3D_UpdateUniforms(GPU_SHADER_TYPE type) +{ + int offset = type == GPU_GEOMETRY_SHADER ? (GPUREG_GSH_BOOLUNIFORM-GPUREG_VSH_BOOLUNIFORM) : 0; + int i = 0; + + // Update FVec uniforms that come from shader constants + if (C3Di_ShaderFVecData[type].dirty) + { + while (i < C3Di_ShaderFVecData[type].count) + { + float24Uniform_s* u = &C3Di_ShaderFVecData[type].data[i++]; + GPUCMD_AddIncrementalWrites(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, (u32*)u, 4); + C3D_FVUnifDirty[type][u->id] = false; + } + C3Di_ShaderFVecData[type].dirty = false; + i = 0; + } + + // Update FVec uniforms + while (i < C3D_FVUNIF_COUNT) + { + if (!C3D_FVUnifDirty[type][i]) + { + i ++; + continue; + } + + // Find the number of consecutive dirty uniforms + int j; + for (j = i; j < C3D_FVUNIF_COUNT && C3D_FVUnifDirty[type][j]; j ++); + + // Upload the uniforms + GPUCMD_AddWrite(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, 0x80000000|i); + GPUCMD_AddWrites(GPUREG_VSH_FLOATUNIFORM_DATA+offset, (u32*)&C3D_FVUnif[type][i], (j-i)*4); + + // Clear the dirty flag + int k; + for (k = i; k < j; k ++) + { + C3D_FVUnifDirty[type][k] = false; + C3Di_FVUnifEverDirty[type][k] = true; + } + + // Advance + i += j; + } + + // Update IVec uniforms + for (i = 0; i < C3D_IVUNIF_COUNT; i ++) + { + if (!C3D_IVUnifDirty[type][i]) continue; + + GPUCMD_AddWrite(GPUREG_VSH_INTUNIFORM_I0+offset+i, C3D_IVUnif[type][i]); + C3D_IVUnifDirty[type][i] = false; + C3Di_IVUnifEverDirty[type][i] = false; + } + + // Update bool uniforms + if (C3D_BoolUnifsDirty[type]) + { + GPUCMD_AddWrite(GPUREG_VSH_BOOLUNIFORM+offset, 0x7FFF0000 | C3D_BoolUnifs[type]); + C3D_BoolUnifsDirty[type] = false; + } +} + +void C3Di_DirtyUniforms(GPU_SHADER_TYPE type) +{ + int i; + C3D_BoolUnifsDirty[type] = true; + if (C3Di_ShaderFVecData[type].count) + C3Di_ShaderFVecData[type].dirty = true; + for (i = 0; i < C3D_FVUNIF_COUNT; i ++) + C3D_FVUnifDirty[type][i] = C3D_FVUnifDirty[type][i] || C3Di_FVUnifEverDirty[type][i]; + for (i = 0; i < C3D_IVUNIF_COUNT; i ++) + C3D_IVUnifDirty[type][i] = C3D_IVUnifDirty[type][i] || C3Di_IVUnifEverDirty[type][i]; +} + +void C3Di_LoadShaderUniforms(shaderInstance_s* si) +{ + GPU_SHADER_TYPE type = si->dvle->type; + if (si->boolUniformMask) + { + C3D_BoolUnifs[type] &= ~si->boolUniformMask; + C3D_BoolUnifs[type] |= si->boolUniforms; + } + + if (type == GPU_GEOMETRY_SHADER) + C3D_BoolUnifs[type] &= ~BIT(15); + C3D_BoolUnifsDirty[type] = true; + + if (si->intUniformMask) + { + int i; + for (i = 0; i < 4; i ++) + { + if (si->intUniformMask & BIT(i)) + { + C3D_IVUnif[type][i] = si->intUniforms[i]; + C3D_IVUnifDirty[type][i] = true; + } + } + } + C3Di_ShaderFVecData[type].dirty = true; + C3Di_ShaderFVecData[type].count = si->numFloat24Uniforms; + C3Di_ShaderFVecData[type].data = si->float24Uniforms; +} + +void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type) +{ + C3Di_ShaderFVecData[type].dirty = false; + C3Di_ShaderFVecData[type].count = 0; + C3Di_ShaderFVecData[type].data = NULL; +} diff --git a/libs/citro3d/test/3ds/Makefile b/libs/citro3d/test/3ds/Makefile new file mode 100644 index 0000000..dc61171 --- /dev/null +++ b/libs/citro3d/test/3ds/Makefile @@ -0,0 +1,249 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +3DSTEX := 3dstex +TOPDIR ?= $(CURDIR) +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 +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := citro3d_test +BUILD := build +SOURCES := source +GRAPHICS := gfx +DATA := data +INCLUDES := include +ROMFS := romfs + +APP_TITLE := citro3d test +APP_DESCRIPTION := v1.0 +APP_AUTHOR := mtheall +ICON := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft + +CFLAGS := -g -Wall -O3 -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(TARGET).map + +LIBS := -lcitro3d -lctru + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) #$(CURDIR)/../.. + + +#--------------------------------------------------------------------------------- +# 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)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CXXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CXXFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) \ + $(CXXFILES:.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) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +PNGROMFS := $(addprefix romfs/,$(PNGFILES:.png=.bin)) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): $(PNGROMFS) + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(PNGROMFS) + +$(ROMFS)/%.rgba.bin: %.rgba.png + @$(3DSTEX) -o $@ --rgba $< + +$(ROMFS)/%.rgb.bin: %.rgb.png + @$(3DSTEX) -o $@ --rgb $< + +$(ROMFS)/%.rgba5551.bin: %.rgba5551.png + @$(3DSTEX) -o $@ --rgba5551 $< + +$(ROMFS)/%.rgb565.bin: %.rgb565.png + @$(3DSTEX) -o $@ --rgb565 $< + +$(ROMFS)/%.rgba4.bin: %.rgba4.png + @$(3DSTEX) -o $@ --rgba4 $< + +$(ROMFS)/%.la.bin: %.la.png + @$(3DSTEX) -o $@ --la $< + +$(ROMFS)/%.hilo.bin: %.hilo.png + @$(3DSTEX) -o $@ --hilo $< + +$(ROMFS)/%.l.bin: %.l.png + @$(3DSTEX) -o $@ --l $< + +$(ROMFS)/%.a.bin: %.a.png + @$(3DSTEX) -o $@ --a $< + +$(ROMFS)/%.la4.bin: %.la4.png + @$(3DSTEX) -o $@ --la4 $< + +$(ROMFS)/%.l4.bin: %.l4.png + @$(3DSTEX) -o $@ --l4 $< + +$(ROMFS)/%.a4.bin: %.a4.png + @$(3DSTEX) -o $@ --a4 $< + +$(ROMFS)/%.etc1.bin: %.etc1.png + @$(3DSTEX) -o $@ --etc1 $< + +$(ROMFS)/%.etc1a4.bin: %.etc1a4.png + @$(3DSTEX) -o $@ --etc1a4 $< + +$(ROMFS)/%.bin: %.png + @$(3DSTEX) -o $@ $< + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(NO_SMDH)),) +.PHONY: all +all : $(OUTPUT).3dsx $(OUTPUT).smdh +$(OUTPUT).smdh : $(TOPDIR)/Makefile +$(OUTPUT).3dsx: $(OUTPUT).smdh +endif +$(OUTPUT).3dsx: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as + $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) + picasso -o $(CURBIN) $1 + 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 +endef + +%.shbin.o : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/libs/citro3d/test/3ds/logo48.png b/libs/citro3d/test/3ds/logo48.png new file mode 100644 index 0000000000000000000000000000000000000000..e754bc3aaa975b75b53f2dbe7700c95953e8116d GIT binary patch literal 613 zcmV-r0-F7aP)WFU8GbZ8()Nlj2>E@cM*00GHKL_t(&-ql#!u7n^A z+%bGOf8Kr3b1DniE2@c!x=Wd9r-kzQy!?XVzvLME5CV07sxCVZAi%GBFNw`kWt}+7H|s)xR#OHs|rH60#3`<)+wPMyyeP!+#4&&wDXQ4T_lxcWHcMJ z+tv$V5i6M1)>KCQE)LTE{DMCT}y>fX=L5nqgeglvEq`R!!jmJl9s9_JvS-A-Gap|QC|RtqheV{AK4Be~Ej zWYxMSM@$P@b{?cihk&!7 z(F!~b4VY2#CUOL5Pe&F`#(Tu*X(`QQqRvGld*Vwnk)Xp35!-kJC#ck$i#i*10GQax zU&xO6XulX}^c)4?@9x=2jQ~wr9RR9MsQv%(E{#C(di-eJI1mQn^n2Ut@oz9L>6=sg zU2pSmqi4x`dKKax)<&)8aZ41!b0DxNh_HPDZsgYQ@X7t300000NkvXXu0mjfEZqPC literal 0 HcmV?d00001 diff --git a/libs/citro3d/test/3ds/logo64.png b/libs/citro3d/test/3ds/logo64.png new file mode 100644 index 0000000000000000000000000000000000000000..c16191ceb296458f47b0578d76188954fe821f02 GIT binary patch literal 725 zcmV;`0xJE9P)mC3A03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00KElL_t(|+U1+;a)clZ zg$iPvjL`}&&7eE8V`vC0Z5fMUyX#U0!L;%EzEg*!12yOE^5dmnYwA5OoA-Ae45NF!n$!3!zqnpMkDpQt0XZ7gUZ3;E>o> z9Do)K1Q13Vy^V$FxHv+Uy}jBMs3-zzM)!K`${?LH#-pPfz=+XN$~NRP6S>#$yBWQW z`6>drS9izoifgn<5+1`$&6lowlTwKqCu zOqg&^Xo(3g}F$4fqM4*!`Y%MT58M;eg z?}^!KgYXiFI<1BSfc%#CxYgjU?( z+#l#U_Z>JI%S9)#PKq@*NLc2T`7lt|X4K~bo-i!#3PBG>EMQn(kb(n1!LT;k1;8i_ zPosZe&wOhv!aGL1Oe_)JCraL)?*0GTHCc;mv&qxby$0l*-J*j%v`s`m5VmLK-_KxK zUYJgnUaxtveAfEC=X3ZZIQrt`mB+23fZqP{bXs0sUJu|8EDAQ2tG)7)00000NkvXX Hu0mjfhM_Q` literal 0 HcmV?d00001 diff --git a/libs/citro3d/test/3ds/romfs/logo.bin b/libs/citro3d/test/3ds/romfs/logo.bin new file mode 100644 index 0000000000000000000000000000000000000000..621baff4d7b33a518d8006b9c5ccf49116f01649 GIT binary patch literal 16384 zcmeH}iE^Yc3`FPC^XG}$;kksy2Vk>IYV9i0I$CX6Fp%%>wWdwr^?GG3+4vq`Ulw;) zE*LO+`o+BZvp!hjz*7v{_nPdDN1XZ^qDw8jUEKOWZwji%gPQd76z=9=eX|bD-|?xd zA$qm^imwT*39JdM35-o3z0dXSv3GOrol6`X+i|S==yx)mN9US-xjF6jP~7&4D{(BR zwc{JN*_-rq^>}CVUf1ln(thHgyKy*2j*}ln(^nTweQ~sp;n9WOQT|b0>ea{@)tfFK zYuSO%zVzGEf&Vnt?nUa~&!ckQUjNJkUi67CToYImc%J~jmyAZyZ__X3SefU4yOw-= zACdQnEj~6FH54!9YVlruNh{Vw-`5=7x24g_jp RP1Y;A-4BpFVPp*^S%ADT;wl* z&E(26pl6Ba$=fw8IAD)Q-uPPSxm7K81;y3kefR7A`YEoQ%fH#Z9Q8M$$u^hHr3E(0 z7n10LQGz%a^UEKbfaaQ?Ns~&p$6nrZuh>2PUOw&#KgO5;es47s&CAcMX7}dT(uuWx zkC;2^&(LV?#d$o|_;8f>c8%}j;#242*u6Zwh2`h_FVBU0f9Dx&4=p{nr9~VpaYkb3 zf>Db&81u^?n}Fsz^!}mV+DJNQk$N!J69;4dE;=X>?G?W0xuz7hu$dEy^*!6;1}jQQn{ zO+a%U`u^MYyYD`FF<$fGgOR7WU97y<7SDdHi+)Z2DlgWJ`bYa~?K4B8rTd1ppk2o0fPx}9&rz?A0sbP*IwP<`I>Rg;{v#rhiZuRe2#aYBb;Z1#yC*NHbF0OR`jVA&%MRFmS=cn4%MK9v` z{c1Jv9oMKIw?^mpaR*urtqH6NeAEQ=zSQEqi9f4Jug4x|w0*s=Bi=(_6K!gV%jfbj zY+ncV(VyyRdEgPMy^-&wW5-JwsvHq-&-mL!5?!Wl| H?F4=RWKCkn literal 0 HcmV?d00001 diff --git a/libs/citro3d/test/3ds/source/main.cpp b/libs/citro3d/test/3ds/source/main.cpp new file mode 100644 index 0000000..decdebd --- /dev/null +++ b/libs/citro3d/test/3ds/source/main.cpp @@ -0,0 +1,927 @@ +#include +#include +#include +#include +#include +#include +#include +#include <3ds.h> +#include + +#include "vshader_shbin.h" + +#define CLEAR_COLOR 0x777777FF + +#define DISPLAY_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \ + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +namespace +{ + +template +inline T clamp(T val, T min, T max) +{ + return std::max(min, std::min(max, val)); +} + +typedef struct +{ + float position[3]; + float texcoord[2]; + float normal[3]; +} attribute_t; + +const attribute_t attribute_list[] = +{ + { { -0.5f, -0.5f, 0.5f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, + { { 0.5f, -0.5f, 0.5f }, { 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, 1.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, 1.0f } }, + { { -0.5f, 0.5f, 0.5f }, { 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f } }, + { { -0.5f, -0.5f, 0.5f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, + + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f } }, + { { -0.5f, 0.5f, -0.5f }, { 1.0f, 0.0f }, { 0.0f, 0.0f, -1.0f } }, + { { 0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, -1.0f } }, + { { 0.5f, 0.5f, -0.5f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, -1.0f } }, + { { 0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f }, { 0.0f, 0.0f, -1.0f } }, + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f } }, + + { { 0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, + { { 0.5f, 0.5f, -0.5f }, { 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } }, + { { 0.5f, -0.5f, 0.5f }, { 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } }, + { { 0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, + + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } }, + { { -0.5f, -0.5f, 0.5f }, { 1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } }, + { { -0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { -1.0f, 0.0f, 0.0f } }, + { { -0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { -1.0f, 0.0f, 0.0f } }, + { { -0.5f, 0.5f, -0.5f }, { 0.0f, 1.0f }, { -1.0f, 0.0f, 0.0f } }, + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } }, + + { { -0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } }, + { { -0.5f, 0.5f, 0.5f }, { 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, 1.0f, 0.0f } }, + { { 0.5f, 0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, 1.0f, 0.0f } }, + { { 0.5f, 0.5f, -0.5f }, { 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f } }, + { { -0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } }, + + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } }, + { { 0.5f, -0.5f, -0.5f }, { 1.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } }, + { { 0.5f, -0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, -1.0f, 0.0f } }, + { { 0.5f, -0.5f, 0.5f }, { 1.0f, 1.0f }, { 0.0f, -1.0f, 0.0f } }, + { { -0.5f, -0.5f, 0.5f }, { 0.0f, 1.0f }, { 0.0f, -1.0f, 0.0f } }, + { { -0.5f, -0.5f, -0.5f }, { 0.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } }, +}; + +#define attribute_list_count (sizeof(attribute_list)/sizeof(attribute_list[0])) + +int uLoc_projection, uLoc_modelView, uLoc_texView; +int uLoc_lightVec, uLoc_lightHalfVec, uLoc_lightClr, uLoc_material; +C3D_Mtx material = +{ + { + { { 0.0f, 0.2f, 0.2f, 0.2f } }, // Ambient + { { 0.0f, 0.4f, 0.4f, 0.4f } }, // Diffuse + { { 0.0f, 0.8f, 0.8f, 0.8f } }, // Specular + { { 1.0f, 0.0f, 0.0f, 0.0f } }, // Emission + } +}; + +struct +{ + C3D_Tex tex; + const char *path; + size_t width, height; + GPU_TEXCOLOR format; +} texture[] = +{ + { {}, "romfs:/logo.bin", 64, 64, GPU_RGBA8, }, +}; + +#define num_textures (sizeof(texture)/sizeof(texture[0])) + +void *vbo_data; + +void sceneInit(shaderProgram_s *program) +{ + uLoc_projection = shaderInstanceGetUniformLocation(program->vertexShader, "projection"); + uLoc_modelView = shaderInstanceGetUniformLocation(program->vertexShader, "modelView"); + uLoc_texView = shaderInstanceGetUniformLocation(program->vertexShader, "texView"); + uLoc_lightVec = shaderInstanceGetUniformLocation(program->vertexShader, "lightVec"); + uLoc_lightHalfVec = shaderInstanceGetUniformLocation(program->vertexShader, "lightHalfVec"); + uLoc_lightClr = shaderInstanceGetUniformLocation(program->vertexShader, "lightClr"); + uLoc_material = shaderInstanceGetUniformLocation(program->vertexShader, "material"); + + // Configure attributes for use with the vertex shader + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord + AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal + + // Create the VBO (vertex buffer object) + vbo_data = linearAlloc(sizeof(attribute_list)); + std::memcpy(vbo_data, attribute_list, sizeof(attribute_list)); + + // Configure buffers + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vbo_data, sizeof(attribute_t), 3, 0x210); + + // Load the texture and bind it to the first texture unit + for(size_t i = 0; i < num_textures; ++i) + { + struct stat st; + int fd = ::open(texture[i].path, O_RDONLY); + ::fstat(fd, &st); + + size_t size = st.st_size; + + void *buffer = std::malloc(size); + void *p = buffer; + + while(size > 0) + { + ssize_t rc = ::read(fd, p, size); + if(rc <= 0 || static_cast(rc) > size) + break; + + size -= rc; + p = reinterpret_cast(p) + rc; + } + + ::close(fd); + + C3D_TexInit(&texture[i].tex, texture[i].width, texture[i].height, texture[i].format); + C3D_TexUpload(&texture[i].tex, buffer); + C3D_TexSetFilter(&texture[i].tex, GPU_LINEAR, GPU_NEAREST); + + ::free(buffer); + } + + C3D_TexBind(0, &texture[0].tex); + + // Configure the first fragment shading substage to blend the texture color with + // the vertex color (calculated by the vertex shader using a lighting algorithm) + // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); +} + +void sceneExit() +{ + for(size_t i = 0; i < num_textures; ++i) + C3D_TexDelete(&texture[i].tex); + + linearFree(vbo_data); +} + +void persp_tilt_test() +{ + C3D_RenderTarget *top; + C3D_Mtx projection; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = -2.0f, + old_x = x, old_y = y, old_z = z; + float angle = 0.0f; + + top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(top, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(top, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + Mtx_PerspTilt(&projection, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, false); + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + C3D_TexBind(0, &texture[0].tex); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + + if((down | held) & KEY_LEFT) + x = clamp(x - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_UP) + y = clamp(y + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_L) + z = clamp(z + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_R) + z = clamp(z - 0.1f, -10.0f, 10.0f); + + if((x != old_x) || (y != old_y) || (z != old_z)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_FrameDrawOn(top); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(top); +} + +void ortho_tilt_test() +{ + C3D_RenderTarget *top; + C3D_Mtx projection; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = 0.0f, + old_x = x, old_y = y, old_z = z; + float angle = 0.0f; + + top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(top, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(top, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + Mtx_OrthoTilt(&projection, 0.0f, 400.0f, 0.0f, 240.0f, 100.0f, -100.0f, false); + + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + C3D_TexBind(0, &texture[0].tex); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + + if((down | held) & KEY_LEFT) + x = clamp(x - 1.0f, 0.0f, 400.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 1.0f, 0.0f, 400.0f); + if((down | held) & KEY_UP) + y = clamp(y + 1.0f, 0.0f, 240.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 1.0f, 0.0f, 240.0f); + if((down | held) & KEY_L) + z = clamp(z + 1.0f, -100.0f, 100.0f); + if((down | held) & KEY_R) + z = clamp(z - 1.0f, -100.0f, 100.0f); + + if((x != old_x) || (y != old_y) || (z != old_z)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_Scale(&modelView, 64.0f, 64.0f, 64.0f); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_FrameDrawOn(top); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(top); +} + +void stereo_tilt_test() +{ + C3D_RenderTarget *topLeft, *topRight; + C3D_Mtx projLeft, projRight; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = -2.0f, + old_x = x, old_y = y, old_z = z; + float iod = osGet3DSliderState(), old_iod = iod; + float focLen = 2.0f, old_focLen = focLen; + float angle = 0.0f; + + gfxSet3D(true); + + topLeft = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + topRight = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetSetOutput(topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); + + Mtx_PerspStereoTilt(&projLeft, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, -iod, focLen, false); + Mtx_PerspStereoTilt(&projRight, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, iod, focLen, false); + + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + C3D_TexBind(0, &texture[0].tex); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + std::printf("(Y/A) focLen %.1f\n", focLen); + std::printf("(3D Slider) iod %.1f\n", iod); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + old_focLen = focLen; + old_iod = iod; + + if((down | held) & KEY_LEFT) + x = clamp(x - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_UP) + y = clamp(y + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_L) + z = clamp(z + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_R) + z = clamp(z - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_Y) + focLen = clamp(focLen - 0.1f, 0.1f, 10.0f); + if((down | held) & KEY_A) + focLen = clamp(focLen + 0.1f, 0.1f, 10.0f); + + iod = osGet3DSliderState(); + + if((x != old_x) || (y != old_y) || (z != old_z) + || (focLen != old_focLen) || (iod != old_iod)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + std::printf("(Y/A) focLen %.1f\n", focLen); + std::printf("(3D Slider) iod %.1f\n", iod); + + Mtx_PerspStereoTilt(&projLeft, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, -iod, focLen, false); + Mtx_PerspStereoTilt(&projRight, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, iod, focLen, false); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + + C3D_FrameDrawOn(topLeft); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projLeft); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + + if(iod > 0.0f) + { + C3D_FrameDrawOn(topRight); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projRight); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + } + + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(topLeft); + C3D_RenderTargetDelete(topRight); + + gfxSet3D(false); +} + +void persp_test() +{ + C3D_RenderTarget *top, *tex; + C3D_Mtx projTop, projTex; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = -2.0f, + old_x = x, old_y = y, old_z = z; + float angle = 0.0f; + + top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(top, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(top, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + tex = C3D_RenderTargetCreate(512, 256, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(tex, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_TexSetFilter(&tex->renderBuf.colorBuf, GPU_LINEAR, GPU_NEAREST); + + Mtx_Persp(&projTex, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, false); + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + + Mtx_OrthoTilt(&projTop, -0.5f, 0.5f, -0.5f, 0.5f, 100.0f, -100.0f, false); + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + + if((down | held) & KEY_LEFT) + x = clamp(x - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_UP) + y = clamp(y + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_L) + z = clamp(z + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_R) + z = clamp(z - 0.1f, -10.0f, 10.0f); + + if((x != old_x) || (y != old_y) || (z != old_z)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + + Mtx_Persp(&projTex, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, false); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + + C3D_TexBind(0, &texture[0].tex); + + C3D_FrameDrawOn(tex); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projTex); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projTop); + Mtx_Identity(&modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + C3D_FrameDrawOn(top); + C3D_TexBind(0, &tex->renderBuf.colorBuf); + C3D_DrawArrays(GPU_TRIANGLES, 0, 6); + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(top); + C3D_RenderTargetDelete(tex); +} + +void stereo_test() +{ + C3D_RenderTarget *topLeft, *topRight, *texLeft, *texRight; + C3D_Mtx projLeft, projRight, proj; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = -2.0f, + old_x = x, old_y = y, old_z = z; + float iod = osGet3DSliderState(), old_iod = iod; + float focLen = 2.0f, old_focLen = focLen; + float angle = 0.0f; + + gfxSet3D(true); + + topLeft = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + topRight = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetSetOutput(topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); + + texLeft = C3D_RenderTargetCreate(512, 256, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + texRight = C3D_RenderTargetCreate(512, 256, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(texLeft, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetClear(texRight, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_TexSetFilter(&texLeft->renderBuf.colorBuf, GPU_LINEAR, GPU_NEAREST); + C3D_TexSetFilter(&texRight->renderBuf.colorBuf, GPU_LINEAR, GPU_NEAREST); + + Mtx_PerspStereo(&projLeft, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, -iod, focLen, false); + Mtx_PerspStereo(&projRight, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, iod, focLen, false); + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + + Mtx_OrthoTilt(&proj, -0.5f, 0.5f, -0.5f, 0.5f, 100.0f, -100.0f, false); + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + std::printf("(Y/A) focLen %.1f\n", focLen); + std::printf("(3D Slider) iod %.1f\n", iod); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + old_focLen = focLen; + old_iod = iod; + + if((down | held) & KEY_LEFT) + x = clamp(x - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_UP) + y = clamp(y + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_L) + z = clamp(z + 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_R) + z = clamp(z - 0.1f, -10.0f, 10.0f); + if((down | held) & KEY_Y) + focLen = clamp(focLen - 0.1f, 0.1f, 10.0f); + if((down | held) & KEY_A) + focLen = clamp(focLen + 0.1f, 0.1f, 10.0f); + + iod = osGet3DSliderState(); + + if((x != old_x) || (y != old_y) || (z != old_z) + || (focLen != old_focLen) || (iod != old_iod)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + std::printf("(Y/A) focLen %.1f\n", focLen); + std::printf("(3D Slider) iod %.1f\n", iod); + + Mtx_PerspStereo(&projLeft, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, -iod, focLen, false); + Mtx_PerspStereo(&projRight, 60.0f*M_TAU/360.0f, 400.0f/240.0f, 1.0f, 10.0f, iod, focLen, false); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + + C3D_TexBind(0, &texture[0].tex); + + C3D_FrameDrawOn(texLeft); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projLeft); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + + if(iod > 0.0f) + { + C3D_FrameDrawOn(texRight); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projRight); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + } + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &proj); + Mtx_Identity(&modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + C3D_FrameDrawOn(topLeft); + C3D_TexBind(0, &texLeft->renderBuf.colorBuf); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + if(iod > 0.0f) + { + C3D_FrameDrawOn(topRight); + C3D_TexBind(0, &texRight->renderBuf.colorBuf); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + } + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(topLeft); + C3D_RenderTargetDelete(topRight); + C3D_RenderTargetDelete(texLeft); + C3D_RenderTargetDelete(texRight); + + gfxSet3D(false); +} + +void ortho_test() +{ + C3D_RenderTarget *top, *tex; + C3D_Mtx projTop, projTex; + C3D_Mtx modelView; + C3D_Mtx texView; + float x = 0.0f, y = 0.0f, z = -2.0f, + old_x = x, old_y = y, old_z = z; + float angle = 0.0f; + + top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(top, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(top, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + tex = C3D_RenderTargetCreate(512, 256, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetClear(tex, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_TexSetFilter(&tex->renderBuf.colorBuf, GPU_LINEAR, GPU_NEAREST); + + Mtx_Ortho(&projTex, 0.0f, 400.0f, 0.0f, 240.0f, 100.0f, -100.0f, false); + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + + Mtx_OrthoTilt(&projTop, -0.5f, 0.5f, -0.5f, 0.5f, 100.0f, -100.0f, false); + Mtx_Identity(&texView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + C3D_FVUnifMtx2x4(GPU_VERTEX_SHADER, uLoc_texView, &texView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f); + + std::printf("\x1b[2J"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + + while(aptMainLoop()) + { + gspWaitForVBlank(); + + hidScanInput(); + u32 down = hidKeysDown(); + u32 held = hidKeysHeld(); + if(down & (KEY_START|KEY_SELECT)) + break; + + old_x = x; + old_y = y; + old_z = z; + + if((down | held) & KEY_LEFT) + x = clamp(x - 1.0f, 0.0f, 400.0f); + if((down | held) & KEY_RIGHT) + x = clamp(x + 1.0f, 0.0f, 400.0f); + if((down | held) & KEY_UP) + y = clamp(y + 1.0f, 0.0f, 240.0f); + if((down | held) & KEY_DOWN) + y = clamp(y - 1.0f, 0.0f, 240.0f); + if((down | held) & KEY_L) + z = clamp(z + 1.0f, -100.0f, 100.0f); + if((down | held) & KEY_R) + z = clamp(z - 1.0f, -100.0f, 100.0f); + + if((x != old_x) || (y != old_y) || (z != old_z)) + { + std::printf("\x1b[0;0H"); + std::printf("(LEFT/RIGHT) x %.1f\n", x); + std::printf("(UP/DOWN) y %.1f\n", y); + std::printf("(L/R) z %.1f\n", z); + } + + Mtx_Identity(&modelView); + Mtx_Translate(&modelView, x, y, z, true); + Mtx_Scale(&modelView, 64.0f, 64.0f, 64.0f); + Mtx_RotateY(&modelView, angle*M_TAU/360.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + angle += 1.0f; + if(angle >= 360.0f) + angle = 0.0f; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + + C3D_TexBind(0, &texture[0].tex); + + C3D_FrameDrawOn(tex); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projTex); + C3D_DrawArrays(GPU_TRIANGLES, 0, attribute_list_count); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projTop); + Mtx_Identity(&modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + C3D_FrameDrawOn(top); + C3D_TexBind(0, &tex->renderBuf.colorBuf); + C3D_DrawArrays(GPU_TRIANGLES, 0, 6); + C3D_FrameEnd(0); + } + + C3D_RenderTargetDelete(top); + C3D_RenderTargetDelete(tex); +} + +typedef struct +{ + const char *name; + void (*test)(); +} test_t; + +test_t tests[] = +{ + { "Mtx_PerspTilt", persp_tilt_test, }, + { "Mtx_OrthoTilt", ortho_tilt_test, }, + { "Mtx_PerspStereoTilt", stereo_tilt_test, }, + { "Mtx_Persp", persp_test, }, + { "Mtx_PerspStereo", stereo_test, }, + { "Mtx_Ortho", ortho_test, }, +}; + +const size_t num_tests = sizeof(tests)/sizeof(tests[0]); + +void print_choices(size_t choice) +{ + std::printf("\x1b[2J"); + for(size_t i = 0; i < num_tests; ++i) + std::printf("\x1b[%zu;0H%c%s", i, i == choice ? '*' : ' ', tests[i].name); +} + +} + +int main(int argc, char *argv[]) +{ + size_t choice = 0; + shaderProgram_s program; + DVLB_s *vsh_dvlb; + + romfsInit(); + gfxInitDefault(); + gfxSet3D(false); + consoleInit(GFX_BOTTOM, nullptr); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + shaderProgramInit(&program); + vsh_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size); + shaderProgramSetVsh(&program, &vsh_dvlb->DVLE[0]); + C3D_BindProgram(&program); + + sceneInit(&program); + + print_choices(choice); + while(aptMainLoop()) + { + gfxFlushBuffers(); + gspWaitForVBlank(); + gfxSwapBuffers(); + + hidScanInput(); + u32 down = hidKeysDown(); + + if(down & KEY_UP) + { + choice = (choice + num_tests - 1) % num_tests; + print_choices(choice); + } + else if(down & KEY_DOWN) + { + choice = (choice + 1) % num_tests; + print_choices(choice); + } + else if(down & KEY_A) + { + tests[choice].test(); + print_choices(choice); + } + else if(down & KEY_B) + break; + } + + sceneExit(); + + shaderProgramFree(&program); + DVLB_Free(vsh_dvlb); + C3D_Fini(); + gfxExit(); + romfsExit(); + + return 0; +} diff --git a/libs/citro3d/test/3ds/source/vshader.v.pica b/libs/citro3d/test/3ds/source/vshader.v.pica new file mode 100644 index 0000000..1068e28 --- /dev/null +++ b/libs/citro3d/test/3ds/source/vshader.v.pica @@ -0,0 +1,89 @@ +; Example PICA200 vertex shader + +; Uniforms +.fvec projection[4], modelView[4], texView[2] +.fvec lightVec, lightHalfVec, lightClr, material[4] +.alias mat_amb material[0] +.alias mat_dif material[1] +.alias mat_spe material[2] +.alias mat_emi material[3] + +; Constants +.constf myconst(0.0, 1.0, -1.0, -0.5) +.alias zeros myconst.xxxx ; Vector full of zeros +.alias ones myconst.yyyy ; Vector full of ones + +; Outputs +.out outpos position +.out outtc0 texcoord0 +.out outclr color + +; Inputs (defined as aliases for convenience) +.alias inpos v0 +.alias intex v1 +.alias innrm v2 + +.proc main + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, ones + + ; r1 = modelView * inpos + dp4 r1.x, modelView[0], r0 + dp4 r1.y, modelView[1], r0 + dp4 r1.z, modelView[2], r0 + dp4 r1.w, modelView[3], r0 + + ; outpos = projection * r1 + dp4 outpos.x, projection[0], r1 + dp4 outpos.y, projection[1], r1 + dp4 outpos.z, projection[2], r1 + dp4 outpos.w, projection[3], r1 + + ; outtex = intex + dp4 outtc0.x, texView[0], intex + dp4 outtc0.y, texView[1], intex + mov outtc0.zw, myconst.xy + + ; Transform the normal vector with the modelView matrix + ; r1 = normalize(modelView * innrm) + mov r0.xyz, innrm + mov r0.w, zeros + dp4 r1.x, modelView[0], r0 + dp4 r1.y, modelView[1], r0 + dp4 r1.z, modelView[2], r0 + mov r1.w, zeros + dp3 r2, r1, r1 ; r2 = x^2+y^2+z^2 for each component + rsq r2, r2 ; r2 = 1/sqrt(r2) '' + mul r1, r2, r1 ; r1 = r1*r2 + + ; Calculate the diffuse level (r0.x) and the shininess level (r0.y) + ; r0.x = max(0, -(lightVec * r1)) + ; r0.y = max(0, (-lightHalfVec[i]) * r1) ^ 2 + dp3 r0.x, lightVec, r1 + add r0.x, zeros, -r0 + dp3 r0.y, -lightHalfVec, r1 + max r0, zeros, r0 + mul r0.y, r0, r0 + + ; Accumulate the vertex color in r1, initializing it to the emission color + mov r1, mat_emi + + ; r1 += specularColor * lightClr * shininessLevel + mul r2, lightClr, r0.yyyy + mad r1, r2, mat_spe, r1 + + ; r1 += diffuseColor * lightClr * diffuseLevel + mul r2, lightClr, r0.xxxx + mad r1, r2, mat_dif, r1 + + ; r1 += ambientColor * lightClr + mov r2, lightClr + mad r1, r2, mat_amb, r1 + + ; outclr = clamp r1 to [0,1] + min outclr, ones, r1 + + ; We're finished + end +.end diff --git a/libs/citro3d/test/pc/.gitignore b/libs/citro3d/test/pc/.gitignore new file mode 100644 index 0000000..78a9397 --- /dev/null +++ b/libs/citro3d/test/pc/.gitignore @@ -0,0 +1,5 @@ +*.d +*.o +test +coverage.info +lcov/ diff --git a/libs/citro3d/test/pc/Makefile b/libs/citro3d/test/pc/Makefile new file mode 100644 index 0000000..b3a670f --- /dev/null +++ b/libs/citro3d/test/pc/Makefile @@ -0,0 +1,42 @@ +TARGET := test + +CFILES := $(wildcard *.c) $(wildcard ../../source/maths/*.c) +CXXFILES := $(wildcard *.cpp) +OFILES := $(addprefix build/,$(CXXFILES:.cpp=.o)) \ + $(patsubst ../../source/maths/%,build/%,$(CFILES:.c=.o)) +DFILES := $(wildcard build/*.d) + +CFLAGS := -Wall -g -pipe -I../../include --coverage +CXXFLAGS := $(CFLAGS) -std=gnu++11 -DGLM_FORCE_RADIANS +LDFLAGS := $(ARCH) -pipe -lm --coverage + +.PHONY: all clean lcov + +all: $(TARGET) + +$(TARGET): $(OFILES) + @echo "Linking $@" + $(CXX) -o $@ $^ $(LDFLAGS) + +lcov: all + @./$(TARGET) + @lcov --capture --no-external --directory ../../include --directory ../../source --directory ../../test/pc --output-file coverage.info + @genhtml coverage.info --output-directory lcov + +$(OFILES): | build + +build: + @[ -d build ] || mkdir build + +build/%.o : %.cpp $(wildcard *.h) + @echo "Compiling $@" + @$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF build/$*.d + +build/%.o : ../../source/maths/%.c $(wildcard *.h) + @echo "Compiling $@" + @$(CC) -o $@ -c $< $(CFLAGS) -MMD -MP -MF build/$*.d + +clean: + $(RM) -r $(TARGET) build/ coverage.info lcov/ + +-include $(DFILES) diff --git a/libs/citro3d/test/pc/main.cpp b/libs/citro3d/test/pc/main.cpp new file mode 100644 index 0000000..3a5d8a9 --- /dev/null +++ b/libs/citro3d/test/pc/main.cpp @@ -0,0 +1,939 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern "C" { +#include +} + +typedef std::default_random_engine generator_t; +typedef std::uniform_real_distribution distribution_t; + +static inline void +randomMatrix(C3D_Mtx &m, generator_t &g, distribution_t &d) +{ + for(size_t i = 0; i < 16; ++i) + m.m[i] = d(g); +} + +static inline glm::vec3 +randomVector3(generator_t &g, distribution_t &d) +{ + return glm::vec3(d(g), d(g), d(g)); +} + +static inline glm::vec4 +randomVector4(generator_t &g, distribution_t &d) +{ + return glm::vec4(d(g), d(g), d(g), d(g)); +} + +static inline float +randomAngle(generator_t &g, distribution_t &d) +{ + return d(g); +} + +static inline C3D_FQuat +randomQuat(generator_t &g, distribution_t &d) +{ + return Quat_New(d(g), d(g), d(g), d(g)); +} + +static inline glm::mat4 +loadMatrix(const C3D_Mtx &m) +{ + return glm::mat4(m.m[ 3], m.m[ 7], m.m[11], m.m[15], + m.m[ 2], m.m[ 6], m.m[10], m.m[14], + m.m[ 1], m.m[ 5], m.m[ 9], m.m[13], + m.m[ 0], m.m[ 4], m.m[ 8], m.m[12]); +} + +static inline glm::quat +loadQuat(const C3D_FQuat &q) +{ + return glm::quat(q.r, q.i, q.j, q.k); +} + +static inline bool +operator==(const glm::vec3 &lhs, const C3D_FVec &rhs) +{ + return std::abs(lhs.x - rhs.x) < 0.001f + && std::abs(lhs.y - rhs.y) < 0.001f + && std::abs(lhs.z - rhs.z) < 0.001f; +} + +static inline bool +operator==(const C3D_FVec &lhs, const glm::vec3 &rhs) +{ + return rhs == lhs; +} + +static inline bool +operator==(const glm::vec4 &lhs, const C3D_FVec &rhs) +{ + return std::abs(lhs.x - rhs.x) < 0.001f + && std::abs(lhs.y - rhs.y) < 0.001f + && std::abs(lhs.z - rhs.z) < 0.001f + && std::abs(lhs.w - rhs.w) < 0.001f; +} + +static inline bool +operator==(const C3D_FVec &lhs, const glm::vec4 &rhs) +{ + return rhs == lhs; +} + +static inline bool +operator==(const glm::mat4 &lhs, const C3D_Mtx &rhs) +{ + for(size_t i = 0; i < 4; ++i) + { + for(size_t j = 0; j < 4; ++j) + { + if(std::abs(lhs[i][j] - rhs.m[j*4+3-i]) > 0.001f) + return false; // LCOV_EXCL_LINE This would cause an assertion failure + } + } + + return true; +} + +static inline bool +operator==(const C3D_Mtx &lhs, const glm::mat4 &rhs) +{ + return rhs == lhs; +} + +static inline bool +operator==(const glm::quat &lhs, const C3D_FQuat &rhs) +{ + return std::abs(lhs.w - rhs.r) < 0.01f + && std::abs(lhs.x - rhs.i) < 0.01f + && std::abs(lhs.y - rhs.j) < 0.01f + && std::abs(lhs.z - rhs.k) < 0.01f; +} + +static inline bool +operator==(const C3D_FQuat &lhs, const glm::quat &rhs) +{ + return rhs == lhs; +} + +static inline bool +operator==(const C3D_FQuat &lhs, const C3D_FQuat &rhs) +{ + return std::abs(lhs.r - rhs.r) < 0.01f + && std::abs(lhs.i - rhs.i) < 0.01f + && std::abs(lhs.j - rhs.j) < 0.01f + && std::abs(lhs.k - rhs.k) < 0.01f; +} + +static inline void +print(const C3D_FVec &v) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + std::printf("% 6.4f % 6.4f % 6.4f % 6.4f\n", v.w, v.x, v.y, v.z); +} + +static inline void +print(const glm::vec3 &v) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + std::printf("% 6.4f % 6.4f % 6.4f\n", v.x, v.y, v.z); +} + +static inline void +print(const glm::vec4 &v) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + std::printf("%6.4f % 6.4f % 6.4f % 6.4f\n", v.w, v.x, v.y, v.z); +} + +static inline void +print(const C3D_Mtx &m) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + for(size_t j = 0; j < 4; ++j) + { + std::printf("% 6.4f % 6.4f % 6.4f % 6.4f\n", + m.m[j*4+3], + m.m[j*4+2], + m.m[j*4+1], + m.m[j*4+0]); + } +} + +static inline void +print(const glm::mat4 &m) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + for(size_t j = 0; j < 4; ++j) + { + std::printf("% 6.4f % 6.4f % 6.4f % 6.4f\n", + m[0][j], + m[1][j], + m[2][j], + m[3][j]); + } +} + +static inline void +print(const glm::quat &q) +{ + std::printf("%s:\n", __PRETTY_FUNCTION__); + std::printf("% 6.4f % 6.4f % 6.4f % 6.4f\n", q.w, q.x, q.y, q.z); +} + +static const glm::vec3 x_axis(1.0f, 0.0f, 0.0f); +static const glm::vec3 y_axis(0.0f, 1.0f, 0.0f); +static const glm::vec3 z_axis(0.0f, 0.0f, 1.0f); +static const glm::vec3 z_flip(1.0f, 1.0f, -1.0f); + +static void +check_matrix(generator_t &gen, distribution_t &dist) +{ + + glm::mat4 fix_depth(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, -0.5f, 1.0f); + glm::mat4 tilt = glm::rotate(glm::mat4(), -static_cast(M_TAU)/4.0f, z_axis); + + // check identity + { + C3D_Mtx m; + Mtx_Identity(&m); + assert(m == glm::mat4()); + } + + // ortho nominal cases + { + C3D_Mtx m; + C3D_FVec v; + float l = 0.0f, + r = 400.0f, + b = 0.0f, + t = 320.0f, + n = 0.0f, + f = 100.0f; + + Mtx_Ortho(&m, l, r, b, t, n, f, false); + + // check near clip plane + v = Mtx_MultiplyFVecH(&m, FVec3_New((r-l)/2.0f, (t-b)/2.0f, -n)); + v = FVec4_PerspDivide(v); + assert(v == FVec4_New(0.0f, 0.0f, -1.0f, 1.0f)); + + // check far clip plane + v = Mtx_MultiplyFVecH(&m, FVec3_New((r-l)/2.0f, (t-b)/2.0f, -f)); + v = FVec4_PerspDivide(v); + assert(v == FVec4_New(0.0f, 0.0f, 0.0f, 1.0f)); + } + + // perspective nominal cases + { + C3D_Mtx m; + C3D_FVec v; + float fovy = C3D_Angle(60.0f/360.0f), + aspect = C3D_AspectRatioTop, + near = 0.1f, + far = 10.0f; + + Mtx_Persp(&m, fovy, aspect, near, far, false); + + // check near clip plane + v = Mtx_MultiplyFVecH(&m, FVec3_New(0.0f, 0.0f, -near)); + v = FVec4_PerspDivide(v); + assert(v == FVec4_New(0.0f, 0.0f, -1.0f, 1.0f)); + + // check far clip plane + v = Mtx_MultiplyFVecH(&m, FVec3_New(0.0f, 0.0f, -far)); + v = FVec4_PerspDivide(v); + assert(v == FVec4_New(0.0f, 0.0f, 0.0f, 1.0f)); + } + + for(size_t x = 0; x < 10000; ++x) + { + // check inverse + { + C3D_Mtx m, inv, id; + + randomMatrix(m, gen, dist); + + // cast to int to try to avoid assertion failure due to rounding error + for(size_t i = 0; i < 16; ++i) + m.m[i] = static_cast(m.m[i]); + + Mtx_Copy(&inv, &m); + if(Mtx_Inverse(&inv)) + { + Mtx_Multiply(&id, &m, &inv); + assert(id == glm::mat4()); // could still fail due to rounding errors + Mtx_Multiply(&id, &inv, &m); + assert(id == glm::mat4()); // could still fail due to rounding errors + } + } + + // check perspective + { + C3D_Mtx m; + float fovy = dist(gen), + aspect = dist(gen), + near = dist(gen), + far = dist(gen), + fovx; + + while(aspect < 0.25f || aspect > 4.0f) + aspect = dist(gen); + + while(fovy < M_TAU / 36.0f + || fovy >= M_TAU / 2.0f + || (fovx = 2.0f * atanf(tanf(fovy/2.0f) * aspect)) < M_TAU / 36.0f + || fovx >= M_TAU / 2.0f) + { + fovy = dist(gen); + } + + while(std::abs(far - near) < 0.1f) + far = dist(gen); + + // RH + Mtx_Persp(&m, fovy, aspect, near, far, false); + glm::mat4 g = glm::perspective(fovy, aspect, near, far); + assert(m == fix_depth*g); + + // LH + Mtx_Persp(&m, fovy, aspect, near, far, true); + g = glm::perspective(fovy, aspect, near, far); + assert(m == fix_depth*glm::scale(g, z_flip)); + } + + // check perspective tilt + { + C3D_Mtx m; + float fovy = dist(gen), + aspect = dist(gen), + near = dist(gen), + far = dist(gen), + fovx; + + while(aspect < 0.25f || aspect > 4.0f) + aspect = dist(gen); + + while(fovy < M_TAU / 36.0f + || fovy >= M_TAU / 2.0f + || (fovx = 2.0f * atanf(tanf(fovy/2.0f) * aspect)) < M_TAU / 36.0f + || fovx >= M_TAU / 2.0f) + { + fovy = dist(gen); + } + + while(std::abs(far - near) < 0.1f) + far = dist(gen); + + // RH + Mtx_PerspTilt(&m, fovy, aspect, near, far, false); + glm::mat4 g = glm::perspective(fovx, 1.0f / aspect, near, far); + assert(m == fix_depth*g*tilt); + + // LH + Mtx_PerspTilt(&m, fovy, aspect, near, far, true); + g = glm::perspective(fovx, 1.0f / aspect, near, far); + assert(m == fix_depth*glm::scale(g, z_flip)*tilt); + } + + // check perspective stereo + { + C3D_Mtx left, right; + float fovy = dist(gen), + aspect = dist(gen), + near = dist(gen), + far = dist(gen), + iod = dist(gen), + focLen = dist(gen), + fovy_tan, + fovx; + + while(aspect < 0.25f || aspect > 4.0f) + aspect = dist(gen); + + while(fovy < M_TAU / 36.0f + || fovy >= M_TAU / 2.0f + || (fovx = 2.0f * atanf(tanf(fovy/2.0f) * aspect)) < M_TAU / 36.0f + || fovx >= M_TAU / 2.0f) + { + fovy = dist(gen); + } + + while(std::abs(far - near) < 0.1f) + far = dist(gen); + + while(focLen < 0.25f) + focLen = dist(gen); + + glm::mat4 g = glm::perspective(fovy, aspect, near, far); + + fovy_tan = tanf(fovy/2.0f); + glm::mat4 left_eye (1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + iod/(focLen*2.0f), 0.0f, 1.0f, 0.0f, + iod*fovy_tan*aspect/2.0f, 0.0f, 0.0f, 1.0f); + glm::mat4 right_eye(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + -iod/(focLen*2.0f), 0.0f, 1.0f, 0.0f, + -iod*fovy_tan*aspect/2.0f, 0.0f, 0.0f, 1.0f); + + // RH + Mtx_PerspStereo(&left, fovy, aspect, near, far, -iod, focLen, false); + Mtx_PerspStereo(&right, fovy, aspect, near, far, iod, focLen, false); + assert(left == fix_depth*g*left_eye); + assert(right == fix_depth*g*right_eye); + + // LH + Mtx_PerspStereo(&left, fovy, aspect, near, far, -iod, focLen, true); + Mtx_PerspStereo(&right, fovy, aspect, near, far, iod, focLen, true); + assert(left == fix_depth*glm::scale(g*left_eye, z_flip)); + assert(right == fix_depth*glm::scale(g*right_eye, z_flip)); + } + + // check perspective stereo tilt + { + C3D_Mtx left, right; + float fovy = dist(gen), + aspect = dist(gen), + near = dist(gen), + far = dist(gen), + iod = dist(gen), + focLen = dist(gen), + fovx, + fovx_tan; + + while(aspect < 0.25f || aspect > 4.0f) + aspect = dist(gen); + + while(fovy < M_TAU / 36.0f + || fovy >= M_TAU / 2.0f + || (fovx = 2.0f * atanf(tanf(fovy/2.0f) * aspect)) < M_TAU / 36.0f + || fovx >= M_TAU / 2.0f) + { + fovy = dist(gen); + } + + while(std::abs(far - near) < 0.1f) + far = dist(gen); + + while(focLen < 0.25f) + focLen = dist(gen); + + glm::mat4 g = glm::perspective(fovx, 1.0f / aspect, near, far); + + fovx_tan = tanf(fovx/2.0f); + glm::mat4 left_eye (1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, -iod/(focLen*2.0f), 1.0f, 0.0f, + 0.0f, -iod*fovx_tan/2.0f, 0.0f, 1.0f); + glm::mat4 right_eye(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, iod/(focLen*2.0f), 1.0f, 0.0f, + 0.0f, iod*fovx_tan/2.0f, 0.0f, 1.0f); + + // RH + Mtx_PerspStereoTilt(&left, fovy, aspect, near, far, -iod, focLen, false); + Mtx_PerspStereoTilt(&right, fovy, aspect, near, far, iod, focLen, false); + assert(left == fix_depth*g*left_eye*tilt); + assert(right == fix_depth*g*right_eye*tilt); + + // LH + Mtx_PerspStereoTilt(&left, fovy, aspect, near, far, -iod, focLen, true); + Mtx_PerspStereoTilt(&right, fovy, aspect, near, far, iod, focLen, true); + assert(left == fix_depth*glm::scale(g*left_eye, z_flip)*tilt); + assert(right == fix_depth*glm::scale(g*right_eye, z_flip)*tilt); + } + + // check ortho + { + C3D_Mtx m; + float l = dist(gen), + r = dist(gen), + b = dist(gen), + t = dist(gen), + n = dist(gen), + f = dist(gen); + + while(std::abs(r-l) < 0.1f) + r = dist(gen); + + while(std::abs(t-b) < 0.1f) + t = dist(gen); + + while(std::abs(f-n) < 0.1f) + f = dist(gen); + + // RH + Mtx_Ortho(&m, l, r, b, t, n, f, false); + glm::mat4 g = glm::ortho(l, r, b, t, n, f); + assert(m == fix_depth*g); + + // LH + Mtx_Ortho(&m, l, r, b, t, n, f, true); + g = glm::ortho(l, r, b, t, n, f); + assert(m == fix_depth*glm::scale(g, z_flip)); + } + + // check ortho tilt + { + C3D_Mtx m; + float l = dist(gen), + r = dist(gen), + b = dist(gen), + t = dist(gen), + n = dist(gen), + f = dist(gen); + + while(std::abs(r-l) < 0.1f) + r = dist(gen); + + while(std::abs(t-b) < 0.1f) + t = dist(gen); + + while(std::abs(f-n) < 0.1f) + f = dist(gen); + + // RH + Mtx_OrthoTilt(&m, l, r, b, t, n, f, false); + glm::mat4 g = glm::ortho(l, r, b, t, n, f); + assert(m == tilt*fix_depth*g); + + // LH + Mtx_OrthoTilt(&m, l, r, b, t, n, f, true); + g = glm::ortho(l, r, b, t, n, f); + assert(m == tilt*fix_depth*glm::scale(g, z_flip)); + } + + // check lookAt + { + C3D_Mtx m; + C3D_FVec camera, target, diff, up; + + // avoid very small distances and 'up' pointing near the target + do + { + camera = FVec3_New(dist(gen), dist(gen), dist(gen)); + target = FVec3_New(dist(gen), dist(gen), dist(gen)); + up = FVec3_New(dist(gen), dist(gen), dist(gen)); + diff = FVec3_Subtract(target, camera); + } while(FVec3_Magnitude(diff) < 0.25f + || FVec3_Magnitude(up) < 0.25f + || FVec3_Dot(up, diff) / FVec3_Magnitude(up) / FVec3_Magnitude(diff) < cosf(30.0f*M_TAU/360.0f)); + + glm::mat4 g = glm::lookAt(glm::vec3(camera.x, camera.y, camera.z), + glm::vec3(target.x, target.y, target.z), + glm::vec3(up.x, up.y, up.z)); + + // RH + Mtx_LookAt(&m, camera, target, up, false); + assert(m == g); + + // LH + Mtx_LookAt(&m, camera, target, up, true); + // I can't say for certain that this is the correct test + assert(m == glm::scale(glm::mat4(), glm::vec3(-1.0f, 1.0f, -1.0f))*g); + } + + // check multiply + { + C3D_Mtx m1, m2; + randomMatrix(m1, gen, dist); + randomMatrix(m2, gen, dist); + + glm::mat4 g1 = loadMatrix(m1); + glm::mat4 g2 = loadMatrix(m2); + + C3D_Mtx result; + Mtx_Multiply(&result, &m1, &m2); + assert(result == g1*g2); + } + + // check translate + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + Mtx_Translate(&m, v.x, v.y, v.z, true); + assert(m == glm::translate(g, v)); + } + + // check translate (reversed) + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + Mtx_Translate(&m, v.x, v.y, v.z, false); + assert(m == glm::translate(glm::mat4(), v)*g); + } + + // check scale + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + Mtx_Scale(&m, v.x, v.y, v.z); + assert(m == glm::scale(g, v)); + } + + // check rotate + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + Mtx_Rotate(&m, FVec3_New(v.x, v.y, v.z), r, true); + assert(m == glm::rotate(g, r, v)); + } + + // check rotate (reversed) + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + Mtx_Rotate(&m, FVec3_New(v.x, v.y, v.z), r, false); + assert(m == glm::rotate(glm::mat4(), r, v)*g); + } + + // check rotate X + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateX(&m, r, true); + assert(m == glm::rotate(g, r, x_axis)); + } + + // check rotate X (reversed) + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateX(&m, r, false); + assert(m == glm::rotate(glm::mat4(), r, x_axis)*g); + } + + // check rotate Y + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateY(&m, r, true); + assert(m == glm::rotate(g, r, y_axis)); + } + + // check rotate Y (reversed) + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateY(&m, r, false); + assert(m == glm::rotate(glm::mat4(), r, y_axis)*g); + } + + // check rotate Z + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateZ(&m, r, true); + assert(m == glm::rotate(g, r, z_axis)); + } + + // check rotate Z (reversed) + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + float r = randomAngle(gen, dist); + + glm::mat4 g = loadMatrix(m); + + Mtx_RotateZ(&m, r, false); + assert(m == glm::rotate(glm::mat4(), r, z_axis)*g); + } + + // check vec3 multiply + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec3 v = randomVector3(gen, dist); + + assert(Mtx_MultiplyFVec3(&m, FVec3_New(v.x, v.y, v.z)) == glm::mat3x3(g)*v); + } + + // check vec4 multiply + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec4 v = randomVector4(gen, dist); + + assert(Mtx_MultiplyFVec4(&m, FVec4_New(v.x, v.y, v.z, v.w)) == g*v); + } + + // check vecH multiply + { + C3D_Mtx m; + randomMatrix(m, gen, dist); + + glm::mat4 g = loadMatrix(m); + glm::vec4 v = randomVector4(gen, dist); + v.w = 1.0f; + + assert(Mtx_MultiplyFVecH(&m, FVec3_New(v.x, v.y, v.z)) == glm::mat4x3(g)*v); + } + } +} + +static void +check_quaternion(generator_t &gen, distribution_t &dist) +{ + // check identity + { + C3D_FQuat q = Quat_Identity(); + glm::quat g; + + assert(q == g); + } + + for(size_t x = 0; x < 10000; ++x) + { + // check negation + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + assert(Quat_Negate(q) == -g); + } + + // check addition + { + C3D_FQuat q1 = randomQuat(gen, dist); + C3D_FQuat q2 = randomQuat(gen, dist); + + glm::quat g1 = loadQuat(q1); + glm::quat g2 = loadQuat(q2); + + assert(Quat_Add(q1, q2) == g1+g2); + } + + // check subtraction + { + C3D_FQuat q1 = randomQuat(gen, dist); + C3D_FQuat q2 = randomQuat(gen, dist); + + glm::quat g1 = loadQuat(q1); + glm::quat g2 = loadQuat(q2); + + assert(Quat_Subtract(q1, q2) == g1 + (-g2)); + } + + // check scale + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + float f = dist(gen); + + assert(Quat_Scale(q, f) == g*f); + } + + // check normalize + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + assert(Quat_Normalize(q) == glm::normalize(g)); + } + + // check dot + { + C3D_FQuat q1 = randomQuat(gen, dist); + C3D_FQuat q2 = randomQuat(gen, dist); + glm::quat g1 = loadQuat(q1); + glm::quat g2 = loadQuat(q2); + + assert(std::abs(Quat_Dot(q1, q2) - glm::dot(g1, g2)) < 0.0001f); + } + + // check conjugate + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + assert(Quat_Conjugate(q) == glm::conjugate(g)); + } + + // check inverse + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + assert(Quat_Inverse(q) == glm::inverse(g)); + } + + // check quaternion multiplication + { + C3D_FQuat q1 = randomQuat(gen, dist); + C3D_FQuat q2 = randomQuat(gen, dist); + glm::quat g1 = loadQuat(q1); + glm::quat g2 = loadQuat(q2); + + assert(Quat_Multiply(q1, q2) == g1*g2); + } + + // check quat pow() + // Note: older versions of glm have broken pow() for quats + { + C3D_FQuat q = randomQuat(gen, dist); + //glm::quat g = loadQuat(q); + float r = dist(gen); + + //assert(Quat_Pow(q, r) == glm::pow(g, r)); + + q = Quat_Normalize(q); + + // check trivial cases + assert(Quat_Pow(q, 1.0f) == q); + assert(Quat_Pow(q, 0.0f) == Quat_Identity()); + assert(Quat_Pow(Quat_Identity(), r) == Quat_Identity()); + + // validate semantics + assert(Quat_Pow(q, r) == Quat_Multiply(Quat_Pow(q, r/2), Quat_Pow(q, r/2))); + } + + // check vector multiplication (cross) + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + glm::vec3 v = randomVector3(gen, dist); + + assert(Quat_CrossFVec3(q, FVec3_New(v.x, v.y, v.z)) == glm::cross(g, v)); + assert(FVec3_CrossQuat(FVec3_New(v.x, v.y, v.z), q) == glm::cross(v, g)); + } + + // check rotation + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + glm::vec3 v = randomVector3(gen, dist); + float r = randomAngle(gen, dist); + + assert(Quat_Rotate(q, FVec3_New(v.x, v.y, v.z), r, false) == glm::rotate(g, r, v)); + assert(Quat_Rotate(q, FVec3_New(v.x, v.y, v.z), r, true) == glm::rotate(glm::quat(), r, v)*g); + } + + // check rotate X + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + float r = randomAngle(gen, dist); + + assert(Quat_RotateX(q, r, false) == glm::rotate(g, r, x_axis)); + assert(Quat_RotateX(q, r, true) == glm::rotate(glm::quat(), r, x_axis)*g); + } + + // check rotate Y + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + float r = randomAngle(gen, dist); + + assert(Quat_RotateY(q, r, false) == glm::rotate(g, r, y_axis)); + assert(Quat_RotateY(q, r, true) == glm::rotate(glm::quat(), r, y_axis)*g); + } + + // check rotate Z + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + float r = randomAngle(gen, dist); + + assert(Quat_RotateZ(q, r, false) == glm::rotate(g, r, z_axis)); + assert(Quat_RotateZ(q, r, true) == glm::rotate(glm::quat(), r, z_axis)*g); + } + + // check conversion to matrix + { + C3D_FQuat q = randomQuat(gen, dist); + glm::quat g = loadQuat(q); + + C3D_Mtx m; + Mtx_FromQuat(&m, q); + + assert(m == glm::mat4_cast(g)); + } + } +} + +int main(int argc, char *argv[]) +{ + std::random_device rd; + generator_t gen(rd()); + distribution_t dist(-10.0f, 10.0f); + + check_matrix(gen, dist); + check_quaternion(gen, dist); + + return EXIT_SUCCESS; +} diff --git a/libs/sf2dlib/.gitignore b/libs/sf2dlib/.gitignore index a020760..c137c57 100644 --- a/libs/sf2dlib/.gitignore +++ b/libs/sf2dlib/.gitignore @@ -2,6 +2,13 @@ *.o *.project *.cproject -libsf2d/.settings/* -libsf2d/build/* -libsf2d/lib/* +*.3dsx +*.cia +*.cxi +*.ncch +*.3ds +*.elf +*.smdh +build/ +lib/ +.settings/ diff --git a/libs/sf2dlib/libsf2d/Makefile b/libs/sf2dlib/libsf2d/Makefile index 28ccec1..febb49e 100644 --- a/libs/sf2dlib/libsf2d/Makefile +++ b/libs/sf2dlib/libsf2d/Makefile @@ -24,18 +24,16 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -CFLAGS := -g -Wall -O2\ - $(ARCH) +CFLAGS := -g -Wall -Werror -O2 -mword-relocations \ + -ffunction-sections -fno-strict-aliasing \ + -fomit-frame-pointer \ + $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS -#CFLAGS += -std=c11 -#WILL HAVE TO BE REMOVED SOON -CFLAGS += -DLIBCTRU_NO_DEPRECATION - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) @@ -43,7 +41,8 @@ ASFLAGS := -g $(ARCH) # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- -LIBDIRS := $(CTRULIB) +LIBDIRS := $(CTRULIB) \ + $(CURDIR)/../../citro3d #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -62,6 +61,8 @@ 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))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) #--------------------------------------------------------------------------------- @@ -79,6 +80,7 @@ endif #--------------------------------------------------------------------------------- export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ @@ -128,18 +130,29 @@ $(OUTPUT) : $(OFILES) @echo $(notdir $<) @$(bin2o) -# WARNING: This is not the right way to do this! TODO: Do it right! #--------------------------------------------------------------------------------- -%.vsh.o : %.vsh +# rules for assembling GPU shaders #--------------------------------------------------------------------------------- - @echo $(notdir $<) - @picasso -o $(notdir $<).shbin $< - @bin2s $(notdir $<).shbin | $(PREFIX)as -o $@ - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h +define shader-as + $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) + picasso -o $(CURBIN) $1 + 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 +endef -sf2d.c: shader.vsh +%.shbin.o : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) -include $(DEPENDS) diff --git a/libs/sf2dlib/libsf2d/data/shader.vsh b/libs/sf2dlib/libsf2d/data/shader.vsh deleted file mode 100644 index 3793136..0000000 --- a/libs/sf2dlib/libsf2d/data/shader.vsh +++ /dev/null @@ -1,30 +0,0 @@ -; Outputs -.out outpos position -.out outtc0 texcoord0 -.out outclr color - -; Inputs -.alias inpos v0 -.alias inarg v1 - -; Uniforms -.fvec projection[4] - -; Constants -.constf RGBA8_TO_FLOAT4(0.00392156862, 0, 0, 0) - -.proc main - ; outpos = projection * in.pos - dp4 outpos.x, projection[0].wzyx, inpos - dp4 outpos.y, projection[1].wzyx, inpos - dp4 outpos.z, projection[2].wzyx, inpos - dp4 outpos.w, projection[3].wzyx, inpos - - ; outtc0 = in.texcoord - mov outtc0, inarg - - ; outclr = RGBA8_TO_FLOAT4(in.color) - mul outclr, RGBA8_TO_FLOAT4.xxxx, inarg - - end -.end diff --git a/libs/sf2dlib/libsf2d/include/sf2d.h b/libs/sf2dlib/libsf2d/include/sf2d.h index b6f3277..c26a923 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d.h +++ b/libs/sf2dlib/libsf2d/include/sf2d.h @@ -4,10 +4,9 @@ * @date 22 March 2015 * @brief sf2dlib header */ -#ifndef SF2D_H -#define SF2D_H - +#pragma once #include <3ds.h> +#include #ifdef __cplusplus extern "C" { @@ -83,7 +82,6 @@ typedef enum { typedef enum { SF2D_PLACE_RAM, /**< RAM allocated */ SF2D_PLACE_VRAM, /**< VRAM allocated */ - SF2D_PLACE_TEMP /**< Temporary memory pool allocated */ } sf2d_place; // Structs @@ -131,21 +129,15 @@ typedef struct { */ typedef struct { - sf2d_place place; /**< Where the texture data resides, RAM or VRAM */ - int tiled; /**< Whether the tetxure is tiled or not */ - sf2d_texfmt pixel_format; /**< Pixel format */ - u32 params; /**< Texture filters and wrapping */ - int width; /**< Texture width */ - int height; /**< Texture height */ - int pow2_w; /**< Nearest power of 2 >= width */ - int pow2_h; /**< Nearest power of 2 >= height */ - int data_size; /**< Size of the raw texture data */ - void *data; /**< Pointer to the data */ + C3D_Tex tex; /**< citro3d texture object */ + int tiled; /**< Whether the texture is tiled or not */ + int width; /**< Actual texture width */ + int height; /**< Actual texture height */ } sf2d_texture; typedef struct { - sf2d_texture texture; // "inherit"/extend standard texture - float projection[4*4]; /**< Orthographic projection matrix for this target */ + C3D_RenderTarget* target; /**< citro3d render target object */ + C3D_Mtx projection; /**< Orthographic projection matrix for this target */ } sf2d_rendertarget; // Basic functions @@ -176,6 +168,12 @@ int sf2d_fini(); */ void sf2d_set_3D(int enable); +/** + * @brief Sets a transformation matrix to apply to vertices + * @param mtx Transformation matrix (or NULL to disable it) + */ +void sf2d_set_transform(C3D_Mtx* mtx); + /** * @brief Starts a frame * @param screen target screen @@ -416,14 +414,6 @@ void sf2d_bind_texture(const sf2d_texture *texture, GPU_TEXUNIT unit); */ void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 color); -/** - * @brief Binds a texture to a GPU texture unit with custom parameters - * @param texture the texture to bind - * @param unit GPU texture unit to bind to - * @param params the parameters the bind with the texture - */ -void sf2d_bind_texture_parameters(const sf2d_texture *texture, GPU_TEXUNIT unit, unsigned int params); - /** * @brief Changes the texture params (filters and wrapping) * @param texture the texture to change the params @@ -633,24 +623,6 @@ void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int */ void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color); -/** - * @brief Draws a part of a texture, with rotation, scaling, color and hotspot - * @param texture the texture to draw - * @param x the x coordinate to draw the texture to - * @param y the y coordinate to draw the texture to - * @param rad rotation (in radians) to draw the texture - * @param tex_x the starting point (x coordinate) where to start drawing - * @param tex_y the starting point (y coordinate) where to start drawing - * @param tex_w the width to draw from the starting point - * @param tex_h the height to draw from the starting point - * @param x_scale the x scale - * @param y_scale the y scale - * @param center_x the x position of the hotspot - * @param center_y the y position of the hotspot - * @param color the color to blend with the texture - */ -void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color); - /** * @brief Draws a texture blended in a certain depth * @param texture the texture to draw @@ -683,8 +655,7 @@ void sf2d_draw_texture_depth(const sf2d_texture *texture, int x, int y, signed s void sf2d_draw_texture_depth_blend(const sf2d_texture *texture, int x, int y, signed short z, u32 color); /** - * @brief Draws a texture using custom texture coordinates and parameters - * @param texture the texture to draw + * @brief Draws the currently-bound texture using custom texture coordinates * @param left the left coordinate of the texture to start drawing * @param top the top coordinate of the texture to start drawing * @param width the width to draw from the starting left coordinate @@ -693,10 +664,24 @@ void sf2d_draw_texture_depth_blend(const sf2d_texture *texture, int x, int y, si * @param v0 the V texture coordinate of the top vertices * @param u1 the U texture coordinate of the right vertices * @param v1 the V texture coordinate of the bottom vertices - * @param params the parameters to draw the texture with */ +void sf2d_draw_quad_uv_current(float left, float top, float right, float bottom, float u0, float v0, + float u1, float v1); + +/** + * @brief Like sf2d_draw_quad_uv_current, but binds the texture + * @param texture the texture to draw + **/ void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float right, float bottom, - float u0, float v0, float u1, float v1, unsigned int params); + float u0, float v0, float u1, float v1); + +/** + * @brief Like sf2d_draw_quad_uv_current, but binds the texture with the given blend color + * @param texture the texture to draw + * @param color the color to blend the texture with + **/ +void sf2d_draw_quad_uv_blend(const sf2d_texture *texture, float left, float top, float right, float bottom, + float u0, float v0, float u1, float v1, u32 color); /** * @brief Changes a pixel of the texture @@ -749,5 +734,3 @@ gfx3dSide_t sf2d_get_current_side(); #ifdef __cplusplus } #endif - -#endif diff --git a/libs/sf2dlib/libsf2d/include/sf2d_private.h b/libs/sf2dlib/libsf2d/include/sf2d_private.h deleted file mode 100644 index 07579ca..0000000 --- a/libs/sf2dlib/libsf2d/include/sf2d_private.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef SF2D_PRIVATE_H -#define SF2D_PRIVATE_H - -#include <3ds.h> -#include "sf2d.h" - - -void GPU_SetDummyTexEnv(u8 num); - -void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices); - -// Vector operations - -void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_vector_3f *vdst); - -// Matrix operations - -void matrix_copy(float *dst, const float *src); -void matrix_identity4x4(float *m); -void matrix_mult4x4(const float *src1, const float *src2, float *dst); -void matrix_set_z_rotation(float *m, float rad); -void matrix_rotate_z(float *m, float rad); -void matrix_set_scaling(float *m, float x_scale, float y_scale, float z_scale); -void matrix_swap_xy(float *m); - -void matrix_init_orthographic(float *m, float left, float right, float bottom, float top, float near, float far); -void matrix_gpu_set_uniform(const float *m, u32 startreg); - -unsigned int next_pow2(unsigned int v); - -#endif diff --git a/libs/sf2dlib/libsf2d/source/sf2d.c b/libs/sf2dlib/libsf2d/source/sf2d.c index 0b76e62..17bbee3 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d.c +++ b/libs/sf2dlib/libsf2d/source/sf2d.c @@ -1,22 +1,13 @@ #include #include "sf2d.h" -#include "sf2d_private.h" -#include "shader_vsh_shbin.h" +#include "shader_shbin.h" static int sf2d_initialized = 0; -static u32 clear_color = 0; -static u32 *gpu_cmd = NULL; -//GPU init variables -static int gpu_cmd_size = 0; // Temporary memory pool static void *pool_addr = NULL; static u32 pool_index = 0; static u32 pool_size = 0; -//GPU framebuffer address -static u32 *gpu_fb_addr = NULL; -//GPU depth buffer address -static u32 *gpu_depth_fb_addr = NULL; //VBlank wait static int vblank_wait = 1; //FPS calculation @@ -29,19 +20,14 @@ static gfx3dSide_t cur_side = GFX_LEFT; //Shader stuff static DVLB_s *dvlb = NULL; static shaderProgram_s shader; -static u32 projection_desc = -1; -//Matrix -static float ortho_matrix_top[4*4]; -static float ortho_matrix_bot[4*4]; +static int projection_desc = -1; +static int transform_desc = -1; +static int useTransform_desc = -1; //Rendertarget things -static sf2d_rendertarget * currentRenderTarget = NULL; -static void * targetDepthBuffer; -static int targetDepthBufferLen = 0; -//Apt hook cookie -static aptHookCookie apt_hook_cookie; -//Functions -static void apt_hook_func(APT_HookType hook, void *param); -static void reset_gpu_apt_resume(); +static sf2d_rendertarget * targetTopLeft; +static sf2d_rendertarget * targetTopRight; +static sf2d_rendertarget * targetBottom; +static int in_render; int sf2d_init() { @@ -54,47 +40,43 @@ int sf2d_init_advanced(int gpucmd_size, int temppool_size) { if (sf2d_initialized) return 0; - gpu_fb_addr = vramMemAlign(400*240*8, 0x100); - gpu_depth_fb_addr = vramMemAlign(400*240*8, 0x100); - gpu_cmd = linearAlloc(gpucmd_size * 4); - pool_addr = linearAlloc(temppool_size); - pool_size = temppool_size; - gpu_cmd_size = gpucmd_size; - gfxInitDefault(); - GPU_Init(NULL); gfxSet3D(false); - GPU_Reset(NULL, gpu_cmd, gpucmd_size); + C3D_Init(gpucmd_size*8); + + //Setup rendertargets + targetTopLeft = sf2d_create_rendertarget(400, 240); + targetTopRight = sf2d_create_rendertarget(400, 240); + targetBottom = sf2d_create_rendertarget(320, 240); + sf2d_set_clear_color(0); + C3D_RenderTargetSetOutput(targetTopLeft->target, GFX_TOP, GFX_LEFT, 0x1000); + C3D_RenderTargetSetOutput(targetTopRight->target, GFX_TOP, GFX_RIGHT, 0x1000); + C3D_RenderTargetSetOutput(targetBottom->target, GFX_BOTTOM, GFX_LEFT, 0x1000); + + //Setup temp pool + pool_addr = linearAlloc(temppool_size); + pool_size = temppool_size; //Setup the shader - dvlb = DVLB_ParseFile((u32 *)shader_vsh_shbin, shader_vsh_shbin_size); + dvlb = DVLB_ParseFile((u32 *)shader_shbin, shader_shbin_size); shaderProgramInit(&shader); shaderProgramSetVsh(&shader, &dvlb->DVLE[0]); //Get shader uniform descriptors projection_desc = shaderInstanceGetUniformLocation(shader.vertexShader, "projection"); + transform_desc = shaderInstanceGetUniformLocation(shader.vertexShader, "transform"); + useTransform_desc = shaderInstanceGetUniformLocation(shader.vertexShader, "useTransform"); - shaderProgramUse(&shader); - - matrix_init_orthographic(ortho_matrix_top, 0.0f, 400.0f, 0.0f, 240.0f, 0.0f, 1.0f); - matrix_init_orthographic(ortho_matrix_bot, 0.0f, 320.0f, 0.0f, 240.0f, 0.0f, 1.0f); - matrix_gpu_set_uniform(ortho_matrix_top, projection_desc); - - //Register the apt callback hook - aptHook(&apt_hook_cookie, apt_hook_func, NULL); + C3D_BindProgram(&shader); + C3D_CullFace(GPU_CULL_NONE); + C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); + C3D_BoolUnifSet(GPU_VERTEX_SHADER, useTransform_desc, false); vblank_wait = 1; current_fps = 0.0f; frames = 0; last_time = osGetTime(); - cur_screen = GFX_TOP; - cur_side = GFX_LEFT; - - GPUCMD_Finalize(); - GPUCMD_FlushAndRun(); - gspWaitForP3D(); - sf2d_pool_reset(); sf2d_initialized = 1; @@ -106,17 +88,12 @@ int sf2d_fini() { if (!sf2d_initialized) return 0; - aptUnhook(&apt_hook_cookie); + linearFree(pool_addr); - gfxExit(); shaderProgramFree(&shader); DVLB_Free(dvlb); - - linearFree(pool_addr); - linearFree(gpu_cmd); - vramFree(gpu_fb_addr); - vramFree(gpu_depth_fb_addr); - linearFree(targetDepthBuffer); + C3D_Fini(); + gfxExit(); sf2d_initialized = 0; @@ -128,143 +105,54 @@ void sf2d_set_3D(int enable) gfxSet3D(enable); } +void sf2d_set_transform(C3D_Mtx* mtx) +{ + C3D_BoolUnifSet(GPU_VERTEX_SHADER, useTransform_desc, mtx != NULL); + if (mtx) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, transform_desc, mtx); + } +} + void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side) { - sf2d_pool_reset(); - GPUCMD_SetBufferOffset(0); + cur_screen = screen; + cur_side = side; - // Only upload the uniform if the screen changes - if (screen != cur_screen) { - if (screen == GFX_TOP) { - matrix_gpu_set_uniform(ortho_matrix_top, projection_desc); - } else { - matrix_gpu_set_uniform(ortho_matrix_bot, projection_desc); - } - cur_screen = screen; - } - - int screen_w; if (screen == GFX_TOP) { - screen_w = 400; - cur_side = side; + if (side == GFX_LEFT) { + sf2d_start_frame_target(targetTopLeft); + } else { + sf2d_start_frame_target(targetTopRight); + } } else { - screen_w = 320; + sf2d_start_frame_target(targetBottom); } - GPU_SetViewport((u32 *)osConvertVirtToPhys(gpu_depth_fb_addr), - (u32 *)osConvertVirtToPhys(gpu_fb_addr), - 0, 0, 240, screen_w); - - GPU_DepthMap(-1.0f, 0.0f); - GPU_SetFaceCulling(GPU_CULL_NONE); - GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); - GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); - GPU_SetBlendingColor(0,0,0,0); - GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); - GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0); - GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0); - - GPU_SetAlphaBlending( - GPU_BLEND_ADD, - GPU_BLEND_ADD, - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, - GPU_ONE, GPU_ZERO - ); - - GPU_SetAlphaTest(false, GPU_ALWAYS, 0x00); - - GPU_SetDummyTexEnv(1); - GPU_SetDummyTexEnv(2); - GPU_SetDummyTexEnv(3); - GPU_SetDummyTexEnv(4); - GPU_SetDummyTexEnv(5); } void sf2d_start_frame_target(sf2d_rendertarget *target) { - sf2d_pool_reset(); - GPUCMD_SetBufferOffset(0); - - // Upload saved uniform - matrix_gpu_set_uniform(target->projection, projection_desc); - - int bufferLen = target->texture.width * target->texture.height * 4; // apparently depth buffer is (or can be) 32bit? - if (bufferLen > targetDepthBufferLen) { // expand depth buffer - if (targetDepthBufferLen > 0) linearFree(targetDepthBuffer); - targetDepthBuffer = linearAlloc(bufferLen); - memset(targetDepthBuffer, 0, bufferLen); - targetDepthBufferLen = bufferLen; + if (!in_render) { + sf2d_pool_reset(); + C3D_FrameBegin(vblank_wait ? C3D_FRAME_SYNCDRAW : 0); + in_render = 1; } - GPU_SetViewport((u32 *)osConvertVirtToPhys(targetDepthBuffer), - (u32 *)osConvertVirtToPhys(target->texture.data), - 0, 0, target->texture.height, target->texture.width); - - currentRenderTarget = target; - - GPU_DepthMap(-1.0f, 0.0f); - GPU_SetFaceCulling(GPU_CULL_NONE); - GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); - GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); - GPU_SetBlendingColor(0,0,0,0); - GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); - GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0); - GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0); - - GPU_SetAlphaBlending( - GPU_BLEND_ADD, - GPU_BLEND_ADD, - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, - GPU_ONE, GPU_ZERO - ); - - GPU_SetAlphaTest(false, GPU_ALWAYS, 0x00); - - GPU_SetDummyTexEnv(1); - GPU_SetDummyTexEnv(2); - GPU_SetDummyTexEnv(3); - GPU_SetDummyTexEnv(4); - GPU_SetDummyTexEnv(5); + C3D_FrameDrawOn(target->target); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, projection_desc, &target->projection); } void sf2d_end_frame() { - GPU_FinishDrawing(); - GPUCMD_Finalize(); - GPUCMD_FlushAndRun(); - gspWaitForP3D(); - - if (!currentRenderTarget) { - //Copy the GPU rendered FB to the screen FB - if (cur_screen == GFX_TOP) { - GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400), - (u32 *)gfxGetFramebuffer(GFX_TOP, cur_side, NULL, NULL), - GX_BUFFER_DIM(240, 400), 0x1000); - } else { - GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 320), - (u32 *)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), - GX_BUFFER_DIM(240, 320), 0x1000); - } - gspWaitForPPF(); - - //Clear the screen - GX_MemoryFill( - gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, - gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH); - gspWaitForPSC0(); - } else { - //gspWaitForPPF(); - //gspWaitForPSC0(); - sf2d_texture_tile32(&(currentRenderTarget->texture)); - } - currentRenderTarget = NULL; + // Nothing } void sf2d_swapbuffers() { - gfxSwapBuffersGpu(); - if (vblank_wait) { - gspWaitForEvent(GSPGPU_EVENT_VBlank0, false); - } + if (!in_render) return; + + in_render = 0; + C3D_FrameEnd(0); + //Calculate FPS frames++; u64 delta_time = osGetTime() - last_time; @@ -323,19 +211,17 @@ void sf2d_pool_reset() void sf2d_set_clear_color(u32 color) { - // GX_SetMemoryFill wants the color inverted? - clear_color = RGBA8_GET_R(color) << 24 | - RGBA8_GET_G(color) << 16 | - RGBA8_GET_B(color) << 8 | - RGBA8_GET_A(color) << 0; + sf2d_clear_target(targetTopLeft, color); + sf2d_clear_target(targetTopRight, color); + sf2d_clear_target(targetBottom, color); } void sf2d_set_scissor_test(GPU_SCISSORMODE mode, u32 x, u32 y, u32 w, u32 h) { if (cur_screen == GFX_TOP) { - GPU_SetScissorTest(mode, 240 - (y + h), 400 - (x + w), 240 - y, 400 - x); + C3D_SetScissor(mode, 240 - (y + h), 400 - (x + w), 240 - y, 400 - x); } else { - GPU_SetScissorTest(mode, 240 - (y + h), 320 - (x + w), 240 - y, 320 - x); + C3D_SetScissor(mode, 240 - (y + h), 320 - (x + w), 240 - y, 320 - x); } } @@ -348,26 +234,3 @@ gfx3dSide_t sf2d_get_current_side() { return cur_side; } - -static void apt_hook_func(APT_HookType hook, void *param) -{ - if (hook == APTHOOK_ONRESTORE) { - reset_gpu_apt_resume(); - } -} - -static void reset_gpu_apt_resume() -{ - GPU_Reset(NULL, gpu_cmd, gpu_cmd_size); // Only required for custom GPU cmd sizes - shaderProgramUse(&shader); - - if (cur_screen == GFX_TOP) { - matrix_gpu_set_uniform(ortho_matrix_top, projection_desc); - } else { - matrix_gpu_set_uniform(ortho_matrix_bot, projection_desc); - } - - GPUCMD_Finalize(); - GPUCMD_FlushAndRun(); - gspWaitForP3D(); -} diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index a28e818..38eb26c 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -1,5 +1,4 @@ #include "sf2d.h" -#include "sf2d_private.h" #include #ifndef M_PI @@ -7,27 +6,19 @@ #endif void sf2d_setup_env_internal(const sf2d_vertex_pos_col* vertices) { - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, 0, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_UNSIGNED_BYTE, 4); + + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_col), 2, 0x10); } void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color) @@ -64,21 +55,21 @@ void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 col sf2d_setup_env_internal(vertices); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices) { sf2d_setup_env_internal(vertices); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_triangle_internal(const sf2d_vertex_pos_col *vertices) { sf2d_setup_env_internal(vertices); - GPU_DrawArray(GPU_TRIANGLES, 0, 3); + C3D_DrawArrays(GPU_TRIANGLES, 0, 3); } void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) @@ -133,17 +124,14 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; - float m[4*4]; - matrix_set_z_rotation(m, rad); - sf2d_vector_3f rot[4]; - - int i; - for (i = 0; i < 4; i++) { - vector_mult_matrix4x4(m, &vertices[i].position, &rot[i]); - vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z}; - } + C3D_Mtx m; + Mtx_Identity(&m); + Mtx_Translate(&m, x+w2, y+h2, 0, true); + Mtx_RotateZ(&m, rad, true); + sf2d_set_transform(&m); sf2d_draw_rectangle_internal(vertices); + sf2d_set_transform(NULL); } void sf2d_draw_rectangle_gradient(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction) @@ -182,17 +170,14 @@ void sf2d_draw_rectangle_gradient_rotate(int x, int y, int w, int h, u32 color1, vertices[2].color = (direction == SF2D_LEFT_TO_RIGHT) ? color1 : color2; vertices[3].color = color2; - float m[4*4]; - matrix_set_z_rotation(m, rad); - sf2d_vector_3f rot[4]; - - int i; - for (i = 0; i < 4; i++) { - vector_mult_matrix4x4(m, &vertices[i].position, &rot[i]); - vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z}; - } + C3D_Mtx m; + Mtx_Identity(&m); + Mtx_Translate(&m, x+w2, y+h2, 0, true); + Mtx_RotateZ(&m, rad, true); + sf2d_set_transform(&m); sf2d_draw_rectangle_internal(vertices); + sf2d_set_transform(NULL); } void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) @@ -227,5 +212,5 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) sf2d_setup_env_internal(vertices); - GPU_DrawArray(GPU_TRIANGLE_FAN, 0, num_segments + 2); + C3D_DrawArrays(GPU_TRIANGLE_FAN, 0, num_segments + 2); } diff --git a/libs/sf2dlib/libsf2d/source/sf2d_private.c b/libs/sf2dlib/libsf2d/source/sf2d_private.c deleted file mode 100644 index 30b0ed8..0000000 --- a/libs/sf2dlib/libsf2d/source/sf2d_private.c +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include "sf2d_private.h" - -#ifndef M_PI -#define M_PI (3.14159265358979323846) -#endif - -//stolen from staplebutt -void GPU_SetDummyTexEnv(u8 num) -{ - GPU_SetTexEnv(num, - GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0), - GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0), - GPU_TEVOPERANDS(0,0,0), - GPU_TEVOPERANDS(0,0,0), - GPU_REPLACE, - GPU_REPLACE, - 0xFFFFFFFF); -} - -void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_vector_3f *vdst) -{ - vdst->x = msrc[0*4 + 0]*vsrc->x + msrc[0*4 + 1]*vsrc->y + msrc[0*4 + 2]*vsrc->z + msrc[0*4 + 3]; - vdst->y = msrc[1*4 + 0]*vsrc->x + msrc[1*4 + 1]*vsrc->y + msrc[1*4 + 2]*vsrc->z + msrc[1*4 + 3]; - vdst->z = msrc[2*4 + 0]*vsrc->x + msrc[2*4 + 1]*vsrc->y + msrc[2*4 + 2]*vsrc->z + msrc[2*4 + 3]; -} - -void matrix_gpu_set_uniform(const float *m, u32 startreg) -{ - GPU_SetFloatUniform(GPU_VERTEX_SHADER, startreg, (u32 *)m, 4); -} - -void matrix_copy(float *dst, const float *src) -{ - memcpy(dst, src, sizeof(float)*4*4); -} - -void matrix_identity4x4(float *m) -{ - m[0] = m[5] = m[10] = m[15] = 1.0f; - m[1] = m[2] = m[3] = 0.0f; - m[4] = m[6] = m[7] = 0.0f; - m[8] = m[9] = m[11] = 0.0f; - m[12] = m[13] = m[14] = 0.0f; -} - -void matrix_mult4x4(const float *src1, const float *src2, float *dst) -{ - int i, j, k; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - dst[i*4 + j] = 0.0f; - for (k = 0; k < 4; k++) { - dst[i*4 + j] += src1[i*4 + k]*src2[k*4 + j]; - } - } - } -} - -void matrix_set_z_rotation(float *m, float rad) -{ - float c = cosf(rad); - float s = sinf(rad); - - matrix_identity4x4(m); - - m[0] = c; - m[1] = -s; - m[4] = s; - m[5] = c; -} - -void matrix_rotate_z(float *m, float rad) -{ - float mr[4*4], mt[4*4]; - matrix_set_z_rotation(mr, rad); - matrix_mult4x4(mr, m, mt); - matrix_copy(m, mt); -} - -void matrix_set_scaling(float *m, float x_scale, float y_scale, float z_scale) -{ - matrix_identity4x4(m); - m[0] = x_scale; - m[5] = y_scale; - m[10] = z_scale; -} - -void matrix_swap_xy(float *m) -{ - float ms[4*4], mt[4*4]; - matrix_identity4x4(ms); - - ms[0] = 0.0f; - ms[1] = 1.0f; - ms[4] = 1.0f; - ms[5] = 0.0f; - - matrix_mult4x4(ms, m, mt); - matrix_copy(m, mt); -} - -void matrix_init_orthographic(float *m, float left, float right, float bottom, float top, float near, float far) -{ - float mo[4*4], mp[4*4]; - - mo[0x0] = 2.0f/(right-left); - mo[0x1] = 0.0f; - mo[0x2] = 0.0f; - mo[0x3] = -(right+left)/(right-left); - - mo[0x4] = 0.0f; - mo[0x5] = 2.0f/(top-bottom); - mo[0x6] = 0.0f; - mo[0x7] = -(top+bottom)/(top-bottom); - - mo[0x8] = 0.0f; - mo[0x9] = 0.0f; - mo[0xA] = -2.0f/(far-near); - mo[0xB] = (far+near)/(far-near); - - mo[0xC] = 0.0f; - mo[0xD] = 0.0f; - mo[0xE] = 0.0f; - mo[0xF] = 1.0f; - - matrix_identity4x4(mp); - mp[0xA] = 0.5; - mp[0xB] = -0.5; - - //Convert Z [-1, 1] to [-1, 0] (PICA shiz) - matrix_mult4x4(mp, mo, m); - // Rotate 180 degrees - matrix_rotate_z(m, M_PI); - // Swap X and Y axis - matrix_swap_xy(m); -} - -//Grabbed from: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -unsigned int next_pow2(unsigned int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return v+1; -} diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index d0b7f09..760a24f 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -2,7 +2,6 @@ #include #include #include "sf2d.h" -#include "sf2d_private.h" #ifndef M_PI #define M_PI (3.14159265358979323846) @@ -10,159 +9,98 @@ #define TEX_MIN_SIZE 32 -static unsigned int nibbles_per_pixel(sf2d_texfmt format) +//Grabbed from: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +unsigned int next_pow2(unsigned int v) { - switch (format) { - case TEXFMT_RGBA8: - return 8; - case TEXFMT_RGB8: - return 6; - case TEXFMT_RGB5A1: - case TEXFMT_RGB565: - case TEXFMT_RGBA4: - case TEXFMT_IA8: - return 4; - case TEXFMT_A4: - return 1; - case TEXFMT_I8: - case TEXFMT_A8: - case TEXFMT_IA4: - default: - return 2; - } -} - -static int calc_buffer_size(sf2d_texfmt pixel_format, int width, int height) -{ - return width * height * (nibbles_per_pixel(pixel_format)>>1); + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v >= TEX_MIN_SIZE ? v : TEX_MIN_SIZE; } sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_format, sf2d_place place) { - int pow2_w = next_pow2(width); - int pow2_h = next_pow2(height); - - if (pow2_w < TEX_MIN_SIZE) pow2_w = TEX_MIN_SIZE; - if (pow2_h < TEX_MIN_SIZE) pow2_h = TEX_MIN_SIZE; - - int data_size = calc_buffer_size(pixel_format, pow2_w, pow2_h); - void *data; + sf2d_texture *texture = calloc(1, sizeof(*texture)); + if (!texture) return NULL; + bool success = false; + texture->width = width; + texture->height = height; if (place == SF2D_PLACE_RAM) { - // If there's not enough linear heap space, return - if (linearSpaceFree() < data_size) { - return NULL; - } - data = linearMemAlign(data_size, 0x80); + success = C3D_TexInit(&texture->tex, next_pow2(width), next_pow2(height), pixel_format); } else if (place == SF2D_PLACE_VRAM) { - // If there's not enough VRAM heap space, return - if (vramSpaceFree() < data_size) { - return NULL; - } - data = vramMemAlign(data_size, 0x80); - } else if (place == SF2D_PLACE_TEMP) { - if (sf2d_pool_space_free() < data_size) { - return NULL; - } - data = sf2d_pool_memalign(data_size, 0x80); - } else { - //wot? + success = C3D_TexInitVRAM(&texture->tex, next_pow2(width), next_pow2(height), pixel_format); + } + + if (!success) { + free(texture); return NULL; } - sf2d_texture *texture = malloc(sizeof(*texture)); - - texture->tiled = 0; - texture->place = place; - texture->pixel_format = pixel_format; - texture->params = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) - | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST) - | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) - | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER); - texture->width = width; - texture->height = height; - texture->pow2_w = pow2_w; - texture->pow2_h = pow2_h; - texture->data_size = data_size; - texture->data = data; - - if (place == SF2D_PLACE_VRAM) { - GX_MemoryFill(texture->data, 0x00000000, (u32*)&((u8*)texture->data)[texture->data_size], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, - NULL, 0x00000000, NULL, 0); - gspWaitForPSC0(); - } else { - memset(texture->data, 0, texture->data_size); - } - + C3D_TexSetWrap(&texture->tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); return texture; } sf2d_rendertarget *sf2d_create_rendertarget(int width, int height) { - sf2d_texture *tx = sf2d_create_texture(width, height, TEXFMT_RGBA8, SF2D_PLACE_RAM); - sf2d_rendertarget *rt = malloc(sizeof(*rt)); - //memcpy(rt, tx, sizeof(*tx)); - rt->texture = *tx; - free(tx); - //tx = * rt->texture; - //rt->projection + sf2d_rendertarget *rt = calloc(1, sizeof(*rt)); + if (!rt) return NULL; - matrix_init_orthographic(rt->projection, 0.0f, width, height, 0.0f, 0.0f, 1.0f); - matrix_rotate_z(rt->projection, M_PI / 2.0f); + rt->target = C3D_RenderTargetCreate(height, width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + if (!rt->target) + { + free(rt); + return NULL; + } + Mtx_OrthoTilt(&rt->projection, 0.0f, width, height, 0.0f, 0.0f, 1.0f, true); return rt; } void sf2d_free_texture(sf2d_texture *texture) { if (texture) { - if (texture->place == SF2D_PLACE_RAM) { - linearFree(texture->data); - } else if (texture->place == SF2D_PLACE_VRAM) { - vramFree(texture->data); - } + C3D_TexDelete(&texture->tex); free(texture); } } void sf2d_free_target(sf2d_rendertarget *target) { - sf2d_free_texture(&(target->texture)); - //free(target); // unnecessary since the texture is the start of the target struct + C3D_RenderTargetDelete(target->target); + free(target); } -void sf2d_clear_target(sf2d_rendertarget *target, u32 color) { - if (color == 0) { // if fully transparent, take a shortcut - memset(target->texture.data, 0, target->texture.width * target->texture.height * 4); - sf2d_texture_tile32(&(target->texture)); - return; - } - +void sf2d_clear_target(sf2d_rendertarget *target, u32 color) +{ color = ((color>>24)&0x000000FF) | ((color>>8)&0x0000FF00) | ((color<<8)&0x00FF0000) | ((color<<24)&0xFF000000); // reverse byte order - - int itarget = target->texture.width * target->texture.height; - for (int i = 0; i < itarget; i++) { memcpy(target->texture.data + i*4, &color, 4); } - - sf2d_texture_tile32(&(target->texture)); + C3D_RenderTargetSetClear(target->target, C3D_CLEAR_ALL, color, 0); } void sf2d_texture_tile32_hardware(sf2d_texture *texture, const void *data, int w, int h) { if (texture->tiled) return; - const u32 flags = (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | - GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | - GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)); + + const u32 flags = (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)); GSPGPU_FlushDataCache(data, (w*h)<<2); - GX_DisplayTransfer( - (u32*)data, - GX_BUFFER_DIM(w, h), - (u32*)texture->data, - GX_BUFFER_DIM(texture->pow2_w, texture->pow2_h), - flags - ); - gspWaitForPPF(); - GSPGPU_InvalidateDataCache(texture->data, texture->data_size); + GSPGPU_FlushDataCache(texture->tex.data, texture->tex.size); + + C3D_SafeDisplayTransfer( + (u32*)data, + GX_BUFFER_DIM(w, h), + (u32*)texture->tex.data, + GX_BUFFER_DIM(texture->tex.width, texture->tex.height), + flags + ); + + gspWaitForPPF(); texture->tiled = 1; } @@ -170,14 +108,14 @@ void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int sour { // TODO: add support for non-RGBA8 textures - u8 *tmp = linearAlloc((dst->pow2_w * dst->pow2_h)<<2); + u8 *tmp = linearAlloc((dst->tex.width * dst->tex.height)<<2); int i, j; for (i = 0; i < source_h; i++) { for (j = 0; j < source_w; j++) { - ((u32 *)tmp)[i*dst->pow2_w + j] = __builtin_bswap32(((u32 *)rgba8)[i*source_w + j]); + ((u32 *)tmp)[i*dst->tex.width + j] = __builtin_bswap32(((u32 *)rgba8)[i*source_w + j]); } } - sf2d_texture_tile32_hardware(dst, tmp, dst->pow2_w, dst->pow2_h); + sf2d_texture_tile32_hardware(dst, tmp, dst->tex.width, dst->tex.height); linearFree(tmp); } @@ -190,86 +128,45 @@ sf2d_texture *sf2d_create_texture_mem_RGBA8(const void *src_buffer, int src_w, i return tex; } +static inline int sf2d_get_texunit(GPU_TEXUNIT unit) +{ + switch (unit) { + case GPU_TEXUNIT0: return 0; + case GPU_TEXUNIT1: return 1; + case GPU_TEXUNIT2: return 2; + default: return -1; + } +} + void sf2d_bind_texture(const sf2d_texture *texture, GPU_TEXUNIT unit) { - GPU_SetTextureEnable(unit); + C3D_TexBind(sf2d_get_texunit(unit), (C3D_Tex*)&texture->tex); - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_TEXTURE0, GPU_TEXTURE0), - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_TEXTURE0, GPU_TEXTURE0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); - - GPU_SetTexture( - unit, - (u32 *)osConvertVirtToPhys(texture->data), - texture->pow2_w, - texture->pow2_h, - texture->params, - texture->pixel_format - ); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); } void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 color) { - GPU_SetTextureEnable(unit); + C3D_TexBind(sf2d_get_texunit(unit), (C3D_Tex*)&texture->tex); - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_CONSTANT, GPU_CONSTANT), - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_CONSTANT, GPU_CONSTANT), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_MODULATE, GPU_MODULATE, - color - ); - - GPU_SetTexture( - unit, - (u32 *)osConvertVirtToPhys(texture->data), - texture->pow2_w, - texture->pow2_h, - texture->params, - texture->pixel_format - ); -} - -void sf2d_bind_texture_parameters(const sf2d_texture *texture, GPU_TEXUNIT unit, unsigned int params) -{ - GPU_SetTextureEnable(unit); - - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_TEXTURE0, GPU_TEXTURE0), - GPU_TEVSOURCES(GPU_TEXTURE0, GPU_TEXTURE0, GPU_TEXTURE0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); - - GPU_SetTexture( - unit, - (u32 *)osConvertVirtToPhys(texture->data), - texture->pow2_w, - texture->pow2_h, - params, - texture->pixel_format - ); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + C3D_TexEnvColor(env, color); } void sf2d_texture_set_params(sf2d_texture *texture, u32 params) { - texture->params = params; + texture->tex.param = params; } int sf2d_texture_get_params(const sf2d_texture *texture) { - return texture->params; + return texture->tex.param; } static inline void sf2d_draw_texture_generic(const sf2d_texture *texture, int x, int y) @@ -285,27 +182,24 @@ static inline void sf2d_draw_texture_generic(const sf2d_texture *texture, int x, vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+h, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x+w, (float)y+h, SF2D_DEFAULT_DEPTH}; - float u = texture->width/(float)texture->pow2_w; - float v = texture->height/(float)texture->pow2_h; + float u = texture->width/(float)texture->tex.width; + float v = texture->height/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; vertices[2].texcoord = (sf2d_vector_2f){0.0f, v}; vertices[3].texcoord = (sf2d_vector_2f){u, v}; - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture(const sf2d_texture *texture, int x, int y) @@ -344,8 +238,8 @@ static inline void sf2d_draw_texture_rotate_hotspot_generic(const sf2d_texture * vertices[3].position.y = h - center_y; vertices[3].position.z = SF2D_DEFAULT_DEPTH; - float u = w/(float)texture->pow2_w; - float v = h/(float)texture->pow2_h; + float u = w/(float)texture->tex.width; + float v = h/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; @@ -362,19 +256,16 @@ static inline void sf2d_draw_texture_rotate_hotspot_generic(const sf2d_texture * vertices[i].position.y = _x*s + _y*c + y; } - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_rotate_hotspot(const sf2d_texture *texture, int x, int y, float rad, float center_x, float center_y) @@ -428,8 +319,8 @@ static inline void sf2d_draw_texture_rotate_scale_hotspot_generic(const sf2d_tex vertices[3].position.y = h - center_y * scale_y; vertices[3].position.z = SF2D_DEFAULT_DEPTH; - float u = w/(float)texture->pow2_w; - float v = h/(float)texture->pow2_h; + float u = w/(float)texture->tex.width; + float v = h/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; @@ -446,19 +337,16 @@ static inline void sf2d_draw_texture_rotate_scale_hotspot_generic(const sf2d_tex vertices[i].position.y = _x*s + _y*c + y; } - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_rotate_scale_hotspot(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y) @@ -483,29 +371,26 @@ static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, i vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+tex_h, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x+tex_w, (float)y+tex_h, SF2D_DEFAULT_DEPTH}; - float u0 = tex_x/(float)texture->pow2_w; - float v0 = tex_y/(float)texture->pow2_h; - float u1 = (tex_x+tex_w)/(float)texture->pow2_w; - float v1 = (tex_y+tex_h)/(float)texture->pow2_h; + float u0 = tex_x/(float)texture->tex.width; + float v0 = tex_y/(float)texture->tex.height; + float u1 = (tex_x+tex_w)/(float)texture->tex.width; + float v1 = (tex_y+tex_h)/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){u0, v0}; vertices[1].texcoord = (sf2d_vector_2f){u1, v0}; vertices[2].texcoord = (sf2d_vector_2f){u0, v1}; vertices[3].texcoord = (sf2d_vector_2f){u1, v1}; - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h) @@ -533,27 +418,24 @@ static inline void sf2d_draw_texture_scale_generic(const sf2d_texture *texture, vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+hs, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x+ws, (float)y+hs, SF2D_DEFAULT_DEPTH}; - float u = texture->width/(float)texture->pow2_w; - float v = texture->height/(float)texture->pow2_h; + float u = texture->width/(float)texture->tex.width; + float v = texture->height/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; vertices[2].texcoord = (sf2d_vector_2f){0.0f, v}; vertices[3].texcoord = (sf2d_vector_2f){u, v}; - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_scale(const sf2d_texture *texture, int x, int y, float x_scale, float y_scale) @@ -573,10 +455,10 @@ static inline void sf2d_draw_texture_part_scale_generic(const sf2d_texture *text sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; - float u0 = tex_x/(float)texture->pow2_w; - float v0 = tex_y/(float)texture->pow2_h; - float u1 = (tex_x+tex_w)/(float)texture->pow2_w; - float v1 = (tex_y+tex_h)/(float)texture->pow2_h; + float u0 = tex_x/(float)texture->tex.width; + float v0 = tex_y/(float)texture->tex.height; + float u1 = (tex_x+tex_w)/(float)texture->tex.width; + float v1 = (tex_y+tex_h)/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){u0, v0}; vertices[1].texcoord = (sf2d_vector_2f){u1, v0}; @@ -591,19 +473,16 @@ static inline void sf2d_draw_texture_part_scale_generic(const sf2d_texture *text vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+tex_h, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x+tex_w, (float)y+tex_h, SF2D_DEFAULT_DEPTH}; - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part_scale(const sf2d_texture *texture, float x, float y, float tex_x, float tex_y, float tex_w, float tex_h, float x_scale, float y_scale) @@ -618,23 +497,23 @@ void sf2d_draw_texture_part_scale_blend(const sf2d_texture *texture, float x, fl sf2d_draw_texture_part_scale_generic(texture, x, y, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); } -static inline void sf2d_draw_texture_part_rotate_scale_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y) +static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) { sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; - int w = tex_w; - int h = tex_h; + int w2 = (tex_w * x_scale)/2.0f; + int h2 = (tex_h * y_scale)/2.0f; - vertices[0].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH}; - vertices[1].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH}; - vertices[2].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float) (h - center_y) * y_scale, SF2D_DEFAULT_DEPTH}; - vertices[3].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float) h - center_y * y_scale, SF2D_DEFAULT_DEPTH}; + vertices[0].position = (sf2d_vector_3f){(float)-w2, (float)-h2, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){(float) w2, (float)-h2, SF2D_DEFAULT_DEPTH}; + vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH}; + vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH}; - float u0 = tex_x/(float)texture->pow2_w; - float v0 = tex_y/(float)texture->pow2_h; - float u1 = (tex_x+tex_w)/(float)texture->pow2_w; - float v1 = (tex_y+tex_h)/(float)texture->pow2_h; + float u0 = tex_x/(float)texture->tex.width; + float v0 = tex_y/(float)texture->tex.height; + float u1 = (tex_x+tex_w)/(float)texture->tex.width; + float v1 = (tex_y+tex_h)/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){u0, v0}; vertices[1].texcoord = (sf2d_vector_2f){u1, v0}; @@ -651,37 +530,28 @@ static inline void sf2d_draw_texture_part_rotate_scale_hotspot_generic(const sf2 vertices[i].position.y = _x*s + _y*c + y; } - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) { sf2d_bind_texture(texture, GPU_TEXUNIT0); - sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f); + sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); } void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color) { sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); - sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f); -} - -void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color) -{ - sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); - sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, center_x, center_y); + sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); } static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, int x, int y, signed short z) @@ -698,27 +568,24 @@ static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+h, depth}; vertices[3].position = (sf2d_vector_3f){(float)x+w, (float)y+h, depth}; - float u = texture->width/(float)texture->pow2_w; - float v = texture->height/(float)texture->pow2_h; + float u = texture->width/(float)texture->tex.width; + float v = texture->height/(float)texture->tex.height; vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; vertices[2].texcoord = (sf2d_vector_2f){0.0f, v}; vertices[3].texcoord = (sf2d_vector_2f){u, v}; - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); + + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_depth(const sf2d_texture *texture, int x, int y, signed short z) @@ -733,8 +600,7 @@ void sf2d_draw_texture_depth_blend(const sf2d_texture *texture, int x, int y, si sf2d_draw_texture_depth_generic(texture, x, y, z); } - -void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float right, float bottom, float u0, float v0, float u1, float v1, unsigned int params) +void sf2d_draw_quad_uv_current(float left, float top, float right, float bottom, float u0, float v0, float u1, float v1) { sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; @@ -749,21 +615,28 @@ void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float vertices[2].texcoord = (sf2d_vector_2f){u0, v1}; vertices[3].texcoord = (sf2d_vector_2f){u1, v1}; - sf2d_bind_texture_parameters(texture, GPU_TEXUNIT0, params); + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vertices, sizeof(sf2d_vertex_pos_tex), 2, 0x10); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); +} + +void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float right, float bottom, float u0, float v0, float u1, float v1) +{ + sf2d_bind_texture(texture, GPU_TEXUNIT0); + sf2d_draw_quad_uv_current(left, top, right, bottom, u0, v0, u1, v1); +} + +void sf2d_draw_quad_uv_blend(const sf2d_texture *texture, float left, float top, float right, float bottom, float u0, float v0, float u1, float v1, u32 color) +{ + sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); + sf2d_draw_quad_uv_current(left, top, right, bottom, u0, v0, u1, v1); } // Grabbed from Citra Emulator (citra/src/video_core/utils.h) @@ -786,25 +659,25 @@ static inline u32 get_morton_offset(u32 x, u32 y, u32 bytes_per_pixel) void sf2d_set_pixel(sf2d_texture *texture, int x, int y, u32 new_color) { - y = (texture->pow2_h - 1 - y); + y = (texture->tex.height - 1 - y); if (texture->tiled) { u32 coarse_y = y & ~7; - u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->pow2_w * 4; - *(u32 *)(texture->data + offset) = new_color; + u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->tex.width * 4; + *(u32 *)(texture->tex.data + offset) = new_color; } else { - ((u32 *)texture->data)[x + y * texture->pow2_w] = new_color; + ((u32 *)texture->tex.data)[x + y * texture->tex.width] = new_color; } } u32 sf2d_get_pixel(sf2d_texture *texture, int x, int y) { - y = (texture->pow2_h - 1 - y); + y = (texture->tex.height - 1 - y); if (texture->tiled) { u32 coarse_y = y & ~7; - u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->pow2_w * 4; - return *(u32 *)(texture->data + offset); + u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->tex.width * 4; + return *(u32 *)(texture->tex.data + offset); } else { - return ((u32 *)texture->data)[x + y * texture->pow2_w]; + return ((u32 *)texture->tex.data)[x + y * texture->tex.width]; } } @@ -814,21 +687,21 @@ void sf2d_texture_tile32(sf2d_texture *texture) if (texture->tiled) return; // TODO: add support for non-RGBA8 textures - u8 *tmp = linearAlloc(texture->pow2_w * texture->pow2_h * 4); + u8 *tmp = linearAlloc(texture->tex.width * texture->tex.height * 4); int i, j; - for (j = 0; j < texture->pow2_h; j++) { - for (i = 0; i < texture->pow2_w; i++) { + for (j = 0; j < texture->tex.height; j++) { + for (i = 0; i < texture->tex.width; i++) { u32 coarse_y = j & ~7; - u32 dst_offset = get_morton_offset(i, j, 4) + coarse_y * texture->pow2_w * 4; + u32 dst_offset = get_morton_offset(i, j, 4) + coarse_y * texture->tex.width * 4; - u32 v = ((u32 *)texture->data)[i + (texture->pow2_h - 1 - j)*texture->pow2_w]; + u32 v = ((u32 *)texture->tex.data)[i + (texture->tex.height - 1 - j)*texture->tex.width]; *(u32 *)(tmp + dst_offset) = __builtin_bswap32(v); /* RGBA8 -> ABGR8 */ } } - memcpy(texture->data, tmp, texture->pow2_w*texture->pow2_h*4); + memcpy(texture->tex.data, tmp, texture->tex.width*texture->tex.height*4); linearFree(tmp); texture->tiled = 1; diff --git a/libs/sf2dlib/sample/Makefile b/libs/sf2dlib/sample/Makefile index 27ae68b..9cad649 100644 --- a/libs/sf2dlib/sample/Makefile +++ b/libs/sf2dlib/sample/Makefile @@ -17,6 +17,7 @@ include $(DEVKITARM)/3ds_rules # INCLUDES is a list of directories containing header files # # NO_SMDH: if set to anything, no SMDH file is generated. +# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) # APP_TITLE is the name of the app stored in the SMDH file (Optional) # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) @@ -31,6 +32,7 @@ BUILD := build SOURCES := source DATA := data INCLUDES := include +#ROMFS := romfs APP_TITLE := SF2DLIB sample APP_DESCRIPTION := SF2DLIB sample @@ -39,10 +41,10 @@ APP_AUTHOR := xerpi #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft CFLAGS := -g -Wall -O2 -mword-relocations \ - -fomit-frame-pointer -ffast-math \ + -fomit-frame-pointer -ffunction-sections \ $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS @@ -52,7 +54,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsf2d -lctru -lm +LIBS := -lsf2d -lcitro3d -lctru -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -79,6 +81,8 @@ 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))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) #--------------------------------------------------------------------------------- @@ -96,6 +100,7 @@ endif #--------------------------------------------------------------------------------- export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ @@ -121,6 +126,10 @@ ifeq ($(strip $(NO_SMDH)),) export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh endif +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- @@ -180,17 +189,29 @@ $(OUTPUT).elf : $(OFILES) @echo $(notdir $<) @$(bin2o) -# WARNING: This is not the right way to do this! TODO: Do it right! #--------------------------------------------------------------------------------- -%.vsh.o : %.vsh +# rules for assembling GPU shaders #--------------------------------------------------------------------------------- +define shader-as + $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) + picasso -o $(CURBIN) $1 + 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 +endef + +%.shbin.o : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o : %.v.pica @echo $(notdir $<) - @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin - @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h - @rm ../$(notdir $<).shbin + @$(call shader-as,$<) + +%.shbin.o : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) -include $(DEPENDS) diff --git a/libs/sfillib/.gitignore b/libs/sfillib/.gitignore deleted file mode 100644 index 700b499..0000000 --- a/libs/sfillib/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -libsfil/build/ -libsfil/lib/ \ No newline at end of file diff --git a/libs/sfillib/libsfil/Makefile b/libs/sfillib/libsfil/Makefile index 3277f5c..5c82e6d 100644 --- a/libs/sfillib/libsfil/Makefile +++ b/libs/sfillib/libsfil/Makefile @@ -24,23 +24,26 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -CFLAGS := -g -Wall -O2\ - $(ARCH) +CFLAGS := -g -Wall -Werror -O2 -mword-relocations \ + -ffunction-sections -fno-strict-aliasing \ + -fomit-frame-pointer \ + $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) - #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- LIBDIRS := $(CTRULIB) $(PORTLIBS) \ $(CURDIR)/../../3ds_portlibs/build \ - $(CURDIR)/../../sf2dlib/libsf2d + $(CURDIR)/../../sf2dlib/libsf2d \ + $(CURDIR)/../../citro3d #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional diff --git a/libs/sfillib/libsfil/source/sfil_bmp.c b/libs/sfillib/libsfil/source/sfil_bmp.c index 24da96a..4e500ca 100644 --- a/libs/sfillib/libsfil/source/sfil_bmp.c +++ b/libs/sfillib/libsfil/source/sfil_bmp.c @@ -45,11 +45,19 @@ static sf2d_texture *_sfil_load_BMP_generic( sf2d_texture *texture = sf2d_create_texture(bmp_ih->biWidth, bmp_ih->biHeight, GPU_RGBA8, place); + if (!texture) + return NULL; + seek_fn(user_data, bmp_fh->bfOffBits); - int stride = texture->pow2_w * 4; + int stride = texture->tex.width * 4; void *buffer = malloc(row_size); + if (!buffer) { + sf2d_free_texture(texture); + return NULL; + } + unsigned int *tex_ptr; unsigned int color; int i, x, y; @@ -59,7 +67,7 @@ static sf2d_texture *_sfil_load_BMP_generic( read_fn(user_data, buffer, row_size); y = bmp_ih->biHeight - 1 - i; - tex_ptr = (unsigned int *)(texture->data + y*stride); + tex_ptr = (unsigned int *)(texture->tex.data + y*stride); for (x = 0; x < bmp_ih->biWidth; x++) { diff --git a/libs/sfillib/libsfil/source/sfil_jpeg.c b/libs/sfillib/libsfil/source/sfil_jpeg.c index 6b694e4..47b287f 100644 --- a/libs/sfillib/libsfil/source/sfil_jpeg.c +++ b/libs/sfillib/libsfil/source/sfil_jpeg.c @@ -4,9 +4,10 @@ #include #include - static sf2d_texture *_sfil_load_JPEG_generic(struct jpeg_decompress_struct *jinfo, struct jpeg_error_mgr *jerr, sf2d_place place) { + jpeg_start_decompress(jinfo); + int row_bytes; switch (jinfo->out_color_space) { case JCS_RGB: @@ -17,18 +18,19 @@ static sf2d_texture *_sfil_load_JPEG_generic(struct jpeg_decompress_struct *jinf } sf2d_texture *texture = sf2d_create_texture(jinfo->image_width, - jinfo->image_height, - GPU_RGBA8, place); + jinfo->image_height, GPU_RGBA8, place); + + if (!texture) + goto exit_error; JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW)); buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_bytes); unsigned int i, color, *tex_ptr; unsigned char *jpeg_ptr; - void *row_ptr = texture->data; - jpeg_start_decompress(jinfo); + void *row_ptr = texture->tex.data; - int stride = texture->pow2_w * 4; + int stride = texture->tex.width * 4; while (jinfo->output_scanline < jinfo->output_height) { jpeg_read_scanlines(jinfo, buffer, 1); @@ -41,6 +43,8 @@ static sf2d_texture *_sfil_load_JPEG_generic(struct jpeg_decompress_struct *jinf } } + jpeg_finish_decompress(jinfo); + free(buffer[0]); free(buffer); @@ -48,6 +52,7 @@ static sf2d_texture *_sfil_load_JPEG_generic(struct jpeg_decompress_struct *jinf return texture; exit_error: + jpeg_abort_decompress(jinfo); return NULL; } @@ -69,7 +74,6 @@ sf2d_texture *sfil_load_JPEG_file(const char *filename, sf2d_place place) sf2d_texture *texture = _sfil_load_JPEG_generic(&jinfo, &jerr, place); - jpeg_finish_decompress(&jinfo); jpeg_destroy_decompress(&jinfo); fclose(fp); @@ -90,7 +94,6 @@ sf2d_texture *sfil_load_JPEG_buffer(const void *buffer, unsigned long buffer_siz sf2d_texture *texture = _sfil_load_JPEG_generic(&jinfo, &jerr, place); - jpeg_finish_decompress(&jinfo); jpeg_destroy_decompress(&jinfo); return texture; diff --git a/libs/sfillib/libsfil/source/sfil_png.c b/libs/sfillib/libsfil/source/sfil_png.c index 5613a8d..9ec4f8c 100644 --- a/libs/sfillib/libsfil/source/sfil_png.c +++ b/libs/sfillib/libsfil/source/sfil_png.c @@ -23,12 +23,12 @@ static sf2d_texture *_sfil_load_PNG_generic(const void *io_ptr, png_rw_ptr read_ { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { - goto exit_error; + goto error_create_read; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { - goto exit_destroy_read; + goto error_create_info; } png_bytep *row_ptrs = NULL; @@ -37,7 +37,7 @@ static sf2d_texture *_sfil_load_PNG_generic(const void *io_ptr, png_rw_ptr read_ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); if (row_ptrs != NULL) free(row_ptrs); - goto exit_error; + return NULL; } png_set_read_fn(png_ptr, (png_voidp)io_ptr, read_data_fn); @@ -57,9 +57,17 @@ static sf2d_texture *_sfil_load_PNG_generic(const void *io_ptr, png_rw_ptr read_ png_set_expand(png_ptr); } + if (bit_depth == 16) + png_set_scale_16(png_ptr); + if (bit_depth == 8 && color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); @@ -77,12 +85,18 @@ static sf2d_texture *_sfil_load_PNG_generic(const void *io_ptr, png_rw_ptr read_ png_read_update_info(png_ptr, info_ptr); row_ptrs = (png_bytep *)malloc(sizeof(png_bytep) * height); - sf2d_texture *texture = sf2d_create_texture(width, height, GPU_RGBA8, place); + if (!row_ptrs) + goto error_alloc_rows; + + sf2d_texture *texture = sf2d_create_texture(width, height, GPU_RGBA8, place); + if (!texture) + goto error_create_tex; + + int stride = texture->tex.width * 4; - int stride = texture->pow2_w * 4; int i; for (i = 0; i < height; i++) { - row_ptrs[i] = (png_bytep)(texture->data + i*stride); + row_ptrs[i] = (png_bytep)(texture->tex.data + i*stride); } png_read_image(png_ptr, row_ptrs); @@ -93,9 +107,13 @@ static sf2d_texture *_sfil_load_PNG_generic(const void *io_ptr, png_rw_ptr read_ sf2d_texture_tile32(texture); return texture; -exit_destroy_read: +error_create_tex: + free(row_ptrs); +error_alloc_rows: + png_destroy_info_struct(png_ptr, &info_ptr); +error_create_info: png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); -exit_error: +error_create_read: return NULL; } diff --git a/libs/sfillib/sample/Makefile b/libs/sfillib/sample/Makefile index bd9a512..a1df7ae 100644 --- a/libs/sfillib/sample/Makefile +++ b/libs/sfillib/sample/Makefile @@ -52,7 +52,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lctru -lm +LIBS := -lsfil -lpng -ljpeg -lz -lsf2d -lcitro3d -lctru -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/libs/sftdlib/.gitignore b/libs/sftdlib/.gitignore deleted file mode 100644 index 18951ef..0000000 --- a/libs/sftdlib/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -libsftd/build/ -libsftd/lib/ \ No newline at end of file diff --git a/libs/sftdlib/libsftd/Makefile b/libs/sftdlib/libsftd/Makefile index d3904fd..195b167 100644 --- a/libs/sftdlib/libsftd/Makefile +++ b/libs/sftdlib/libsftd/Makefile @@ -24,13 +24,16 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -CFLAGS := -g -Wall -O2\ - $(ARCH) +CFLAGS := -g -Wall -Werror -O2 -mword-relocations \ + -ffunction-sections -fno-strict-aliasing \ + -fomit-frame-pointer \ + $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) @@ -40,7 +43,8 @@ ASFLAGS := -g $(ARCH) #--------------------------------------------------------------------------------- LIBDIRS := $(CTRULIB) $(PORTLIBS) \ $(CURDIR)/../../3ds_portlibs/build \ - $(CURDIR)/../../sf2dlib/libsf2d + $(CURDIR)/../../sf2dlib/libsf2d \ + $(CURDIR)/../../citro3d #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -90,7 +94,7 @@ all: $(BUILD) lib: @[ -d $@ ] || mkdir -p $@ - + $(BUILD): lib @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile diff --git a/libs/sftdlib/libsftd/source/texture_atlas.c b/libs/sftdlib/libsftd/source/texture_atlas.c index f8e76bc..167254b 100644 --- a/libs/sftdlib/libsftd/source/texture_atlas.c +++ b/libs/sftdlib/libsftd/source/texture_atlas.c @@ -64,7 +64,7 @@ int texture_atlas_insert(texture_atlas *atlas, unsigned int character, const voi } } - GSPGPU_FlushDataCache(atlas->tex->data, atlas->tex->data_size); + GSPGPU_FlushDataCache(atlas->tex->tex.data, atlas->tex->tex.size); return 1; } diff --git a/libs/sftdlib/sample/Makefile b/libs/sftdlib/sample/Makefile index c4e3adf..2f5cea7 100644 --- a/libs/sftdlib/sample/Makefile +++ b/libs/sftdlib/sample/Makefile @@ -39,10 +39,10 @@ APP_AUTHOR := xerpi #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft CFLAGS := -g -Wall -O2 -mword-relocations \ - -fomit-frame-pointer -ffast-math \ + -fomit-frame-pointer -ffunction-sections \ $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS @@ -52,7 +52,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lm +LIBS := -lsftd -lfreetype -lpng -lz -lsf2d -lcitro3d -lctru -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/source/ctr.c b/source/ctr.c index 62e1a38..dd34ff3 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -136,6 +136,13 @@ The `ctr.uds` module. void load_uds_lib(lua_State *L); void unload_uds_lib(lua_State *L); +/*** +The `ctr.swkbd` module. +@table swkbd +@see ctr.swkbd +*/ +void load_swkbd_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -207,6 +214,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "mic", load_mic_lib, NULL }, { "thread", load_thread_lib, NULL }, { "uds", load_uds_lib, unload_uds_lib }, + { "swkbd", load_swkbd_lib, NULL }, { NULL, NULL, NULL } }; diff --git a/source/gfx.c b/source/gfx.c index bf9dc8d..0dde5c0 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -655,26 +655,9 @@ static int gfx_target___index(lua_State *L) { luaL_getmetatable(L, "LTexture"); lua_setmetatable(L, -2); - texture->texture = &(target->target->texture); - texture->scaleX = 1.0f; - texture->scaleY = 1.0f; - texture->blendColor = 0xffffffff; + // TODO: return an usable texture return 1; - } else if (strcmp(name, "duck") == 0) { - sf2d_rendertarget *target = sf2d_create_rendertarget(64, 64); - for(int i=0;;i++) { - sf2d_clear_target(target, 0xff000000); - sf2d_start_frame_target(target); - sf2d_draw_fill_circle(i%380, i%200, 10, 0xff0000ff); - sf2d_end_frame(); - //sf2d_texture_tile32(&target->texture); - - sf2d_start_frame(GFX_TOP, GFX_LEFT); - sf2d_draw_texture(&target->texture, 10, 10); - sf2d_end_frame(); - sf2d_swapbuffers(); - } } else { for (u8 i=0;target_methods[i].name;i++) { if (strcmp(target_methods[i].name, name) == 0) { diff --git a/source/swkbd.c b/source/swkbd.c new file mode 100644 index 0000000..a4a292e --- /dev/null +++ b/source/swkbd.c @@ -0,0 +1,204 @@ +/*** +The `swkbd` module. +@module ctr.swkbd +@usage local swkbd = require("ctr.swkbd") +*/ + +#include <3ds/types.h> +#include <3ds/applets/swkbd.h> + +#include +#include + +#include +#include + +// functions + +/*** +Create a software keyboard. +@function keyboard +@tparam[opt=TYPE_NORMAL] TYPE type keyboard type. +@tparam[opt=2] integer buttons number of buttons, can be 1, 2 or 3. Will default to 2 if the number isn't 1, 2 or 3 +@tparam[opt=max] integer maxLength maximum length of the text. (maximum 65000) +@treturn keyboard a software keyboard object +*/ +static int swkbd_keyboard(lua_State *L) { + SwkbdType type = luaL_optinteger(L, 1, SWKBD_TYPE_NORMAL); + int buttons = luaL_optinteger(L, 2, 2); + int maxLength = luaL_optinteger(L, 3, -1); + + SwkbdState *keyboard = lua_newuserdata(L, sizeof(*keyboard)); + luaL_getmetatable(L, "LKeyboard"); + lua_setmetatable(L, -2); + + swkbdInit(keyboard, type, buttons, maxLength); + + return 1; +} + +// methods + +/*** +Keyboard object +@section object +*/ + +/*** +Launch the keyboard applet. +@function :launch +@tparam[opt=max] integer maxLength maximum text length (max 65000) +@treturn[1] string text (UTF-8) +@treturn[1] integer button pressed (1, 2 or 3) +@treturn[2] nil +@treturn[2] string error. In case of an "OUTOFMEM" error, this function will throw an error instead of returning. +*/ +static int keyboard_launch(lua_State *L) { + SwkbdState *keyboard = luaL_checkudata(L, 1, "LKeyboard"); + int length = luaL_optinteger(L, 2, 65000); + + char* buff = malloc(length); + + SwkbdButton button = swkbdInputText(keyboard, buff, length); + + if (button<0) { + free(buff); + lua_pushnil(L); + switch (button) { + case SWKBD_NONE: + lua_pushstring(L, "That wasn't supposed to happen."); + break; + + case SWKBD_INVALID_INPUT: + lua_pushstring(L, "Invalid parameters."); + break; + + case SWKBD_OUTOFMEM: + luaL_error(L, "Out of memory."); + break; + + default: + lua_pushfstring(L, "Unexpected error: %d.", button); + break; + } + + return 2; + } + lua_pushstring(L, buff); + free(buff); + lua_pushinteger(L, button); + + return 2; +} + +/*** +Set the initial text in the textbox. +@function :setInitialText +@tparam[opt=""] string text initial text +*/ +static int keyboard_setInitialText(lua_State *L) { + SwkbdState *keyboard = luaL_checkudata(L, 1, "LKeyboard"); + const char* text = luaL_optstring(L, 2, ""); + + swkbdSetInitialText(keyboard, text); + + return 0; +} + +/*** +Set the text displayed in the textbox when it's empty. +@function :setHintText +@tparam[opt=""] string text hint text +*/ +static int keyboard_setHintText(lua_State *L) { + SwkbdState *keyboard = luaL_checkudata(L, 1, "LKeyboard"); + const char* text = luaL_optstring(L, 2, ""); + + swkbdSetHintText(keyboard, text); + + return 0; +} + +//////////////////// + +static const struct luaL_Reg swkbd_lib[] = { + {"keyboard", swkbd_keyboard}, + {NULL, NULL} +}; + +static const struct luaL_Reg swkbd_object_methods[] = { + {"launch", keyboard_launch}, + {"setInitialText", keyboard_setInitialText}, + {"setHintText", keyboard_setHintText}, + {NULL, NULL} +}; + +/*** +Constants +@section constants +*/ + +struct { char *name; int value; } swkbd_constants[] = { + /*** + @field TYPE_NORMAL + */ + {"TYPE_NORMAL", SWKBD_TYPE_NORMAL}, + /*** + @field TYPE_QWERTY + */ + {"TYPE_QWERTY", SWKBD_TYPE_QWERTY}, + /*** + @field TYPE_NUMPAD + */ + {"TYPE_NUMPAD", SWKBD_TYPE_NUMPAD}, + /*** + @field TYPE_WESTERN + */ + {"TYPE_WESTERN", SWKBD_TYPE_WESTERN}, + /*** + @field ANYTHING + */ + {"ANYTHING", SWKBD_ANYTHING}, + /*** + @field NOTEMPTY + */ + {"NOTEMPTY", SWKBD_NOTEMPTY}, + /*** + @field NOTEMPTY_NOTBLANK + */ + {"NOTEMPTY_NOTBLANK", SWKBD_NOTEMPTY_NOTBLANK}, + /*** + @field NOTBLANK_NOTEMPTY + */ + {"NOTBLANK_NOTEMPTY", SWKBD_NOTBLANK_NOTEMPTY}, + /*** + @field NOTBLANK + */ + {"NOTBLANK", SWKBD_NOTBLANK}, + /*** + @field FIXEDLEN + */ + {"FIXEDLEN", SWKBD_FIXEDLEN}, + + {NULL, 0} +}; + +int luaopen_swkbd_lib(lua_State *L) { + luaL_newmetatable(L, "LKeyboard"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, swkbd_object_methods, 0); + + luaL_newlib(L, swkbd_lib); + + for (int i = 0; swkbd_constants[i].name; i++) { + lua_pushinteger(L, swkbd_constants[i].value); + lua_setfield(L, -2, swkbd_constants[i].name); + } + + return 1; +} + +void load_swkbd_lib(lua_State *L) { + luaL_requiref(L, "ctr.swkbd", luaopen_swkbd_lib, false); +} diff --git a/source/texture.c b/source/texture.c index d099daa..4ea3064 100644 --- a/source/texture.c +++ b/source/texture.c @@ -175,7 +175,8 @@ static int texture_drawPart(lua_State *L) { float hotspotX = luaL_optnumber(L, 9, 0.0f); float hotspotY = luaL_optnumber(L, 10, 0.0f); - sf2d_draw_texture_part_rotate_scale_hotspot_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor); + //sf2d_draw_texture_part_rotate_scale_hotspot_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor); // TODO: fix + sf2d_draw_texture_part_rotate_scale_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, texture->blendColor); return 0; } @@ -409,11 +410,6 @@ struct { char *name; int value; } texture_constants[] = { */ {"PLACE_VRAM", SF2D_PLACE_VRAM}, /*** - Constant used to select a temporary RAM pool. Don't use it. - @field PLACE_TEMP - */ - {"PLACE_TEMP", SF2D_PLACE_TEMP}, - /*** Constant used to select the PNG type. @field TYPE_PNG */ diff --git a/source/thread.c b/source/thread.c index c949e58..88dde8c 100644 --- a/source/thread.c +++ b/source/thread.c @@ -22,6 +22,8 @@ typedef struct { Thread thread; const char *code; char *error; + void* pool; + int poolSize; // in bytes } thread_userdata; void entryPoint(void *thread) {