diff --git a/include/c2d/base.h b/include/c2d/base.h new file mode 100644 index 0000000..a98e0e9 --- /dev/null +++ b/include/c2d/base.h @@ -0,0 +1,511 @@ +/** + * @file base.h + * @brief Basic citro2d initialization and drawing API + */ +#pragma once +#include +#include + +#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, ¶ms, 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, ¶ms, 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); +} +/** @} */ diff --git a/include/c2d/font.h b/include/c2d/font.h new file mode 100644 index 0000000..130aa8e --- /dev/null +++ b/include/c2d/font.h @@ -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); + +/** @} */ diff --git a/include/c2d/sprite.h b/include/c2d/sprite.h new file mode 100644 index 0000000..d242aed --- /dev/null +++ b/include/c2d/sprite.h @@ -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); +} + +/** @} */ diff --git a/include/c2d/spritesheet.h b/include/c2d/spritesheet.h new file mode 100644 index 0000000..83c434e --- /dev/null +++ b/include/c2d/spritesheet.h @@ -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); + +/** @} */ diff --git a/include/c2d/text.h b/include/c2d/text.h new file mode 100755 index 0000000..53a2419 --- /dev/null +++ b/include/c2d/text.h @@ -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, ...); + +/** @} */ diff --git a/include/c3d/attribs.h b/include/c3d/attribs.h new file mode 100644 index 0000000..71d404d --- /dev/null +++ b/include/c3d/attribs.h @@ -0,0 +1,16 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 flags[2]; + u64 permutation; + int attrCount; +} C3D_AttrInfo; + +void AttrInfo_Init(C3D_AttrInfo* info); +int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count); +int AttrInfo_AddFixed(C3D_AttrInfo* info, int regId); + +C3D_AttrInfo* C3D_GetAttrInfo(void); +void C3D_SetAttrInfo(C3D_AttrInfo* info); diff --git a/include/c3d/base.h b/include/c3d/base.h new file mode 100644 index 0000000..97796c6 --- /dev/null +++ b/include/c3d/base.h @@ -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; +} diff --git a/include/c3d/buffers.h b/include/c3d/buffers.h new file mode 100644 index 0000000..fe49dcb --- /dev/null +++ b/include/c3d/buffers.h @@ -0,0 +1,21 @@ +#pragma once +#include "types.h" + +typedef struct +{ + u32 offset; + u32 flags[2]; +} C3D_BufCfg; + +typedef struct +{ + u32 base_paddr; + int bufCount; + C3D_BufCfg buffers[12]; +} C3D_BufInfo; + +void BufInfo_Init(C3D_BufInfo* info); +int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation); + +C3D_BufInfo* C3D_GetBufInfo(void); +void C3D_SetBufInfo(C3D_BufInfo* info); diff --git a/include/c3d/effect.h b/include/c3d/effect.h new file mode 100644 index 0000000..26e3617 --- /dev/null +++ b/include/c3d/effect.h @@ -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); diff --git a/include/c3d/fog.h b/include/c3d/fog.h new file mode 100644 index 0000000..8541af2 --- /dev/null +++ b/include/c3d/fog.h @@ -0,0 +1,39 @@ +#pragma once +#include "types.h" +#include + +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); diff --git a/include/c3d/framebuffer.h b/include/c3d/framebuffer.h new file mode 100644 index 0000000..af5c9b9 --- /dev/null +++ b/include/c3d/framebuffer.h @@ -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; + } +} diff --git a/include/c3d/light.h b/include/c3d/light.h new file mode 100644 index 0000000..a32c4b9 --- /dev/null +++ b/include/c3d/light.h @@ -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); +} diff --git a/include/c3d/lightlut.h b/include/c3d/lightlut.h new file mode 100644 index 0000000..5905ba9 --- /dev/null +++ b/include/c3d/lightlut.h @@ -0,0 +1,35 @@ +#pragma once +#include "types.h" +#include + +typedef struct +{ + u32 data[256]; +} C3D_LightLut; + +typedef struct +{ + C3D_LightLut lut; + float bias, scale; +} C3D_LightLutDA; + +typedef float (* C3D_LightLutFunc)(float x, float param); +typedef float (* C3D_LightLutFuncDA)(float dist, float arg0, float arg1); + +static inline float quadratic_dist_attn(float dist, float linear, float quad) +{ + return 1.0f / (1.0f + linear*dist + quad*dist*dist); +} + +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)) diff --git a/include/c3d/maths.h b/include/c3d/maths.h new file mode 100644 index 0000000..026ec4e --- /dev/null +++ b/include/c3d/maths.h @@ -0,0 +1,796 @@ +#pragma once +#include "types.h" +#include +#include + +/** + * @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 lhs∙rhs + */ +static inline float FVec4_Dot(C3D_FVec lhs, C3D_FVec rhs) +{ + // A∙B = sum of component-wise products + return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w; +} + +/** + * @brief Magnitude of a FVec4 + * @param[in] v Vector + * @return ‖v‖ + */ +static inline float FVec4_Magnitude(C3D_FVec v) +{ + // ‖v‖ = √(v∙v) + return sqrtf(FVec4_Dot(v,v)); +} + +/** + * @brief Normalize a FVec4 + * @param[in] v FVec4 to normalize + * @return v/‖v‖ + */ +static inline C3D_FVec FVec4_Normalize(C3D_FVec v) +{ + // get vector magnitude + float m = FVec4_Magnitude(v); + + // scale by inverse magnitude to get a unit vector + return FVec4_New(v.x/m, v.y/m, v.z/m, v.w/m); +} + +/** + * @brief Create a new FVec3 + * @param[in] x X-component + * @param[in] y Y-component + * @param[in] z Z-component + * @return New FVec3 + */ +static inline C3D_FVec FVec3_New(float x, float y, float z) +{ + return FVec4_New(x, y, z, 0.0f); +} + +/** + * @brief Dot product of two FVec3s + * @param[in] lhs Left-side FVec3 + * @param[in] rhs Right-side FVec3 + * @return lhs∙rhs + */ +static inline float FVec3_Dot(C3D_FVec lhs, C3D_FVec rhs) +{ + // A∙B = sum of component-wise products + return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; +} + +/** + * @brief Magnitude of a FVec3 + * @param[in] v Vector + * @return ‖v‖ + */ +static inline float FVec3_Magnitude(C3D_FVec v) +{ + // ‖v‖ = √(v∙v) + return sqrtf(FVec3_Dot(v,v)); +} + +/** + * @brief Normalize a FVec3 + * @param[in] v FVec3 to normalize + * @return v/‖v‖ + */ +static inline C3D_FVec FVec3_Normalize(C3D_FVec v) +{ + // get vector magnitude + float m = FVec3_Magnitude(v); + + // scale by inverse magnitude to get a unit vector + return FVec3_New(v.x/m, v.y/m, v.z/m); +} + +/** + * @brief Add two FVec3s + * @param[in] lhs Augend + * @param[in] rhs Addend + * @return lhs+rhs (sum) + */ +static inline C3D_FVec FVec3_Add(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise addition + return FVec3_New(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); +} + +/** + * @brief Subtract two FVec3s + * @param[in] lhs Minuend + * @param[in] rhs Subtrahend + * @return lhs-rhs (difference) + */ +static inline C3D_FVec FVec3_Subtract(C3D_FVec lhs, C3D_FVec rhs) +{ + // component-wise subtraction + return FVec3_New(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z); +} + +/** + * @brief Distance between two 3D points + * @param[in] lhs Relative origin + * @param[in] rhs Relative point of interest + * @return ‖lhs-rhs‖ + */ +static inline float FVec3_Distance(C3D_FVec lhs, C3D_FVec rhs) +{ + // distance = ‖lhs-rhs‖ + return FVec3_Magnitude(FVec3_Subtract(lhs, rhs)); +} + +/** + * @brief Scale a 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 lhs∙rhs + */ +#define Quat_Dot(lhs,rhs) FVec4_Dot(lhs,rhs) + +/** + * @brief Multiply two Quaternions + * @param[in] lhs Multiplicand + * @param[in] rhs Multiplier + * @return lhs*rhs + */ +C3D_FQuat Quat_Multiply(C3D_FQuat lhs, C3D_FQuat rhs); + +/** + * @brief Raise Quaternion to a power + * @note If p is 0, this returns the identity Quaternion. + * If p is 1, this returns q. + * @param[in] q Base Quaternion + * @param[in] p Power + * @return qp + */ +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-1 + */ +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); +/** @} */ +/** @} */ diff --git a/include/c3d/mtxstack.h b/include/c3d/mtxstack.h new file mode 100644 index 0000000..1de0c25 --- /dev/null +++ b/include/c3d/mtxstack.h @@ -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); diff --git a/include/c3d/proctex.h b/include/c3d/proctex.h new file mode 100644 index 0000000..3abf7ab --- /dev/null +++ b/include/c3d/proctex.h @@ -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; +} diff --git a/include/c3d/renderqueue.h b/include/c3d/renderqueue.h new file mode 100644 index 0000000..6eedd1e --- /dev/null +++ b/include/c3d/renderqueue.h @@ -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); diff --git a/include/c3d/texenv.h b/include/c3d/texenv.h new file mode 100644 index 0000000..9a42d1b --- /dev/null +++ b/include/c3d/texenv.h @@ -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 diff --git a/include/c3d/texture.h b/include/c3d/texture.h new file mode 100644 index 0000000..5500a8e --- /dev/null +++ b/include/c3d/texture.h @@ -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; +} diff --git a/include/c3d/types.h b/include/c3d/types.h new file mode 100644 index 0000000..1ee8334 --- /dev/null +++ b/include/c3d/types.h @@ -0,0 +1,81 @@ +#pragma once +#if defined(__3DS__) || defined(_3DS) +#include <3ds.h> +#else +#include +#include +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; +/** @} */ diff --git a/include/c3d/uniforms.h b/include/c3d/uniforms.h new file mode 100644 index 0000000..9e50153 --- /dev/null +++ b/include/c3d/uniforms.h @@ -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); diff --git a/include/citro2d.h b/include/citro2d.h new file mode 100644 index 0000000..f42d2f9 --- /dev/null +++ b/include/citro2d.h @@ -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 +#include + +#include "c2d/base.h" +#include "c2d/spritesheet.h" +#include "c2d/sprite.h" +#include "c2d/text.h" +#include "c2d/font.h" + +#ifdef __cplusplus +} +#endif diff --git a/include/citro3d.h b/include/citro3d.h new file mode 100644 index 0000000..80e5d47 --- /dev/null +++ b/include/citro3d.h @@ -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 diff --git a/include/tex3ds.h b/include/tex3ds.h new file mode 100644 index 0000000..c43d10e --- /dev/null +++ b/include/tex3ds.h @@ -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 +#endif + +#include +#include + +#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 diff --git a/source/main.cpp b/source/main.cpp index 425fa7d..dce58bb 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -2,6 +2,8 @@ #include #include "utilsconsole.hpp" #include "graphics.hpp" +#include +#include #include 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(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(); }