Compare commits

...

6 commits

Author SHA1 Message Date
zy
950a8b9251 add things to README.md 2025-07-30 20:34:07 +02:00
zy
5a116fa904 s 2024-12-29 19:19:38 +01:00
Zy
714d9a8187 add: main tester 2024-10-12 17:23:10 +02:00
Zy
8e9de2f977 dev: add /*.lst to .gitignore 2024-10-12 17:22:51 +02:00
Zy
6b216eae93 dev: fix check_headers rule 2024-10-12 17:22:21 +02:00
Zy
120dcc6391 dev: add check_headers rule in the Makefile 2024-10-05 14:52:52 +02:00
28 changed files with 3269 additions and 3 deletions

2
.gitignore vendored
View file

@ -42,3 +42,5 @@ output/
# Debug files
*.dSYM/
/*.lst

View file

@ -343,3 +343,28 @@ endef
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
# Added by Zy
# To check if the headers are protected and if they include everything they need.
check_headers :
@ERROR=0; \
for HEADER in $(wildcard *.h) $(wildcard **/*.h) $(wildcard *.hpp) $(wildcard **/*.hpp); \
do \
echo "check header $$HEADER..."; \
> __tmp_check_header.cpp echo "#include \"$$HEADER\""; \
>> __tmp_check_header.cpp echo "#include \"$$HEADER\""; \
>> __tmp_check_header.cpp echo "int main(void) {}"; \
$(CPP) $(INCLUDE) $(COMMON) $(LIBS) -o __tmp_check_header.out -c __tmp_check_header.cpp; \
if [ $$? -ne 0 ]; \
then \
ERROR=1; \
/bin/echo " ### error :( ###"; \
else \
/bin/echo " ### good :) ###"; \
fi; \
2> /dev/null rm -- "__tmp_check_header.out" "__tmp_check_header.c"; \
done; \
if [ $$ERROR -eq 0 ]; then true; else false; fi;
.PHONY : check_headers

View file

@ -1,4 +1,6 @@
template from https://github.com/TricksterGuy/3ds-template
so this is just me trying to see how you can develop things on the nintendo 3ds. have fun trying it ig
this repo is based on the template https://github.com/TricksterGuy/3ds-template
Files:
- LICENSE.txt apparently i must not remove it lol

511
include/c2d/base.h Normal file
View file

@ -0,0 +1,511 @@
/**
* @file base.h
* @brief Basic citro2d initialization and drawing API
*/
#pragma once
#include <citro3d.h>
#include <tex3ds.h>
#define C2D_DEFAULT_MAX_OBJECTS 4096
#ifdef __cplusplus
#define C2D_CONSTEXPR constexpr
#define C2D_OPTIONAL(_x) =_x
#else
#define C2D_CONSTEXPR static inline
#define C2D_OPTIONAL(_x)
#endif
typedef struct
{
struct
{
float x, y, w, h;
} pos;
struct
{
float x, y;
} center;
float depth;
float angle;
} C2D_DrawParams;
typedef enum
{
C2D_TintSolid, ///< Plain solid tint color
C2D_TintMult, ///< Tint color multiplied by texture color
C2D_TintLuma, ///< Tint color multiplied by grayscale converted texture color
} C2D_TintMode;
typedef struct
{
u32 color; ///< RGB tint color and Alpha transparency
float blend; ///< Blending strength of the tint color (0.0~1.0)
} C2D_Tint;
typedef enum
{
C2D_TopLeft, ///< Top left corner
C2D_TopRight, ///< Top right corner
C2D_BotLeft, ///< Bottom left corner
C2D_BotRight, ///< Bottom right corner
} C2D_Corner;
typedef struct
{
C3D_Tex* tex;
const Tex3DS_SubTexture* subtex;
} C2D_Image;
typedef struct
{
C2D_Tint corners[4];
} C2D_ImageTint;
/** @defgroup Helper Helper functions
* @{
*/
/** @brief Clamps a value between bounds
* @param[in] x The value to clamp
* @param[in] min The lower bound
* @param[in] max The upper bound
* @returns The clamped value
*/
C2D_CONSTEXPR float C2D_Clamp(float x, float min, float max)
{
return x <= min ? min : x >= max ? max : x;
}
/** @brief Converts a float to u8
* @param[in] x Input value (0.0~1.0)
* @returns Output value (0~255)
*/
C2D_CONSTEXPR u8 C2D_FloatToU8(float x)
{
return (u8)(255.0f*C2D_Clamp(x, 0.0f, 1.0f)+0.5f);
}
/** @brief Builds a 32-bit RGBA color value
* @param[in] r Red component (0~255)
* @param[in] g Green component (0~255)
* @param[in] b Blue component (0~255)
* @param[in] a Alpha component (0~255)
* @returns The 32-bit RGBA color value
*/
C2D_CONSTEXPR u32 C2D_Color32(u8 r, u8 g, u8 b, u8 a)
{
return r | (g << (u32)8) | (b << (u32)16) | (a << (u32)24);
}
/** @brief Builds a 32-bit RGBA color value from float values
* @param[in] r Red component (0.0~1.0)
* @param[in] g Green component (0.0~1.0)
* @param[in] b Blue component (0.0~1.0)
* @param[in] a Alpha component (0.0~1.0)
* @returns The 32-bit RGBA color value
*/
C2D_CONSTEXPR u32 C2D_Color32f(float r, float g, float b, float a)
{
return C2D_Color32(C2D_FloatToU8(r),C2D_FloatToU8(g),C2D_FloatToU8(b),C2D_FloatToU8(a));
}
/** @brief Configures one corner of an image tint structure
* @param[in] tint Image tint structure
* @param[in] corner The corner of the image to tint
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_SetImageTint(C2D_ImageTint* tint, C2D_Corner corner, u32 color, float blend)
{
tint->corners[corner].color = color;
tint->corners[corner].blend = blend;
}
/** @brief Configures an image tint structure with the specified tint parameters applied to all corners
* @param[in] tint Image tint structure
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_PlainImageTint(C2D_ImageTint* tint, u32 color, float blend)
{
C2D_SetImageTint(tint, C2D_TopLeft, color, blend);
C2D_SetImageTint(tint, C2D_TopRight, color, blend);
C2D_SetImageTint(tint, C2D_BotLeft, color, blend);
C2D_SetImageTint(tint, C2D_BotRight, color, blend);
}
/** @brief Configures an image tint structure to just apply transparency to the image
* @param[in] tint Image tint structure
* @param[in] alpha Alpha transparency value to apply to the image
*/
static inline void C2D_AlphaImageTint(C2D_ImageTint* tint, float alpha)
{
C2D_PlainImageTint(tint, C2D_Color32f(0.0f, 0.0f, 0.0f, alpha), 0.0f);
}
/** @brief Configures an image tint structure with the specified tint parameters applied to the top side (e.g. for gradients)
* @param[in] tint Image tint structure
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_TopImageTint(C2D_ImageTint* tint, u32 color, float blend)
{
C2D_SetImageTint(tint, C2D_TopLeft, color, blend);
C2D_SetImageTint(tint, C2D_TopRight, color, blend);
}
/** @brief Configures an image tint structure with the specified tint parameters applied to the bottom side (e.g. for gradients)
* @param[in] tint Image tint structure
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_BottomImageTint(C2D_ImageTint* tint, u32 color, float blend)
{
C2D_SetImageTint(tint, C2D_BotLeft, color, blend);
C2D_SetImageTint(tint, C2D_BotRight, color, blend);
}
/** @brief Configures an image tint structure with the specified tint parameters applied to the left side (e.g. for gradients)
* @param[in] tint Image tint structure
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_LeftImageTint(C2D_ImageTint* tint, u32 color, float blend)
{
C2D_SetImageTint(tint, C2D_TopLeft, color, blend);
C2D_SetImageTint(tint, C2D_BotLeft, color, blend);
}
/** @brief Configures an image tint structure with the specified tint parameters applied to the right side (e.g. for gradients)
* @param[in] tint Image tint structure
* @param[in] color RGB tint color and Alpha transparency
* @param[in] blend Blending strength of the tint color (0.0~1.0)
*/
static inline void C2D_RightImageTint(C2D_ImageTint* tint, u32 color, float blend)
{
C2D_SetImageTint(tint, C2D_TopRight, color, blend);
C2D_SetImageTint(tint, C2D_BotRight, color, blend);
}
/** @} */
/** @defgroup Base Basic functions
* @{
*/
/** @brief Initialize citro2d
* @param[in] maxObjects Maximum number of 2D objects that can be drawn per frame.
* @remarks Pass C2D_DEFAULT_MAX_OBJECTS as a starting point.
* @returns true on success, false on failure
*/
bool C2D_Init(size_t maxObjects);
/** @brief Deinitialize citro2d */
void C2D_Fini(void);
/** @brief Prepares the GPU for rendering 2D content
* @remarks This needs to be done only once in the program if citro2d is the sole user of the GPU.
*/
void C2D_Prepare(void);
/** @brief Ensures all 2D objects so far have been drawn */
void C2D_Flush(void);
/** @brief Configures the size of the 2D scene.
* @param[in] width The width of the scene, in pixels.
* @param[in] height The height of the scene, in pixels.
* @param[in] tilt Whether the scene is tilted like the 3DS's sideways screens.
*/
void C2D_SceneSize(u32 width, u32 height, bool tilt);
/** @brief Configures the size of the 2D scene to match that of the specified render target.
* @param[in] target Render target
*/
static inline void C2D_SceneTarget(C3D_RenderTarget* target)
{
C2D_SceneSize(target->frameBuf.width, target->frameBuf.height, target->linked);
}
/** @brief Resets the model transformation matrix. */
void C2D_ViewReset(void);
/** @brief Saves the current model transformation matrix.
* @param[out] matrix Pointer to save the current matrix to
*/
void C2D_ViewSave(C3D_Mtx* matrix);
/** @brief Restores a previously saved model transformation matrix.
* @param[in] matrix Pointer to matrix to restor
*/
void C2D_ViewRestore(const C3D_Mtx* matrix);
/** @brief Translates everything drawn via the model matrix.
* @param[in] x Translation in the x direction
* @param[in] y Translation in the y direction
*/
void C2D_ViewTranslate(float x, float y);
/** @brief Rotates everything drawn via the model matrix.
* @param[in] rotation Rotation in the counterclockwise direction in radians
*/
void C2D_ViewRotate(float rotation);
/** @brief Rotates everything drawn via the model matrix.
* @param[in] rotation Rotation in the counterclockwise direction in degrees
*/
static inline void C2D_ViewRotateDegrees(float rotation)
{
C2D_ViewRotate(C3D_AngleFromDegrees(rotation));
}
/** @brief Shears everything drawn via the model matrix.
* @param[in] x Shear factor in the x direction
* @param[in] y Shear factor in the y direction
*/
void C2D_ViewShear(float x, float y);
/** @brief Scales everything drawn via the model matrix.
* @param[in] x Scale factor in the x direction
* @param[in] y Scale factor in the y direction
*/
void C2D_ViewScale(float x, float y);
/** @brief Helper function to create a render target for a screen
* @param[in] screen Screen (GFX_TOP or GFX_BOTTOM)
* @param[in] side Side (GFX_LEFT or GFX_RIGHT)
* @returns citro3d render target object
*/
C3D_RenderTarget* C2D_CreateScreenTarget(gfxScreen_t screen, gfx3dSide_t side);
/** @brief Helper function to clear a rendertarget using the specified color
* @param[in] target Render target to clear
* @param[in] color 32-bit RGBA color value to fill the target with
*/
void C2D_TargetClear(C3D_RenderTarget* target, u32 color);
/** @brief Helper function to begin drawing a 2D scene on a render target
* @param[in] target Render target to draw the 2D scene to
*/
static inline void C2D_SceneBegin(C3D_RenderTarget* target)
{
C2D_Flush();
C3D_FrameDrawOn(target);
C2D_SceneTarget(target);
}
/** @} */
/** @defgroup Env Drawing environment functions
* @{
*/
/** @brief Configures the fading color
* @param[in] color 32-bit RGBA color value to be used as the fading color (0 by default)
* @remark The alpha component of the color is used as the strength of the fading color.
* If alpha is zero, the fading color has no effect. If it is the highest value,
* the rendered pixels will all have the fading color. Everything inbetween is
* rendered as a blend of the original pixel color and the fading color.
*/
bool C2D_Fade(u32 color);
/** @brief Configures the formula used to calculate the tinted texture color
* @param[in] mode Tinting mode
* @remark Texture tinting works by linearly interpolating between the regular texture color
* and the tinted texture color according to the blending strength parameter.
* This function can be used to change how the tinted texture color is precisely
* calculated, refer to \ref C2D_TintMode for a list of available tinting modes.
*/
bool C2D_SetTintMode(C2D_TintMode mode);
/** @} */
/** @defgroup Drawing Drawing functions
* @{
*/
/** @brief Draws an image using the GPU (variant accepting C2D_DrawParams)
* @param[in] img Handle of the image to draw
* @param[in] params Parameters with which to draw the image
* @param[in] tint Tint parameters to apply to the image (optional, can be null)
* @returns true on success, false on failure
*/
bool C2D_DrawImage(C2D_Image img, const C2D_DrawParams* params, const C2D_ImageTint* tint C2D_OPTIONAL(nullptr));
/** @brief Draws an image using the GPU (variant accepting position/scaling)
* @param[in] img Handle of the image to draw
* @param[in] x X coordinate at which to place the top left corner of the image
* @param[in] y Y coordinate at which to place the top left corner of the image
* @param[in] depth Depth value to draw the image with
* @param[in] tint Tint parameters to apply to the image (optional, can be null)
* @param[in] scaleX Horizontal scaling factor to apply to the image (optional, by default 1.0f); negative values apply a horizontal flip
* @param[in] scaleY Vertical scaling factor to apply to the image (optional, by default 1.0f); negative values apply a vertical flip
*/
static inline bool C2D_DrawImageAt(C2D_Image img, float x, float y, float depth,
const C2D_ImageTint* tint C2D_OPTIONAL(nullptr),
float scaleX C2D_OPTIONAL(1.0f), float scaleY C2D_OPTIONAL(1.0f))
{
C2D_DrawParams params =
{
{ x, y, scaleX*img.subtex->width, scaleY*img.subtex->height },
{ 0.0f, 0.0f },
depth, 0.0f
};
return C2D_DrawImage(img, &params, tint);
}
/** @brief Draws an image using the GPU (variant accepting position/scaling/rotation)
* @param[in] img Handle of the image to draw
* @param[in] x X coordinate at which to place the center of the image
* @param[in] y Y coordinate at which to place the center of the image
* @param[in] depth Depth value to draw the image with
* @param[in] angle Angle (in radians) to rotate the image by, counter-clockwise
* @param[in] tint Tint parameters to apply to the image (optional, can be null)
* @param[in] scaleX Horizontal scaling factor to apply to the image (optional, by default 1.0f); negative values apply a horizontal flip
* @param[in] scaleY Vertical scaling factor to apply to the image (optional, by default 1.0f); negative values apply a vertical flip
*/
static inline bool C2D_DrawImageAtRotated(C2D_Image img, float x, float y, float depth, float angle,
const C2D_ImageTint* tint C2D_OPTIONAL(nullptr),
float scaleX C2D_OPTIONAL(1.0f), float scaleY C2D_OPTIONAL(1.0f))
{
C2D_DrawParams params =
{
{ x, y, scaleX*img.subtex->width, scaleY*img.subtex->height },
{ (scaleX*img.subtex->width)/2.0f, (scaleY*img.subtex->height)/2.0f },
depth, angle
};
return C2D_DrawImage(img, &params, tint);
}
/** @brief Draws a plain triangle using the GPU
* @param[in] x0 X coordinate of the first vertex of the triangle
* @param[in] y0 Y coordinate of the first vertex of the triangle
* @param[in] clr0 32-bit RGBA color of the first vertex of the triangle
* @param[in] x1 X coordinate of the second vertex of the triangle
* @param[in] y1 Y coordinate of the second vertex of the triangle
* @param[in] clr1 32-bit RGBA color of the second vertex of the triangle
* @param[in] x2 X coordinate of the third vertex of the triangle
* @param[in] y2 Y coordinate of the third vertex of the triangle
* @param[in] clr2 32-bit RGBA color of the third vertex of the triangle
* @param[in] depth Depth value to draw the triangle with
*/
bool C2D_DrawTriangle(
float x0, float y0, u32 clr0,
float x1, float y1, u32 clr1,
float x2, float y2, u32 clr2,
float depth);
/** @brief Draws a plain line using the GPU
* @param[in] x0 X coordinate of the first vertex of the line
* @param[in] y0 Y coordinate of the first vertex of the line
* @param[in] clr0 32-bit RGBA color of the first vertex of the line
* @param[in] x1 X coordinate of the second vertex of the line
* @param[in] y1 Y coordinate of the second vertex of the line
* @param[in] clr1 32-bit RGBA color of the second vertex of the line
* @param[in] thickness Thickness, in pixels, of the line
* @param[in] depth Depth value to draw the line with
*/
bool C2D_DrawLine(
float x0, float y0, u32 clr0,
float x1, float y1, u32 clr1,
float thickness, float depth);
/** @brief Draws a plain rectangle using the GPU
* @param[in] x X coordinate of the top-left vertex of the rectangle
* @param[in] y Y coordinate of the top-left vertex of the rectangle
* @param[in] z Z coordinate (depth value) to draw the rectangle with
* @param[in] w Width of the rectangle
* @param[in] h Height of the rectangle
* @param[in] clr0 32-bit RGBA color of the top-left corner of the rectangle
* @param[in] clr1 32-bit RGBA color of the top-right corner of the rectangle
* @param[in] clr2 32-bit RGBA color of the bottom-left corner of the rectangle
* @param[in] clr3 32-bit RGBA color of the bottom-right corner of the rectangle
*/
bool C2D_DrawRectangle(
float x, float y, float z, float w, float h,
u32 clr0, u32 clr1, u32 clr2, u32 clr3);
/** @brief Draws a plain rectangle using the GPU (with a solid color)
* @param[in] x X coordinate of the top-left vertex of the rectangle
* @param[in] y Y coordinate of the top-left vertex of the rectangle
* @param[in] z Z coordinate (depth value) to draw the rectangle with
* @param[in] w Width of the rectangle
* @param[in] h Height of the rectangle
* @param[in] clr 32-bit RGBA color of the rectangle
*/
static inline bool C2D_DrawRectSolid(
float x, float y, float z, float w, float h,
u32 clr)
{
return C2D_DrawRectangle(x,y,z,w,h,clr,clr,clr,clr);
}
/** @brief Draws an ellipse using the GPU
* @param[in] x X coordinate of the top-left vertex of the ellipse
* @param[in] y Y coordinate of the top-left vertex of the ellipse
* @param[in] z Z coordinate (depth value) to draw the ellipse with
* @param[in] w Width of the ellipse
* @param[in] h Height of the ellipse
* @param[in] clr0 32-bit RGBA color of the top-left corner of the ellipse
* @param[in] clr1 32-bit RGBA color of the top-right corner of the ellipse
* @param[in] clr2 32-bit RGBA color of the bottom-left corner of the ellipse
* @param[in] clr3 32-bit RGBA color of the bottom-right corner of the ellipse
* @note Switching to and from "circle mode" internally requires an expensive state change. As such, the recommended usage of this feature is to draw all non-circular objects first, then draw all circular objects.
*/
bool C2D_DrawEllipse(
float x, float y, float z, float w, float h,
u32 clr0, u32 clr1, u32 clr2, u32 clr3);
/** @brief Draws a ellipse using the GPU (with a solid color)
* @param[in] x X coordinate of the top-left vertex of the ellipse
* @param[in] y Y coordinate of the top-left vertex of the ellipse
* @param[in] z Z coordinate (depth value) to draw the ellipse with
* @param[in] w Width of the ellipse
* @param[in] h Height of the ellipse
* @param[in] clr 32-bit RGBA color of the ellipse
* @note Switching to and from "circle mode" internally requires an expensive state change. As such, the recommended usage of this feature is to draw all non-circular objects first, then draw all circular objects.
*/
static inline bool C2D_DrawEllipseSolid(
float x, float y, float z, float w, float h,
u32 clr)
{
return C2D_DrawEllipse(x,y,z,w,h,clr,clr,clr,clr);
}
/** @brief Draws a circle (an ellipse with identical width and height) using the GPU
* @param[in] x X coordinate of the center of the circle
* @param[in] y Y coordinate of the center of the circle
* @param[in] z Z coordinate (depth value) to draw the ellipse with
* @param[in] radius Radius of the circle
* @param[in] clr0 32-bit RGBA color of the top-left corner of the ellipse
* @param[in] clr1 32-bit RGBA color of the top-right corner of the ellipse
* @param[in] clr2 32-bit RGBA color of the bottom-left corner of the ellipse
* @param[in] clr3 32-bit RGBA color of the bottom-right corner of the ellipse
* @note Switching to and from "circle mode" internally requires an expensive state change. As such, the recommended usage of this feature is to draw all non-circular objects first, then draw all circular objects.
*/
static inline bool C2D_DrawCircle(
float x, float y, float z, float radius,
u32 clr0, u32 clr1, u32 clr2, u32 clr3)
{
return C2D_DrawEllipse(
x - radius,y - radius,z,radius*2,radius*2,
clr0,clr1,clr2,clr3);
}
/** @brief Draws a circle (an ellipse with identical width and height) using the GPU (with a solid color)
* @param[in] x X coordinate of the center of the circle
* @param[in] y Y coordinate of the center of the circle
* @param[in] z Z coordinate (depth value) to draw the ellipse with
* @param[in] radius Radius of the circle
* @param[in] clr 32-bit RGBA color of the ellipse
* @note Switching to and from "circle mode" internally requires an expensive state change. As such, the recommended usage of this feature is to draw all non-circular objects first, then draw all circular objects.
*/
static inline bool C2D_DrawCircleSolid(
float x, float y, float z, float radius,
u32 clr)
{
return C2D_DrawCircle(x,y,z,radius,clr,clr,clr,clr);
}
/** @} */

95
include/c2d/font.h Normal file
View file

@ -0,0 +1,95 @@
/**
* @file font.h
* @brief Font loading and management
*/
#pragma once
#include "base.h"
struct C2D_Font_s;
typedef struct C2D_Font_s* C2D_Font;
/** @defgroup Font Font functions
* @{
*/
/** @brief Load a font from a file
* @param[in] filename Name of the font file (.bcfnt)
* @returns Font handle
* @retval NULL Error
*/
C2D_Font C2D_FontLoad(const char* filename);
/** @brief Load a font from memory
* @param[in] data Data to load
* @param[in] size Size of the data to load
* @returns Font handle
* @retval NULL Error
*/
C2D_Font C2D_FontLoadFromMem(const void* data, size_t size);
/** @brief Load a font from file descriptor
* @param[in] fd File descriptor used to load data
* @returns Font handle
* @retval NULL Error
*/
C2D_Font C2D_FontLoadFromFD(int fd);
/** @brief Load font from stdio file handle
* @param[in] f File handle used to load data
* @returns Font handle
* @retval NULL Error
*/
C2D_Font C2D_FontLoadFromHandle(FILE* f);
/** @brief Load corresponding font from system archive
* @param[in] region Region to get font from
* @returns Font handle
* @retval NULL Error
* @remark JPN, USA, EUR, and AUS all use the same font.
*/
C2D_Font C2D_FontLoadSystem(CFG_Region region);
/** @brief Free a font
* @param[in] font Font handle
*/
void C2D_FontFree(C2D_Font font);
/** @brief Set a font's texture filter
* @param[in] font Font handle
* @param[in] magFilter the magnification filter
* @param[in] minFilter the minification filter
*/
void C2D_FontSetFilter(C2D_Font font, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter);
/** @brief Find the glyph index of a codepoint, or returns the default
* @param[in] font Font to search, or NULL for system font
* @param[in] codepoint Codepoint to search for
* @returns Glyph index
* @retval font->cfnt->finf.alterCharIndex The codepoint does not exist in the font
*/
int C2D_FontGlyphIndexFromCodePoint(C2D_Font font, u32 codepoint);
/** @brief Get character width info for a given index
* @param[in] font Font to read from, or NULL for system font
* @param[in] glyphIndex Index to get the width of
* @returns Width info for glyph
*/
charWidthInfo_s* C2D_FontGetCharWidthInfo(C2D_Font font, int glyphIndex);
/** @brief Calculate glyph position of given index
* @param[in] font Font to read from, or NULL for system font
* @param[out] out Glyph position
* @param[in] glyphIndex Index to get position of
* @param[in] flags Misc flags
* @param[in] scaleX Size to scale in X
* @param[in] scaleY Size to scale in Y
*/
void C2D_FontCalcGlyphPos(C2D_Font font, fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY);
/** @brief Get the font info structure associated with the font
* @param[in] font Font to read from, or NULL for the system font
* @returns FINF associated with the font
*/
FINF_s* C2D_FontGetInfo(C2D_Font font);
/** @} */

179
include/c2d/sprite.h Normal file
View file

@ -0,0 +1,179 @@
/**
* @file sprite.h
* @brief Stateful sprite API
*/
#pragma once
#include "spritesheet.h"
typedef struct
{
C2D_Image image;
C2D_DrawParams params;
} C2D_Sprite;
/** @defgroup Sprite Sprite functions
* @{
*/
/** @brief Initializes a sprite from an image
* @param[in] Pointer to sprite
* @param[in] image Image to use
*/
static inline void C2D_SpriteFromImage(C2D_Sprite* sprite, C2D_Image image)
{
sprite->image = image;
sprite->params.pos.x = 0.0f;
sprite->params.pos.y = 0.0f;
sprite->params.pos.w = image.subtex->width;
sprite->params.pos.h = image.subtex->height;
sprite->params.center.x = 0.0f;
sprite->params.center.y = 0.0f;
sprite->params.angle = 0.0f;
sprite->params.depth = 0.0f;
}
/** @brief Initializes a sprite from an image stored in a sprite sheet
* @param[in] Pointer to sprite
* @param[in] sheet Sprite sheet handle
* @param[in] index Index of the image inside the sprite sheet
*/
static inline void C2D_SpriteFromSheet(C2D_Sprite* sprite, C2D_SpriteSheet sheet, size_t index)
{
C2D_SpriteFromImage(sprite, C2D_SpriteSheetGetImage(sheet, index));
}
/** @brief Scale sprite (relative)
* @param[in] sprite Pointer to sprite
* @param[in] x X scale (negative values flip the sprite horizontally)
* @param[in] y Y scale (negative values flip the sprite vertically)
*/
static inline void C2D_SpriteScale(C2D_Sprite* sprite, float x, float y)
{
sprite->params.pos.w *= x;
sprite->params.pos.h *= y;
sprite->params.center.x *= x;
sprite->params.center.y *= y;
}
/** @brief Rotate sprite (relative)
* @param[in] sprite Pointer to sprite
* @param[in] radians Amount to rotate in radians
*/
static inline void C2D_SpriteRotate(C2D_Sprite* sprite, float radians)
{
sprite->params.angle += radians;
}
/** @brief Rotate sprite (relative)
* @param[in] sprite Pointer to sprite
* @param[in] degrees Amount to rotate in degrees
*/
static inline void C2D_SpriteRotateDegrees(C2D_Sprite* sprite, float degrees)
{
C2D_SpriteRotate(sprite, C3D_AngleFromDegrees(degrees));
}
/** @brief Move sprite (relative)
* @param[in] sprite Pointer to sprite
* @param[in] x X translation
* @param[in] y Y translation
*/
static inline void C2D_SpriteMove(C2D_Sprite* sprite, float x, float y)
{
sprite->params.pos.x += x;
sprite->params.pos.y += y;
}
/** @brief Scale sprite (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] x X scale (negative values flip the sprite horizontally)
* @param[in] y Y scale (negative values flip the sprite vertically)
*/
static inline void C2D_SpriteSetScale(C2D_Sprite* sprite, float x, float y)
{
float oldCenterX = sprite->params.center.x / sprite->params.pos.w;
float oldCenterY = sprite->params.center.y / sprite->params.pos.h;
sprite->params.pos.w = x*sprite->image.subtex->width;
sprite->params.pos.h = y*sprite->image.subtex->height;
sprite->params.center.x = fabsf(oldCenterX*sprite->params.pos.w);
sprite->params.center.y = fabsf(oldCenterY*sprite->params.pos.h);
}
/** @brief Rotate sprite (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] radians Amount to rotate in radians
*/
static inline void C2D_SpriteSetRotation(C2D_Sprite* sprite, float radians)
{
sprite->params.angle = radians;
}
/** @brief Rotate sprite (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] degrees Amount to rotate in degrees
*/
static inline void C2D_SpriteSetRotationDegrees(C2D_Sprite* sprite, float degrees)
{
C2D_SpriteSetRotation(sprite, C3D_AngleFromDegrees(degrees));
}
/** @brief Set the center of a sprite in values independent of the sprite size (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] x X position of the center (0.0 through 1.0)
* @param[in] y Y position of the center (0.0 through 1.0)
*/
static inline void C2D_SpriteSetCenter(C2D_Sprite* sprite, float x, float y)
{
sprite->params.center.x = x*sprite->params.pos.w;
sprite->params.center.y = y*sprite->params.pos.h;
}
/** @brief Set the center of a sprite in terms of pixels (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] x X position of the center (in pixels)
* @param[in] y Y position of the center (in pixels)
*/
static inline void C2D_SpriteSetCenterRaw(C2D_Sprite* sprite, float x, float y)
{
sprite->params.center.x = x;
sprite->params.center.y = y;
}
/** @brief Move sprite (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] x X position
* @param[in] y Y position
*/
static inline void C2D_SpriteSetPos(C2D_Sprite* sprite, float x, float y)
{
sprite->params.pos.x = x;
sprite->params.pos.y = y;
}
/** @brief Sets the depth level of a sprite (absolute)
* @param[in] sprite Pointer to sprite
* @param[in] depth Depth value
*/
static inline void C2D_SpriteSetDepth(C2D_Sprite* sprite, float depth)
{
sprite->params.depth = depth;
}
/** @brief Draw sprite
* @param[in] sprite Sprite to draw
*/
static inline bool C2D_DrawSprite(const C2D_Sprite* sprite)
{
return C2D_DrawImage(sprite->image, &sprite->params, NULL);
}
/** @brief Draw sprite with color tinting
* @param[in] sprite Sprite to draw
* @param[in] tint Color tinting parameters to apply to the sprite
*/
static inline bool C2D_DrawSpriteTinted(const C2D_Sprite* sprite, const C2D_ImageTint* tint)
{
return C2D_DrawImage(sprite->image, &sprite->params, tint);
}
/** @} */

62
include/c2d/spritesheet.h Normal file
View file

@ -0,0 +1,62 @@
/**
* @file spritesheet.h
* @brief Spritesheet (texture atlas) loading and management
*/
#pragma once
#include "base.h"
struct C2D_SpriteSheet_s;
typedef struct C2D_SpriteSheet_s* C2D_SpriteSheet;
/** @defgroup SpriteSheet Sprite sheet functions
* @{
*/
/** @brief Load a sprite sheet from file
* @param[in] filename Name of the sprite sheet file (.t3x)
* @returns Sprite sheet handle
* @retval NULL Error
*/
C2D_SpriteSheet C2D_SpriteSheetLoad(const char* filename);
/** @brief Load a sprite sheet from memory
* @param[in] data Data to load
* @param[in] size Size of the data to load
* @returns Sprite sheet handle
* @retval NULL Error
*/
C2D_SpriteSheet C2D_SpriteSheetLoadFromMem(const void* data, size_t size);
/** @brief Load sprite sheet from file descriptor
* @param[in] fd File descriptor used to load data
* @returns Sprite sheet handle
* @retval NULL Error
*/
C2D_SpriteSheet C2D_SpriteSheetFromFD(int fd);
/** @brief Load sprite sheet from stdio file handle
* @param[in] f File handle used to load data
* @returns Sprite sheet handle
* @retval NULL Error
*/
C2D_SpriteSheet C2D_SpriteSheetLoadFromHandle(FILE* f);
/** @brief Free a sprite sheet
* @param[in] sheet Sprite sheet handle
*/
void C2D_SpriteSheetFree(C2D_SpriteSheet sheet);
/** @brief Retrieves the number of sprites in the specified sprite sheet
* @param[in] sheet Sprite sheet handle
* @returns Number of sprites
*/
size_t C2D_SpriteSheetCount(C2D_SpriteSheet sheet);
/** @brief Retrieves the specified image from the specified sprite sheet
* @param[in] sheet Sprite sheet handle
* @param[in] index Index of the image to retrieve
* @returns Image object
*/
C2D_Image C2D_SpriteSheetGetImage(C2D_SpriteSheet sheet, size_t index);
/** @} */

154
include/c2d/text.h Executable file
View file

@ -0,0 +1,154 @@
/**
* @file text.h
* @brief Text rendering API
*/
#pragma once
#include "base.h"
#include "font.h"
struct C2D_TextBuf_s;
typedef struct C2D_TextBuf_s* C2D_TextBuf;
/** @defgroup Text Text drawing functions
* @{
*/
/// Text object.
typedef struct
{
C2D_TextBuf buf; ///< Buffer associated with the text.
size_t begin; ///< Reserved for internal use.
size_t end; ///< Reserved for internal use.
float width; ///< Width of the text in pixels, according to 1x scale metrics.
u32 lines; ///< Number of lines in the text.
u32 words; ///< Number of words in the text.
C2D_Font font; ///< Font used to draw the text, or NULL for system font
} C2D_Text;
enum
{
C2D_AtBaseline = BIT(0), ///< Matches the Y coordinate with the baseline of the font.
C2D_WithColor = BIT(1), ///< Draws text with color. Requires a u32 color value.
C2D_AlignLeft = 0 << 2, ///< Draws text aligned to the left. This is the default.
C2D_AlignRight = 1 << 2, ///< Draws text aligned to the right.
C2D_AlignCenter = 2 << 2, ///< Draws text centered.
C2D_AlignJustified = 3 << 2, ///< Draws text justified. When C2D_WordWrap is not specified, right edge is x + scaleX*text->width. Otherwise, right edge is x + the width specified for those values.
C2D_AlignMask = 3 << 2, ///< Bitmask for alignment values.
C2D_WordWrap = BIT(4), ///< Draws text with wrapping of full words before specified width. Requires a float value, passed after color if C2D_WithColor is specified.
};
/** @brief Creates a new text buffer.
* @param[in] maxGlyphs Maximum number of glyphs that can be stored in the buffer.
* @returns Text buffer handle (or NULL on failure).
*/
C2D_TextBuf C2D_TextBufNew(size_t maxGlyphs);
/** @brief Resizes a text buffer.
* @param[in] buf Text buffer to resize.
* @param[in] maxGlyphs Maximum number of glyphs that can be stored in the buffer.
* @returns New text buffer handle (or NULL on failure).
* @remarks If successful, old text buffer handle becomes invalid.
*/
C2D_TextBuf C2D_TextBufResize(C2D_TextBuf buf, size_t maxGlyphs);
/** @brief Deletes a text buffer.
* @param[in] buf Text buffer handle.
* @remarks This also invalidates all text objects previously created with this buffer.
*/
void C2D_TextBufDelete(C2D_TextBuf buf);
/** @brief Clears all stored text in a buffer.
* @param[in] buf Text buffer handle.
*/
void C2D_TextBufClear(C2D_TextBuf buf);
/** @brief Retrieves the number of glyphs stored in a text buffer.
* @param[in] buf Text buffer handle.
* @returns The number of glyphs.
*/
size_t C2D_TextBufGetNumGlyphs(C2D_TextBuf buf);
/** @brief Parses and adds a single line of text to a text buffer.
* @param[out] text Pointer to text object to store information in.
* @param[in] buf Text buffer handle.
* @param[in] str String to parse.
* @param[in] lineNo Line number assigned to the text (used to calculate vertical position).
* @remarks Whitespace doesn't add any glyphs to the text buffer and is thus "free".
* @returns On success, a pointer to the character on which string processing stopped, which
* can be a newline ('\n'; indicating that's where the line ended), the null character
* ('\0'; indicating the end of the string was reached), or any other character
* (indicating the text buffer is full and no more glyphs can be added).
* On failure, NULL.
*/
const char* C2D_TextParseLine(C2D_Text* text, C2D_TextBuf buf, const char* str, u32 lineNo);
/** @brief Parses and adds a single line of text to a text buffer.
* @param[out] text Pointer to text object to store information in.
* @param[in] font Font to get glyphs from, or null for system font
* @param[in] buf Text buffer handle.
* @param[in] str String to parse.
* @param[in] lineNo Line number assigned to the text (used to calculate vertical position).
* @remarks Whitespace doesn't add any glyphs to the text buffer and is thus "free".
* @returns On success, a pointer to the character on which string processing stopped, which
* can be a newline ('\n'; indicating that's where the line ended), the null character
* ('\0'; indicating the end of the string was reached), or any other character
* (indicating the text buffer is full and no more glyphs can be added).
* On failure, NULL.
*/
const char* C2D_TextFontParseLine(C2D_Text* text, C2D_Font font, C2D_TextBuf buf, const char* str, u32 lineNo);
/** @brief Parses and adds arbitrary text (including newlines) to a text buffer.
* @param[out] text Pointer to text object to store information in.
* @param[in] buf Text buffer handle.
* @param[in] str String to parse.
* @remarks Whitespace doesn't add any glyphs to the text buffer and is thus "free".
* @returns On success, a pointer to the character on which string processing stopped, which
* can be the null character ('\0'; indicating the end of the string was reached),
* or any other character (indicating the text buffer is full and no more glyphs can be added).
* On failure, NULL.
*/
const char* C2D_TextParse(C2D_Text* text, C2D_TextBuf buf, const char* str);
/** @brief Parses and adds arbitrary text (including newlines) to a text buffer.
* @param[out] text Pointer to text object to store information in.
* @param[in] font Font to get glyphs from, or null for system font
* @param[in] buf Text buffer handle.
* @param[in] str String to parse.
* @remarks Whitespace doesn't add any glyphs to the text buffer and is thus "free".
* @returns On success, a pointer to the character on which string processing stopped, which
* can be the null character ('\0'; indicating the end of the string was reached),
* or any other character (indicating the text buffer is full and no more glyphs can be added).
* On failure, NULL.
*/
const char* C2D_TextFontParse(C2D_Text* text, C2D_Font font, C2D_TextBuf buf, const char* str);
/** @brief Optimizes a text object in order to be drawn more efficiently.
* @param[in] text Pointer to text object.
*/
void C2D_TextOptimize(const C2D_Text* text);
/** @brief Retrieves the total dimensions of a text object.
* @param[in] text Pointer to text object.
* @param[in] scaleX Horizontal size of the font. 1.0f corresponds to the native size of the font.
* @param[in] scaleY Vertical size of the font. 1.0f corresponds to the native size of the font.
* @param[out] outWidth (optional) Variable in which to store the width of the text.
* @param[out] outHeight (optional) Variable in which to store the height of the text.
*/
void C2D_TextGetDimensions(const C2D_Text* text, float scaleX, float scaleY, float* outWidth, float* outHeight);
/** @brief Draws text using the GPU.
* @param[in] text Pointer to text object.
* @param[in] flags Text drawing flags.
* @param[in] x Horizontal position to draw the text on.
* @param[in] y Vertical position to draw the text on. If C2D_AtBaseline is not specified (default), this
* is the top left corner of the block of text; otherwise this is the position of the baseline
* of the first line of text.
* @param[in] z Depth value of the text. If unsure, pass 0.0f.
* @param[in] scaleX Horizontal size of the font. 1.0f corresponds to the native size of the font.
* @param[in] scaleY Vertical size of the font. 1.0f corresponds to the native size of the font.
* @remarks The default 3DS system font has a glyph height of 30px, and the baseline is at 25px.
*/
void C2D_DrawText(const C2D_Text* text, u32 flags, float x, float y, float z, float scaleX, float scaleY, ...);
/** @} */

16
include/c3d/attribs.h Normal file
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);

