1
0
Fork 0
mirror of https://github.com/ctruLua/ctruLua.git synced 2025-10-28 16:59:30 +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

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