This commit is contained in:
zy 2024-12-29 19:19:38 +01:00
parent 714d9a8187
commit 5a116fa904
25 changed files with 3176 additions and 0 deletions

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;
@ -370,9 +372,41 @@ static void mode_3d_moving_square() {
}
}
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 = 9;
@ -385,6 +419,7 @@ int main(void) {;
{"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);
@ -408,8 +443,11 @@ int main(void) {;
if (ans == 7)
mode_3d_moving_square();
if (ans == 8)
mode_citro2d();
if (ans == 9)
break;
gfxSet3D(false);
}
gfxExit();
C2D_Fini();
}