46
include/c3d/base.h Normal file
View file

@ -0,0 +1,46 @@
#pragma once
#include "buffers.h"
#include "maths.h"
#define C3D_DEFAULT_CMDBUF_SIZE 0x40000
enum
{
C3D_UNSIGNED_BYTE = 0,
C3D_UNSIGNED_SHORT = 1,
};
bool C3D_Init(size_t cmdBufSize);
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);
}
// 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;
}

21
include/c3d/buffers.h Normal file
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);

15
include/c3d/effect.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include "types.h"
void C3D_DepthMap(bool bIsZBuffer, 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_EarlyDepthTest(bool enable, GPU_EARLYDEPTHFUNC function, u32 ref);
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);
void C3D_FragOpShadow(float scale, float bias);

39
include/c3d/fog.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "types.h"
#include <math.h>
typedef struct
{
u32 data[128];
} C3D_FogLut;
typedef struct
{
u32 diff[8];
u32 color[8];
} C3D_GasLut;
static inline float FogLut_CalcZ(float depth, float near, float far)
{
return far*near/(depth*(far-near)+near);
}
void FogLut_FromArray(C3D_FogLut* lut, const float data[256]);
void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, float far);
void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip);
void C3D_FogColor(u32 color);
void C3D_FogLutBind(C3D_FogLut* lut);
void GasLut_FromArray(C3D_GasLut* lut, const u32 data[9]);
void C3D_GasBeginAcc(void);
void C3D_GasDeltaZ(float value);
void C3D_GasAccMax(float value);
void C3D_GasAttn(float value);
void C3D_GasLightPlanar(float min, float max, float attn);
void C3D_GasLightView(float min, float max, float attn);
void C3D_GasLightDirection(float dotp);
void C3D_GasLutInput(GPU_GASLUTINPUT input);
void C3D_GasLutBind(C3D_GasLut* lut);

