1
0
Fork 0
mirror of https://github.com/ctruLua/ctruLua.git synced 2025-10-27 16:39:29 +00:00

Updated all the libs, added citro3d, added ctr.swkbd (WIP, untested)

This commit is contained in:
Firew0lf 2016-08-05 17:30:24 +02:00
parent 68a44645f7
commit 49c87e5526
97 changed files with 7341 additions and 944 deletions

View file

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

10
libs/citro3d/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
*~
*.3dsx
*.elf
*.exe
*.smdh
*.tar.bz2
Thumbs.db
build/
lib/
bin/

141
libs/citro3d/Makefile Normal file
View file

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

28
libs/citro3d/README.md Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,23 @@
#pragma once
#include "maths.h"
#define C3D_MTXSTACK_SIZE 8
typedef struct
{
C3D_Mtx m[C3D_MTXSTACK_SIZE];
int pos;
u8 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);

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,32 @@
#pragma once
#ifdef _3DS
#include <3ds.h>
#else
#include <stdbool.h>
#include <stdint.h>
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);
}

View file

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

View file

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

View file

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

365
libs/citro3d/source/base.c Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

157
libs/citro3d/source/light.c Normal file
View file

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

View file

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

View file

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

2
libs/citro3d/source/maths/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.d
*.o

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,12 @@
#include <c3d/maths.h>
void Mtx_Scale(C3D_Mtx* mtx, float x, float y, float z)
{
int i;
for (i = 0; i < 4; ++i)
{
mtx->r[i].x *= x;
mtx->r[i].y *= y;
mtx->r[i].z *= z;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,43 @@
#include <c3d/mtxstack.h>
#include <c3d/uniforms.h>
void MtxStack_Init(C3D_MtxStack* stk)
{
stk->pos = 0;
stk->unifPos = 0xFF;
stk->isDirty = true;
Mtx_Identity(&stk->m[0]);
}
void MtxStack_Bind(C3D_MtxStack* stk, 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;
}

View file

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

View file

@ -0,0 +1,404 @@
#include "context.h"
#include <c3d/renderqueue.h>
#include <string.h>
#include <stdlib.h>
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);
}

View file

@ -0,0 +1,81 @@
#include <c3d/texenv.h>
#include <string.h>
#include "context.h"
void TexEnv_Init(C3D_TexEnv* env)
{
env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0);
env->srcAlpha = env->srcRgb;
env->opRgb = GPU_TEVOPERANDS(0,0,0);
env->opAlpha = env->opRgb;
env->funcRgb = GPU_REPLACE;
env->funcAlpha = env->funcRgb;
env->color = 0xFFFFFFFF;
env->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;
}

View file

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

View file

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

View file

@ -0,0 +1,249 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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):
# - <Project name>.png
# - icon.png
# - <libctru folder>/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
#---------------------------------------------------------------------------------------

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

View file

@ -0,0 +1,927 @@
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <3ds.h>
#include <citro3d.h>
#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<class T>
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<size_t>(rc) > size)
break;
size -= rc;
p = reinterpret_cast<char*>(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;
}

View file

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

5
libs/citro3d/test/pc/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.d
*.o
test
coverage.info
lcov/

View file

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

View file

@ -0,0 +1,939 @@
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <random>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
extern "C" {
#include <c3d/maths.h>
}
typedef std::default_random_engine generator_t;
typedef std::uniform_real_distribution<float> 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<float>(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<int>(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;
}

View file

@ -2,6 +2,13 @@
*.o
*.project
*.cproject
libsf2d/.settings/*
libsf2d/build/*
libsf2d/lib/*
*.3dsx
*.cia
*.cxi
*.ncch
*.3ds
*.elf
*.smdh
build/
lib/
.settings/

View file

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

View file

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

View file

@ -4,10 +4,9 @@
* @date 22 March 2015
* @brief sf2dlib header
*/
#ifndef SF2D_H
#define SF2D_H
#pragma once
#include <3ds.h>
#include <citro3d.h>
#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

View file

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

View file

@ -1,22 +1,13 @@
#include <string.h>
#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();
}

View file

@ -1,5 +1,4 @@
#include "sf2d.h"
#include "sf2d_private.h"
#include <math.h>
#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);
}

View file

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

View file

@ -2,7 +2,6 @@
#include <string.h>
#include <math.h>
#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;

View file

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

View file

@ -1,2 +0,0 @@
libsfil/build/
libsfil/lib/

View file

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

View file

@ -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++) {

View file

@ -4,9 +4,10 @@
#include <stdlib.h>
#include <jpeglib.h>
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;

View file

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

View file

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

View file

@ -1,2 +0,0 @@
libsftd/build/
libsftd/lib/

View file

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

View file

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

View file

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

View file

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

View file

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

204
source/swkbd.c Normal file
View file

@ -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 <lapi.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <malloc.h>
// 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);
}

View file

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

View file

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