69
include/c3d/framebuffer.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#include "texture.h"
typedef struct
{
void* colorBuf;
void* depthBuf;
u16 width;
u16 height;
GPU_COLORBUF colorFmt;
GPU_DEPTHBUF depthFmt;
bool block32;
u8 colorMask : 4;
u8 depthMask : 4;
} C3D_FrameBuf;
// Flags for C3D_FrameBufClear
typedef enum
{
C3D_CLEAR_COLOR = BIT(0),
C3D_CLEAR_DEPTH = BIT(1),
C3D_CLEAR_ALL = C3D_CLEAR_COLOR | C3D_CLEAR_DEPTH,
} C3D_ClearBits;
u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt);
u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt);
C3D_FrameBuf* C3D_GetFrameBuf(void);
void C3D_SetFrameBuf(C3D_FrameBuf* fb);
void C3D_FrameBufTex(C3D_FrameBuf* fb, C3D_Tex* tex, GPU_TEXFACE face, int level);
void C3D_FrameBufClear(C3D_FrameBuf* fb, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth);
void C3D_FrameBufTransfer(C3D_FrameBuf* fb, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags);
static inline void C3D_FrameBufAttrib(C3D_FrameBuf* fb, u16 width, u16 height, bool block32)
{
fb->width = width;
fb->height = height;
fb->block32 = block32;
}
static inline void C3D_FrameBufColor(C3D_FrameBuf* fb, void* buf, GPU_COLORBUF fmt)
{
if (buf)
{
fb->colorBuf = buf;
fb->colorFmt = fmt;
fb->colorMask = 0xF;
} else
{
fb->colorBuf = NULL;
fb->colorFmt = GPU_RB_RGBA8;
fb->colorMask = 0;
}
}
static inline void C3D_FrameBufDepth(C3D_FrameBuf* fb, void* buf, GPU_DEPTHBUF fmt)
{
if (buf)
{
fb->depthBuf = buf;
fb->depthFmt = fmt;
fb->depthMask = fmt == GPU_RB_DEPTH24_STENCIL8 ? 0x3 : 0x2;
} else
{
fb->depthBuf = NULL;
fb->depthFmt = GPU_RB_DEPTH24;
fb->depthMask = 0;
}
}

157
include/c3d/light.h Normal file
View file

@ -0,0 +1,157 @@
#pragma once
#include "lightlut.h"
#include "maths.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_IsCP(n) BIT(18+(n))
#define C3DF_LightEnv_IsCP_Any (0xFF<<18)
#define C3DF_LightEnv_LutDirty(n) BIT(26+(n))
#define C3DF_LightEnv_LutDirtyAll (0x3F<<26)
};
struct C3D_LightEnv_t
{
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, GPU_LIGHTLUTID lutId, GPU_LIGHTLUTINPUT input, bool negative, 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);
/**
* @brief Configures whether to use the z component of the normal map.
* @param[out] env Pointer to light environment structure.
* @param[in] enable false if the z component is reconstructed from the xy components
* of the normal map, true if the z component is taken from the normal map.
*/
void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool enable);
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 ambient[3];
float diffuse[3];
float specular0[3];
float specular1[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_LightAmbient(C3D_Light* light, float r, float g, float b);
void C3D_LightDiffuse(C3D_Light* light, float r, float g, float b);
void C3D_LightSpecular0(C3D_Light* light, float r, float g, float b);
void C3D_LightSpecular1(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);
static inline void C3D_LightColor(C3D_Light* light, float r, float g, float b)
{
C3D_LightDiffuse(light, r, g, b);
C3D_LightSpecular0(light, r, g, b);
C3D_LightSpecular1(light, r, g, b);
}

35
include/c3d/lightlut.h Normal file
View file

@ -0,0 +1,35 @@
#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);
}
static inline float spot_step(float angle, float cutoff)
{
return angle >= cutoff ? 1.0f : 0.0f;
}
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 LightLut_Spotlight(lut, angle) LightLut_FromFunc((lut), spot_step, cosf(angle), true)
#define LightLutDA_Quadratic(lut, from, to, linear, quad) LightLutDA_Create((lut), quadratic_dist_attn, (from), (to), (linear), (quad))

796
include/c3d/maths.h Normal file
View file

@ -0,0 +1,796 @@
#pragma once
#include "types.h"
#include <math.h>
#include <string.h>
/**
* @addtogroup math_support
* @brief Implementations of matrix, vector, and quaternion operations.
* @{
*/
/**
* The one true circumference-to-radius ratio.
* See http://tauday.com/tau-manifesto
*/
#define M_TAU (6.28318530717958647692528676655900576)
// Define the legacy circle constant as well
#ifndef M_PI
#define M_PI (M_TAU/2)
#endif
/**
* @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 equivalent to `FVec4_Scale(v, -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 FVec3
* @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 FVec3
* @note This is equivalent to `FVec3_Scale(v, -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 Creates a matrix with the diagonal using the given parameters.
* @param[out] out Output matrix.
* @param[in] x The X component.
* @param[in] y The Y component.
* @param[in] z The Z component.
* @param[in] w The W component.
*/
static inline void Mtx_Diagonal(C3D_Mtx* out, float x, float y, float z, float w)
{
Mtx_Zeros(out);
out->r[0].x = x;
out->r[1].y = y;
out->r[2].z = z;
out->r[3].w = w;
}
/**
* @brief Identity matrix
* @param[out] out Matrix to fill
*/
static inline void Mtx_Identity(C3D_Mtx* out)
{
Mtx_Diagonal(out, 1.0f, 1.0f, 1.0f, 1.0f);
}
/**
*@brief Transposes the matrix. Row => Column, and vice versa.
*@param[in,out] out Output matrix.
*/
void Mtx_Transpose(C3D_Mtx* out);
/**
* @brief Matrix addition
* @param[out] out Output matrix.
* @param[in] lhs Left matrix.
* @param[in] rhs Right matrix.
* @return lhs+rhs (sum)
*/
static inline void Mtx_Add(C3D_Mtx* out, const C3D_Mtx* lhs, const C3D_Mtx* rhs)
{
for (int i = 0; i < 16; i++)
out->m[i] = lhs->m[i] + rhs->m[i];
}
/**
* @brief Matrix subtraction
* @param[out] out Output matrix.
* @param[in] lhs Left matrix.
* @param[in] rhs Right matrix.
* @return lhs-rhs (difference)
*/
static inline void Mtx_Subtract(C3D_Mtx* out, const C3D_Mtx* lhs, const C3D_Mtx* rhs)
{
for (int i = 0; i < 16; i++)
out->m[i] = lhs->m[i] - rhs->m[i];
}
/**
* @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
* @param[in,out] out Matrix to inverse
* @retval 0.0f Degenerate matrix (no 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 mtx*v (product)
*/
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 mtx*v (product)
*/
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 mtx*v (product)
*/
static inline C3D_FVec Mtx_MultiplyFVecH(const C3D_Mtx* mtx, C3D_FVec v)
{
v.w = 1.0f;
return Mtx_MultiplyFVec4(mtx, v);
}
/**
* @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);
/** @} */
/**
* @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
* @sa Mtx_OrthoTilt
*/
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
* @sa Mtx_PerspTilt
* @sa Mtx_PerspStereo
* @sa Mtx_PerspStereoTilt
*/
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
* @sa Mtx_Persp
* @sa Mtx_PerspTilt
* @sa Mtx_PerspStereoTilt
*/
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[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
* @sa Mtx_Ortho
*/
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
* @sa Mtx_Persp
* @sa Mtx_PerspStereo
* @sa Mtx_PerspStereoTilt
*/
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 @ref 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
* @sa Mtx_Persp
* @sa Mtx_PerspTilt
* @sa Mtx_PerspStereo
*/
void Mtx_PerspStereoTilt(C3D_Mtx* mtx, float fovy, float aspect, float near, float far, float iod, float screen, bool isLeftHanded);
/**
* @brief Look-At matrix, based on 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 Whether to build a LH projection
*/
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 Real component
* @return New Quaternion
*/
#define Quat_New(i,j,k,r) FVec4_New(i,j,k,r)
/**
* @brief Negate a Quaternion
* @note This is equivalent to `Quat_Scale(v, -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<sup>p</sup>
*/
C3D_FQuat Quat_Pow(C3D_FQuat q, float p);
/**
* @brief Cross product of Quaternion and FVec3
* @param[in] q Base Quaternion
* @param[in] v Vector to cross
* @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 Quaternion equivalent to 4x4 matrix
* @note If the matrix is orthogonal or special orthogonal, where determinant(matrix) = +1.0f, then the matrix can be converted.
* @param[in] m Input Matrix
* @return Generated Quaternion
*/
C3D_FQuat Quat_FromMtx(const C3D_Mtx* m);
/**
* @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 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 equivalent to `Quat_Pow(v, -1)`
* @param[in] q Quaternion of which to get inverse
* @return q<sup>-1</sup>
*/
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] v Base FVec3
* @param[in] q Quaternion to cross
* @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);
}
/**
* @brief Converting Pitch, Yaw, and Roll to Quaternion equivalent
* @param[in] pitch The pitch angle in radians.
* @param[in] yaw The yaw angle in radians.
* @param[in] roll The roll angle in radians.
* @param[in] bRightSide Whether to transform from the right side
* @return C3D_FQuat The Quaternion equivalent with the pitch, yaw, and roll (in that order) orientations applied.
*/
C3D_FQuat Quat_FromPitchYawRoll(float pitch, float yaw, float roll, bool bRightSide);
/**
* @brief Quaternion Look-At
* @param[in] source C3D_FVec Starting position. Origin of rotation.
* @param[in] target C3D_FVec Target position to orient towards.
* @param[in] forwardVector C3D_FVec The Up vector.
* @param[in] upVector C3D_FVec The Up vector.
* @return Quaternion rotation.
*/
C3D_FQuat Quat_LookAt(C3D_FVec source, C3D_FVec target, C3D_FVec forwardVector, C3D_FVec upVector);
/**
* @brief Quaternion, created from a given axis and angle in radians.
* @param[in] axis C3D_FVec The axis to rotate around at.
* @param[in] angle float The angle to rotate. Unit: Radians
* @return Quaternion rotation based on the axis and angle. Axis doesn't have to be orthogonal.
*/
C3D_FQuat Quat_FromAxisAngle(C3D_FVec axis, float angle);
/** @} */
/** @} */

24
include/c3d/mtxstack.h Normal file
View file

@ -0,0 +1,24 @@
#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);

125
include/c3d/proctex.h Normal file
View file

@ -0,0 +1,125 @@
#pragma once
#include "types.h"
typedef struct
{
u32 color[256];
u32 diff[256];
} C3D_ProcTexColorLut;
typedef struct
{
union
{
u32 proctex0;
struct
{
u32 uClamp : 3;
u32 vClamp : 3;
u32 rgbFunc : 4;
u32 alphaFunc : 4;
bool alphaSeparate : 1;
bool enableNoise : 1;
u32 uShift : 2;
u32 vShift : 2;
u32 lodBiasLow : 8;
};
};
union
{
u32 proctex1;
struct
{
u16 uNoiseAmpl;
u16 uNoisePhase;
};
};
union
{
u32 proctex2;
struct
{
u16 vNoiseAmpl;
u16 vNoisePhase;
};
};
union
{
u32 proctex3;
struct
{
u16 uNoiseFreq;
u16 vNoiseFreq;
};
};
union
{
u32 proctex4;
struct
{
u32 minFilter : 3;
u32 unknown1 : 8;
u32 width : 8;
u32 lodBiasHigh : 8;
};
};
union
{
u32 proctex5;
struct
{
u32 offset : 8;
u32 unknown2 : 24;
};
};
} C3D_ProcTex;
enum
{
C3D_ProcTex_U = BIT(0),
C3D_ProcTex_V = BIT(1),
C3D_ProcTex_UV = C3D_ProcTex_U | C3D_ProcTex_V,
};
void C3D_ProcTexInit(C3D_ProcTex* pt, int offset, int length);
void C3D_ProcTexNoiseCoefs(C3D_ProcTex* pt, int mode, float amplitude, float frequency, float phase);
void C3D_ProcTexLodBias(C3D_ProcTex* pt, float bias);
void C3D_ProcTexBind(int texCoordId, C3D_ProcTex* pt);
// GPU_LUT_NOISE, GPU_LUT_RGBMAP, GPU_LUT_ALPHAMAP
typedef u32 C3D_ProcTexLut[128];
void C3D_ProcTexLutBind(GPU_PROCTEX_LUTID id, C3D_ProcTexLut* lut);
void ProcTexLut_FromArray(C3D_ProcTexLut* lut, const float in[129]);
void C3D_ProcTexColorLutBind(C3D_ProcTexColorLut* lut);
void ProcTexColorLut_Write(C3D_ProcTexColorLut* out, const u32* in, int offset, int length);
static inline void C3D_ProcTexClamp(C3D_ProcTex* pt, GPU_PROCTEX_CLAMP u, GPU_PROCTEX_CLAMP v)
{
pt->uClamp = u;
pt->vClamp = v;
}
static inline void C3D_ProcTexCombiner(C3D_ProcTex* pt, bool separate, GPU_PROCTEX_MAPFUNC rgb, GPU_PROCTEX_MAPFUNC alpha)
{
pt->alphaSeparate = separate;
pt->rgbFunc = rgb;
if (separate)
pt->alphaFunc = alpha;
}
static inline void C3D_ProcTexNoiseEnable(C3D_ProcTex* pt, bool enable)
{
pt->enableNoise = enable;
}
static inline void C3D_ProcTexShift(C3D_ProcTex* pt, GPU_PROCTEX_SHIFT u, GPU_PROCTEX_SHIFT v)
{
pt->uShift = u;
pt->vShift = v;
}
static inline void C3D_ProcTexFilter(C3D_ProcTex* pt, GPU_PROCTEX_FILTER min)
{
pt->minFilter = min;
}

79
include/c3d/renderqueue.h Normal file
View file

@ -0,0 +1,79 @@
#pragma once
#include "framebuffer.h"
typedef struct C3D_RenderTarget_tag C3D_RenderTarget;
struct C3D_RenderTarget_tag
{
C3D_RenderTarget *next, *prev;
C3D_FrameBuf frameBuf;
bool used;
bool ownsColor, ownsDepth;
bool linked;
gfxScreen_t screen;
gfx3dSide_t side;
u32 transferFlags;
};
// Flags for C3D_FrameBegin
enum
{
C3D_FRAME_SYNCDRAW = BIT(0), // Perform C3D_FrameSync before checking the GPU status
C3D_FRAME_NONBLOCK = BIT(1), // Return false instead of waiting if the GPU is busy
};
float C3D_FrameRate(float fps);
void C3D_FrameSync(void);
u32 C3D_FrameCounter(int id);
bool C3D_FrameBegin(u8 flags);
bool C3D_FrameDrawOn(C3D_RenderTarget* target);
void C3D_FrameSplit(u8 flags);
void C3D_FrameEnd(u8 flags);
void C3D_FrameEndHook(void (* hook)(void*), void* param);
float C3D_GetDrawingTime(void);
float C3D_GetProcessingTime(void);
#if defined(__GNUC__) && !defined(__cplusplus)
typedef union __attribute__((__transparent_union__))
{
int __i;
GPU_DEPTHBUF __e;
} C3D_DEPTHTYPE;
#else
union C3D_DEPTHTYPE
{
private:
int __i;
GPU_DEPTHBUF __e;
public:
C3D_DEPTHTYPE(GPU_DEPTHBUF e) : __e(e) {}
C3D_DEPTHTYPE(int i) : __i(-1) { (void)i; }
};
#endif
#define C3D_DEPTHTYPE_OK(_x) ((_x).__i >= 0)
#define C3D_DEPTHTYPE_VAL(_x) ((_x).__e)
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt);
C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt);
void C3D_RenderTargetDelete(C3D_RenderTarget* target);
void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags);
static inline void C3D_RenderTargetDetachOutput(C3D_RenderTarget* target)
{
C3D_RenderTargetSetOutput(NULL, target->screen, target->side, 0);
}
static inline void C3D_RenderTargetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth)
{
C3D_FrameBufClear(&target->frameBuf, clearBits, clearColor, clearDepth);
}
void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags);
void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags);
void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1);

98
include/c3d/texenv.h Normal file
View file

@ -0,0 +1,98 @@
#pragma once
#include "types.h"
typedef struct
{
u16 srcRgb, srcAlpha;
union
{
u32 opAll;
struct { u32 opRgb:12, opAlpha:12; };
};
u16 funcRgb, funcAlpha;
u32 color;
u16 scaleRgb, scaleAlpha;
} C3D_TexEnv;
typedef enum
{
C3D_RGB = BIT(0),
C3D_Alpha = BIT(1),
C3D_Both = C3D_RGB | C3D_Alpha,
} C3D_TexEnvMode;
C3D_TexEnv* C3D_GetTexEnv(int id);
void C3D_SetTexEnv(int id, C3D_TexEnv* env);
void C3D_DirtyTexEnv(C3D_TexEnv* env);
void C3D_TexEnvBufUpdate(int mode, int mask);
void C3D_TexEnvBufColor(u32 color);
static inline void C3D_TexEnvInit(C3D_TexEnv* env)
{
env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0);
env->srcAlpha = env->srcRgb;
env->opAll = 0;
env->funcRgb = GPU_REPLACE;
env->funcAlpha = env->funcRgb;
env->color = 0xFFFFFFFF;
env->scaleRgb = GPU_TEVSCALE_1;
env->scaleAlpha = GPU_TEVSCALE_1;
}
#ifdef __cplusplus
#define _C3D_DEFAULT(x) = x
#else
#define _C3D_DEFAULT(x)
#endif
static inline void C3D_TexEnvSrc(C3D_TexEnv* env, C3D_TexEnvMode mode,
GPU_TEVSRC s1,
GPU_TEVSRC s2 _C3D_DEFAULT(GPU_PRIMARY_COLOR),
GPU_TEVSRC s3 _C3D_DEFAULT(GPU_PRIMARY_COLOR))
{
int param = GPU_TEVSOURCES((int)s1, (int)s2, (int)s3);
if ((int)mode & C3D_RGB)
env->srcRgb = param;
if ((int)mode & C3D_Alpha)
env->srcAlpha = param;
}
static inline void C3D_TexEnvOpRgb(C3D_TexEnv* env,
GPU_TEVOP_RGB o1,
GPU_TEVOP_RGB o2 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR),
GPU_TEVOP_RGB o3 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR))
{
env->opRgb = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3);
}
static inline void C3D_TexEnvOpAlpha(C3D_TexEnv* env,
GPU_TEVOP_A o1,
GPU_TEVOP_A o2 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA),
GPU_TEVOP_A o3 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA))
{
env->opAlpha = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3);
}
static inline void C3D_TexEnvFunc(C3D_TexEnv* env, C3D_TexEnvMode mode, GPU_COMBINEFUNC param)
{
if ((int)mode & C3D_RGB)
env->funcRgb = param;
if ((int)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, GPU_TEVSCALE param)
{
if (mode & C3D_RGB)
env->scaleRgb = param;
if (mode & C3D_Alpha)
env->scaleAlpha = param;
}
#undef _C3D_DEFAULT

183
include/c3d/texture.h Normal file
View file

@ -0,0 +1,183 @@
#pragma once
#include "types.h"
typedef struct
{
void* data[6];
} C3D_TexCube;
typedef struct
{
union
{
void* data;
C3D_TexCube* cube;
};
GPU_TEXCOLOR fmt : 4;
size_t size : 28;
union
{
u32 dim;
struct
{
u16 height;
u16 width;
};
};
u32 param;
u32 border;
union
{
u32 lodParam;
struct
{
u16 lodBias;
u8 maxLevel;
u8 minLevel;
};
};
} C3D_Tex;
typedef struct CTR_ALIGN(8)
{
u16 width;
u16 height;
u8 maxLevel : 4;
GPU_TEXCOLOR format : 4;
GPU_TEXTURE_MODE_PARAM type : 3;
bool onVram : 1;
} C3D_TexInitParams;
bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p);
void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level);
void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face);
void C3D_TexBind(int unitId, C3D_Tex* tex);
void C3D_TexFlush(C3D_Tex* tex);
void C3D_TexDelete(C3D_Tex* tex);
void C3D_TexShadowParams(bool perspective, float bias);
static inline int C3D_TexCalcMaxLevel(u32 width, u32 height)
{
return (31-__builtin_clz(width < height ? width : height)) - 3; // avoid sizes smaller than 8
}
static inline u32 C3D_TexCalcLevelSize(u32 size, int level)
{
return size >> (2*level);
}
static inline u32 C3D_TexCalcTotalSize(u32 size, int maxLevel)
{
/*
S = s + sr + sr^2 + sr^3 + ... + sr^n
Sr = sr + sr^2 + sr^3 + ... + sr^(n+1)
S-Sr = s - sr^(n+1)
S(1-r) = s(1 - r^(n+1))
S = s (1 - r^(n+1)) / (1-r)
r = 1/4
1-r = 3/4
S = 4s (1 - (1/4)^(n+1)) / 3
S = 4s (1 - 1/4^(n+1)) / 3
S = (4/3) (s - s/4^(n+1))
S = (4/3) (s - s/(1<<(2n+2)))
S = (4/3) (s - s>>(2n+2))
*/
return (size - C3D_TexCalcLevelSize(size,maxLevel+1)) * 4 / 3;
}
static inline bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex, NULL,
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, false });
}
static inline bool C3D_TexInitMipmap(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex, NULL,
(C3D_TexInitParams){ width, height, (u8)C3D_TexCalcMaxLevel(width, height), format, GPU_TEX_2D, false });
}
static inline bool C3D_TexInitCube(C3D_Tex* tex, C3D_TexCube* cube, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex, cube,
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_CUBE_MAP, false });
}
static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex, NULL,
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, true });
}
static inline bool C3D_TexInitShadow(C3D_Tex* tex, u16 width, u16 height)
{
return C3D_TexInitWithParams(tex, NULL,
(C3D_TexInitParams){ width, height, 0, GPU_RGBA8, GPU_TEX_SHADOW_2D, true });
}
static inline bool C3D_TexInitShadowCube(C3D_Tex* tex, C3D_TexCube* cube, u16 width, u16 height)
{
return C3D_TexInitWithParams(tex, cube,
(C3D_TexInitParams){ width, height, 0, GPU_RGBA8, GPU_TEX_SHADOW_CUBE, true });
}
static inline GPU_TEXTURE_MODE_PARAM C3D_TexGetType(C3D_Tex* tex)
{
return (GPU_TEXTURE_MODE_PARAM)((tex->param>>28)&0x7);
}
static inline void* C3D_TexGetImagePtr(C3D_Tex* tex, void* data, int level, u32* size)
{
if (size) *size = level >= 0 ? C3D_TexCalcLevelSize(tex->size, level) : C3D_TexCalcTotalSize(tex->size, tex->maxLevel);
if (!level) return data;
return (u8*)data + (level > 0 ? C3D_TexCalcTotalSize(tex->size, level-1) : 0);
}
static inline void* C3D_Tex2DGetImagePtr(C3D_Tex* tex, int level, u32* size)
{
return C3D_TexGetImagePtr(tex, tex->data, level, size);
}
static inline void* C3D_TexCubeGetImagePtr(C3D_Tex* tex, GPU_TEXFACE face, int level, u32* size)
{
return C3D_TexGetImagePtr(tex, tex->cube->data[face], level, size);
}
static inline void C3D_TexUpload(C3D_Tex* tex, const void* data)
{
C3D_TexLoadImage(tex, data, GPU_TEXFACE_2D, 0);
}
static inline 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);
}
static inline void C3D_TexSetFilterMipmap(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM filter)
{
tex->param &= ~GPU_TEXTURE_MIP_FILTER(GPU_LINEAR);
tex->param |= GPU_TEXTURE_MIP_FILTER(filter);
}
static inline 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);
}
static inline void C3D_TexSetLodBias(C3D_Tex* tex, float lodBias)
{
int iLodBias = (int)(lodBias*0x100);
if (iLodBias > 0xFFF)
iLodBias = 0xFFF;
else if (iLodBias < -0x1000)
iLodBias = -0x1000;
tex->lodBias = iLodBias & 0x1FFF;
}

81
include/c3d/types.h Normal file
View file

@ -0,0 +1,81 @@
#pragma once
#if defined(__3DS__) || defined(_3DS)
#include <3ds.h>
#else
#include <stdbool.h>
#include <stdint.h>
typedef uint8_t u8;
typedef uint32_t u32;
#endif
#ifndef CITRO3D_NO_DEPRECATION
#define C3D_DEPRECATED __attribute__ ((deprecated))
#else
#define C3D_DEPRECATED
#endif
typedef u32 C3D_IVec;
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);
}
/**
* @defgroup math_support Math Support Library
* @brief Implementations of matrix, vector, and quaternion operations.
* @{
*/
/**
* @struct C3D_FVec
* @brief Float vector
*
* Matches PICA layout
*/
typedef union
{
/**
* @brief Vector access
*/
struct
{
float w; ///< W-component
float z; ///< Z-component
float y; ///< Y-component
float x; ///< X-component
};
/**
* @brief Quaternion access
*/
struct
{
float r; ///< Real component
float k; ///< K-component
float j; ///< J-component
float i; ///< I-component
};
/**
* @brief Raw access
*/
float c[4];
} C3D_FVec;
/**
* @struct C3D_FQuat
* @brief Float quaternion. See @ref C3D_FVec.
*/
typedef C3D_FVec C3D_FQuat;
/**
* @struct C3D_Mtx
* @brief Row-major 4x4 matrix
*/
typedef union
{
C3D_FVec r[4]; ///< Rows are vectors
float m[4*4]; ///< Raw access
} C3D_Mtx;
/** @} */

78
include/c3d/uniforms.h Normal file
View file

@ -0,0 +1,78 @@
#pragma once
#include "maths.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);

26
include/citro2d.h Normal file
View file

@ -0,0 +1,26 @@
/**
* @file citro2d.h
* @brief Central citro2d header. Includes all others.
*/
#pragma once
#ifdef CITRO2D_BUILD
#error "This header file is only for external users of citro2d."
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <citro3d.h>
#include <tex3ds.h>
#include "c2d/base.h"
#include "c2d/spritesheet.h"
#include "c2d/sprite.h"
#include "c2d/text.h"
#include "c2d/font.h"
#ifdef __cplusplus
}
#endif

34
include/citro3d.h Normal file
View file

@ -0,0 +1,34 @@
#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/proctex.h"
#include "c3d/light.h"
#include "c3d/lightlut.h"
#include "c3d/fog.h"
#include "c3d/framebuffer.h"
#include "c3d/renderqueue.h"
#ifdef __cplusplus
}
#endif

215
include/tex3ds.h Normal file
View file

@ -0,0 +1,215 @@
/*------------------------------------------------------------------------------
* Copyright (c) 2017
* Michael Theall (mtheall)
*
* This file is part of citro3d.
*
* 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.
*----------------------------------------------------------------------------*/
/** @file tex3ds.h
* @brief tex3ds support
*/
#pragma once
#ifdef CITRO3D_BUILD
#include "c3d/texture.h"
#else
#include <citro3d.h>
#endif
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Subtexture
* @note If top < bottom, the subtexture is rotated 1/4 revolution counter-clockwise
*/
typedef struct Tex3DS_SubTexture
{
u16 width; ///< Sub-texture width (pixels)
u16 height; ///< Sub-texture height (pixels)
float left; ///< Left u-coordinate
float top; ///< Top v-coordinate
float right; ///< Right u-coordinate
float bottom; ///< Bottom v-coordinate
} Tex3DS_SubTexture;
/** @brief Texture */
typedef struct Tex3DS_Texture_s* Tex3DS_Texture;
/** @brief Import Tex3DS texture
* @param[in] input Input data
* @param[in] insize Size of the input data
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImport(const void* input, size_t insize, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Import Tex3DS texture
*
* @description
* For example, use this if you want to import from a large file without
* pulling the entire file into memory.
*
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @param[in] callback Data callback
* @param[in] userdata User data passed to callback
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportCallback(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata);
/** @brief Import Tex3DS texture
*
* Starts reading at the current file descriptor's offset. The file
* descriptor's position is left at the end of the decoded data. On error, the
* file descriptor's position is indeterminate.
*
* @param[in] fd Open file descriptor
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportFD(int fd, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Import Tex3DS texture
*
* Starts reading at the current file stream's offset. The file stream's
* position is left at the end of the decoded data. On error, the file
* stream's position is indeterminate.
*
* @param[in] fp Open file stream
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportStdio(FILE* fp, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Get number of subtextures
* @param[in] texture Tex3DS texture
* @returns Number of subtextures
*/
size_t Tex3DS_GetNumSubTextures(const Tex3DS_Texture texture);
/** @brief Get subtexture
* @param[in] texture Tex3DS texture
* @param[in] index Subtexture index
* @returns Subtexture info
*/
const Tex3DS_SubTexture* Tex3DS_GetSubTexture(const Tex3DS_Texture texture, size_t index);
/** @brief Check if subtexture is rotated
* @param[in] subtex Subtexture to check
* @returns whether subtexture is rotated
*/
static inline bool
Tex3DS_SubTextureRotated(const Tex3DS_SubTexture* subtex)
{
return subtex->top < subtex->bottom;
}
/** @brief Get bottom-left texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureBottomLeft(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->left;
*v = subtex->bottom;
} else
{
*u = subtex->bottom;
*v = subtex->left;
}
}
/** @brief Get bottom-right texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureBottomRight(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->right;
*v = subtex->bottom;
} else
{
*u = subtex->bottom;
*v = subtex->right;
}
}
/** @brief Get top-left texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureTopLeft(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->left;
*v = subtex->top;
} else
{
*u = subtex->top;
*v = subtex->left;
}
}
/** @brief Get top-right texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureTopRight(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->right;
*v = subtex->top;
} else
{
*u = subtex->top;
*v = subtex->right;
}
}
/** @brief Free Tex3DS texture
* @param[in] texture Tex3DS texture to free
*/
void Tex3DS_TextureFree(Tex3DS_Texture texture);
#ifdef __cplusplus
}
#endif

View file

@ -2,6 +2,8 @@
#include <iostream>
#include "utilsconsole.hpp"
#include "graphics.hpp"
#include <citro2d.h>
#include <citro3d.h>
#include <string.h>
using namespace std;
@ -317,12 +319,97 @@ void mode_yellue(void) {
}
}
static void mode_3d_moving_square() {
int ans = 0;
int z = 0;
gfxSet3D(true);
while (aptMainLoop() && ans != 1) {
static const int menu_size = 2;
static const s_uc_menu_element menu[] = {
{"z ", true, &z, -100, 100},
{"back", false, NULL, 0, 0},
};
static auto menuVec = vector<s_uc_menu_element>(menu, menu + menu_size);
ans = uc_menu(menuVec);
gspWaitForVBlank();
static const u32 color_back = 0xFFFFFF;
static const u32 color_square = 0xFFFF00;
u16 width, height;
t_color *frame;
frame = (t_color *) gfxGetFramebuffer(
GFX_TOP, GFX_LEFT, &height, &width);
for (int i = 0; i < width * height; i++)
{
int x = i / height - z;
if (x < 0 || x >= width)
continue;
int y = height - i % height - 1;
if (x > 190 && x < 210 && y > 100 && y < 120)
rgb_set(frame[i], color_square);
else
rgb_set(frame[i], color_back);
}
frame = (t_color *) gfxGetFramebuffer(
GFX_TOP, GFX_RIGHT, &height, &width);
for (int i = 0; i < width * height; i++)
{
int x = i / height + z;
if (x < 0 || x >= width)
continue;
int y = height - i % height - 1;
if (x > 190 && x < 210 && y > 100 && y < 120)
rgb_set(frame[i], color_square);
else
rgb_set(frame[i], color_back);
}
gfxFlushBuffers();
gfxSwapBuffers();
}
}
static void mode_citro2d() {
uc_pause();
C2D_Prepare();
uc_pause();
C2D_Image img;
uc_pause();
img.tex = new C3D_Tex;
uc_pause();
C3D_TexInitVRAM(img.tex, 200, 100, GPU_RGBA8);
uc_pause();
img.subtex = NULL;
uc_pause();
C3D_RenderTarget* top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT);
uc_pause();
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
uc_pause();
C2D_TargetClear(top, C2D_Color32(255, 128, 192, 0));
uc_pause();
C3D_FrameEnd(0);
uc_pause();
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
uc_pause();
C2D_SceneBegin(top);
uc_pause();
C2D_DrawImageAt(img, 50, 30, 32);
uc_pause();
C3D_FrameEnd(0);
uc_pause();
}
int main(void) {;
gfxInitDefault();
consoleInit(GFX_BOTTOM, NULL);
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
C2D_Init(C2D_DEFAULT_MAX_OBJECTS);
while (aptMainLoop()) {
static const int menu_size = 7;
static const int menu_size = 9;
static const s_uc_menu_element menu[] = {
{"ugly rainbow", false, NULL, 0, 0},
{"direction", false, NULL, 0, 0},
@ -331,9 +418,14 @@ int main(void) {;
{"3D square", false, NULL, 0, 0},
{"ugly 3D rainbow", false, NULL, 0, 0},
{"yellue", false, NULL, 0, 0},
{"3D moving square", false, NULL, 0, 0},
{"citro2d", false, NULL, 0, 0},
{"quit", false, NULL, 0, 0},
};
static auto menuVec = vector<s_uc_menu_element>(menu, menu + menu_size);
int ans = uc_menu(menuVec);
switch (ans)
{}
if (ans == 0)
mode_ugly_rainbow();
if (ans == 1)
@ -348,7 +440,14 @@ int main(void) {;
mode_ugly_3d_rainbow();
if (ans == 6)
mode_yellue();
if (ans == 7)
mode_3d_moving_square();
if (ans == 8)
mode_citro2d();
if (ans == 9)
break;
gfxSet3D(false);
}
gfxExit();
}
C2D_Fini();
}