Skip to content

Commit

Permalink
Add proctex-powered untextured circle/ellipse rendering functions (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
Swiftloke authored and fincs committed Dec 29, 2018
1 parent 40e6e92 commit 9a26392
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 48 deletions.
69 changes: 69 additions & 0 deletions include/c2d/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,73 @@ static inline bool C2D_DrawRectSolid(
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] 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_DrawCircleSolid(
float x, float y, float z, float radius,
u32 clr)
{
return C2D_DrawCircle(x,y,z,radius,clr,clr,clr,clr);
}
/** @} */
120 changes: 92 additions & 28 deletions source/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,22 @@ bool C2D_Init(size_t maxObjects)
C3D_ProcTexCombiner(&ctx->ptBlend, true, GPU_PT_U, GPU_PT_V);
C3D_ProcTexFilter(&ctx->ptBlend, GPU_PT_LINEAR);

C3D_ProcTexInit(&ctx->ptCircle, 0, 1);
C3D_ProcTexClamp(&ctx->ptCircle, GPU_PT_MIRRORED_REPEAT, GPU_PT_MIRRORED_REPEAT);
C3D_ProcTexCombiner(&ctx->ptCircle, true, GPU_PT_SQRT2, GPU_PT_SQRT2);
C3D_ProcTexFilter(&ctx->ptCircle, GPU_PT_LINEAR);

// Prepare proctex lut
float data[129];
int i;
for (i = 0; i <= 128; i ++)
data[i] = i/128.0f;
ProcTexLut_FromArray(&ctx->ptBlendLut, data);

for (i = 0; i <= 128; i ++)
data[i] = (i >= 127) ? 0 : 1;
ProcTexLut_FromArray(&ctx->ptCircleLut, data);

ctx->flags = C2DiF_Active;
ctx->vtxBufPos = 0;
ctx->vtxBufLastPos = 0;
Expand Down Expand Up @@ -113,16 +122,7 @@ void C2D_Prepare(void)
C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
C3D_TexEnvColor(env, 0xFFFFFFFF);

// Set texenv1 to blend the output of texenv0 with the primary color
// texenv1.rgb = mix(texenv0.rgb, vtx.color.rgb, vtx.blend.y);
// texenv1.a = texenv0.a * vtx.color.a;
env = C3D_GetTexEnv(1);
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PREVIOUS, GPU_PRIMARY_COLOR, GPU_TEXTURE3);
C3D_TexEnvOpRgb(env, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_ONE_MINUS_SRC_ALPHA);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_PREVIOUS, GPU_PRIMARY_COLOR, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_INTERPOLATE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
//TexEnv1 set afterwards by C2Di_Update()

/*
// Set texenv2 to tint the output of texenv1 with the specified tint
Expand Down Expand Up @@ -283,6 +283,7 @@ bool C2D_DrawImage(C2D_Image img, const C2D_DrawParams* params, const C2D_ImageT
if (6 > (ctx->vtxBufSize - ctx->vtxBufPos))
return false;

C2Di_SetCircle(false);
C2Di_SetTex(img.tex);
C2Di_Update();

Expand Down Expand Up @@ -317,13 +318,13 @@ bool C2D_DrawImage(C2D_Image img, const C2D_DrawParams* params, const C2D_ImageT
const C2D_Tint* tintBotRight = tint ? &tint->corners[C2D_BotRight] : &s_defaultTint;

// Draw triangles
C2Di_AppendVtx(quad.topLeft[0], quad.topLeft[1], params->depth, tcTopLeft[0], tcTopLeft[1], tintTopLeft->blend, tintTopLeft->color);
C2Di_AppendVtx(quad.botLeft[0], quad.botLeft[1], params->depth, tcBotLeft[0], tcBotLeft[1], tintBotLeft->blend, tintBotLeft->color);
C2Di_AppendVtx(quad.botRight[0], quad.botRight[1], params->depth, tcBotRight[0], tcBotRight[1], tintBotRight->blend, tintBotRight->color);
C2Di_AppendVtx(quad.topLeft[0], quad.topLeft[1], params->depth, tcTopLeft[0], tcTopLeft[1], 0, tintTopLeft->blend, tintTopLeft->color);
C2Di_AppendVtx(quad.botLeft[0], quad.botLeft[1], params->depth, tcBotLeft[0], tcBotLeft[1], 0, tintBotLeft->blend, tintBotLeft->color);
C2Di_AppendVtx(quad.botRight[0], quad.botRight[1], params->depth, tcBotRight[0], tcBotRight[1], 0, tintBotRight->blend, tintBotRight->color);

C2Di_AppendVtx(quad.topLeft[0], quad.topLeft[1], params->depth, tcTopLeft[0], tcTopLeft[1], tintTopLeft->blend, tintTopLeft->color);
C2Di_AppendVtx(quad.botRight[0], quad.botRight[1], params->depth, tcBotRight[0], tcBotRight[1], tintBotRight->blend, tintBotRight->color);
C2Di_AppendVtx(quad.topRight[0], quad.topRight[1], params->depth, tcTopRight[0], tcTopRight[1], tintTopRight->blend, tintTopRight->color);
C2Di_AppendVtx(quad.topLeft[0], quad.topLeft[1], params->depth, tcTopLeft[0], tcTopLeft[1], 0, tintTopLeft->blend, tintTopLeft->color);
C2Di_AppendVtx(quad.botRight[0], quad.botRight[1], params->depth, tcBotRight[0], tcBotRight[1], 0, tintBotRight->blend, tintBotRight->color);
C2Di_AppendVtx(quad.topRight[0], quad.topRight[1], params->depth, tcTopRight[0], tcTopRight[1], 0, tintTopRight->blend, tintTopRight->color);

return true;
}
Expand All @@ -336,13 +337,14 @@ bool C2D_DrawTriangle(float x0, float y0, u32 clr0, float x1, float y1, u32 clr1
if (3 > (ctx->vtxBufSize - ctx->vtxBufPos))
return false;

C2Di_SetCircle(false);
// Not necessary:
//C2Di_SetSrc(C2DiF_Src_None);
C2Di_Update();

C2Di_AppendVtx(x0, y0, depth, -1.0f, -1.0f, 1.0f, clr0);
C2Di_AppendVtx(x1, y1, depth, -1.0f, -1.0f, 1.0f, clr1);
C2Di_AppendVtx(x2, y2, depth, -1.0f, -1.0f, 1.0f, clr2);
C2Di_AppendVtx(x0, y0, depth, -1.0f, -1.0f, 0.0f, 1.0f, clr0);
C2Di_AppendVtx(x1, y1, depth, -1.0f, -1.0f, 0.0f, 1.0f, clr1);
C2Di_AppendVtx(x2, y2, depth, -1.0f, -1.0f, 0.0f, 1.0f, clr2);
return true;
}

Expand All @@ -354,21 +356,45 @@ bool C2D_DrawRectangle(float x, float y, float z, float w, float h, u32 clr0, u3
if (6 > (ctx->vtxBufSize - ctx->vtxBufPos))
return false;

C2Di_SetCircle(false);
// Not necessary:
//C2Di_SetSrc(C2DiF_Src_None);
C2Di_Update();

C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, 1.0f, clr0);
C2Di_AppendVtx(x, y+h, z, -1.0f, -1.0f, 1.0f, clr2);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 1.0f, clr3);
C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, 0.0f, 1.0f, clr0);
C2Di_AppendVtx(x, y+h, z, -1.0f, -1.0f, 0.0f, 1.0f, clr2);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 0.0f, 1.0f, clr3);

C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, 1.0f, clr0);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 1.0f, clr3);
C2Di_AppendVtx(x+w, y, z, -1.0f, -1.0f, 1.0f, clr1);
C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, 0.0f, 1.0f, clr0);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 0.0f, 1.0f, clr3);
C2Di_AppendVtx(x+w, y, z, -1.0f, -1.0f, 0.0f, 1.0f, clr1);
return true;
}

void C2Di_AppendVtx(float x, float y, float z, float u, float v, float blend, u32 color)
bool C2D_DrawEllipse(float x, float y, float z, float w, float h, u32 clr0, u32 clr1, u32 clr2, u32 clr3)
{
C2Di_Context* ctx = C2Di_GetContext();
if (!(ctx->flags & C2DiF_Active))
return false;
if (6 > (ctx->vtxBufSize - ctx->vtxBufPos))
return false;

C2Di_SetCircle(true);
// Not necessary:
//C2Di_SetSrc(C2DiF_Src_None);
C2Di_Update();

C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, -1.0f, -1.0f, clr0);
C2Di_AppendVtx(x, y+h, z, -1.0f, -1.0f, -1.0f, 1.0f, clr2);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 1.0f, 1.0f, clr3);

C2Di_AppendVtx(x, y, z, -1.0f, -1.0f, -1.0f, -1.0f, clr0);
C2Di_AppendVtx(x+w, y+h, z, -1.0f, -1.0f, 1.0f, 1.0f, clr3);
C2Di_AppendVtx(x+w, y, z, -1.0f, -1.0f, 1.0f, -1.0f, clr1);
return true;
}

void C2Di_AppendVtx(float x, float y, float z, float u, float v, float ptx, float pty, u32 color)
{
C2Di_Context* ctx = C2Di_GetContext();
C2Di_Vertex* vtx = &ctx->vtxBuf[ctx->vtxBufPos++];
Expand All @@ -377,8 +403,8 @@ void C2Di_AppendVtx(float x, float y, float z, float u, float v, float blend, u3
vtx->pos[2] = z;
vtx->texcoord[0] = u;
vtx->texcoord[1] = v;
vtx->blend[0] = 0.0f; // reserved for future expansion
vtx->blend[1] = blend;
vtx->ptcoord[0] = ptx;
vtx->ptcoord[1] = pty;
vtx->color = color;
}

Expand Down Expand Up @@ -410,5 +436,43 @@ void C2Di_Update(void)
if (flags & C2DiF_DirtyFade)
C3D_TexEnvColor(C3D_GetTexEnv(5), ctx->fadeClr);

if (flags & C2DiF_DirtyProcTex)
{
if (ctx->flags & C2DiF_ProcTex_Circle) // flags variable is only for dirty flags
{
C3D_ProcTexBind(1, &ctx->ptCircle);
C3D_ProcTexLutBind(GPU_LUT_ALPHAMAP, &ctx->ptCircleLut);

// Set TexEnv1 to use proctex to generate a circle.
// This circle then either passes through the alpha (if the fragment
// is within the circle) or discards the fragment.
// Unfortunately, blending the vertex color is not possible
// (because proctex is already being used), therefore it is simply multiplied.
// texenv1.rgb = texenv0.rgb * vtx.color.rgb;
// texenv1.a = texenv0.rgb * proctex.a;
C3D_TexEnv* env = C3D_GetTexEnv(1);
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PREVIOUS, GPU_PRIMARY_COLOR, 0);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_PREVIOUS, GPU_TEXTURE3, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
}
else
{
C3D_ProcTexBind(1, &ctx->ptBlend);
C3D_ProcTexLutBind(GPU_LUT_ALPHAMAP, &ctx->ptBlendLut);

// Set texenv1 to blend the output of texenv0 with the primary color
// texenv1.rgb = mix(texenv0.rgb, vtx.color.rgb, vtx.blend.y);
// texenv1.a = texenv0.a * vtx.color.a;
C3D_TexEnv* env = C3D_GetTexEnv(1);
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PREVIOUS, GPU_PRIMARY_COLOR, GPU_TEXTURE3);
C3D_TexEnvOpRgb(env, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_ONE_MINUS_SRC_ALPHA);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_PREVIOUS, GPU_PRIMARY_COLOR, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_INTERPOLATE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
}
}

ctx->flags &= ~C2DiF_DirtyAny;
}
39 changes: 30 additions & 9 deletions source/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ typedef struct
{
float pos[3];
float texcoord[2];
float blend[2];
float ptcoord[2];
u32 color;
} C2Di_Vertex;

Expand All @@ -16,7 +16,9 @@ typedef struct
C3D_AttrInfo attrInfo;
C3D_BufInfo bufInfo;
C3D_ProcTex ptBlend;
C3D_ProcTex ptCircle;
C3D_ProcTexLut ptBlendLut;
C3D_ProcTexLut ptCircleLut;
u32 sceneW, sceneH;

C2Di_Vertex* vtxBuf;
Expand All @@ -33,18 +35,21 @@ typedef struct

enum
{
C2DiF_Active = BIT(0),
C2DiF_DirtyProj = BIT(1),
C2DiF_DirtyMdlv = BIT(2),
C2DiF_DirtyTex = BIT(3),
C2DiF_DirtySrc = BIT(4),
C2DiF_DirtyFade = BIT(5),
C2DiF_Active = BIT(0),
C2DiF_DirtyProj = BIT(1),
C2DiF_DirtyMdlv = BIT(2),
C2DiF_DirtyTex = BIT(3),
C2DiF_DirtySrc = BIT(4),
C2DiF_DirtyFade = BIT(5),
C2DiF_DirtyProcTex = BIT(6),

C2DiF_Src_None = 0,
C2DiF_Src_Tex = BIT(31),
C2DiF_Src_Mask = BIT(31),

C2DiF_DirtyAny = C2DiF_DirtyProj | C2DiF_DirtyMdlv | C2DiF_DirtyTex | C2DiF_DirtySrc | C2DiF_DirtyFade,
C2DiF_ProcTex_Circle = BIT(30),

C2DiF_DirtyAny = C2DiF_DirtyProj | C2DiF_DirtyMdlv | C2DiF_DirtyTex | C2DiF_DirtySrc | C2DiF_DirtyFade | C2DiF_DirtyProcTex,
};

static inline C2Di_Context* C2Di_GetContext(void)
Expand Down Expand Up @@ -72,6 +77,22 @@ static inline void C2Di_SetTex(C3D_Tex* tex)
}
}

static inline void C2Di_SetCircle(bool iscircle)
{
C2Di_Context* ctx = C2Di_GetContext();
if (iscircle && !(ctx->flags & C2DiF_ProcTex_Circle))
{
ctx->flags |= C2DiF_DirtyProcTex;
ctx->flags |= C2DiF_ProcTex_Circle;
}
else if (!iscircle && (ctx->flags & C2DiF_ProcTex_Circle))
{
ctx->flags |= C2DiF_DirtyProcTex;
ctx->flags &= ~C2DiF_ProcTex_Circle;
}

}

typedef struct
{
float topLeft[2];
Expand All @@ -81,6 +102,6 @@ typedef struct
} C2Di_Quad;

void C2Di_CalcQuad(C2Di_Quad* quad, const C2D_DrawParams* params);
void C2Di_AppendVtx(float x, float y, float z, float u, float v, float blend, u32 color);
void C2Di_AppendVtx(float x, float y, float z, float u, float v, float ptx, float pty, u32 color);
void C2Di_FlushVtxBuf(void);
void C2Di_Update(void);
10 changes: 5 additions & 5 deletions source/render2d.v.pica
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
.out oColor color

; Inputs
.in iPosition v0
.in iTexCoord v1
.in iBlend v2
.in iColor v3
.in iPosition v0
.in iTexCoord v1
.in iProcTexCoord v2
.in iColor v3

; Main procedure
.entry render2d_main
Expand All @@ -45,7 +45,7 @@
mov oTexCoord0, iTexCoord

; oTexCoord1 = iBlend
mov oTexCoord1, iBlend
mov oTexCoord1, iProcTexCoord

; oColor = iColor / 255
mul oColor, clrdiv, iColor
Expand Down
14 changes: 8 additions & 6 deletions source/text.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ void C2D_DrawText(const C2D_Text* text, u32 flags, float x, float y, float z, fl

va_end(va);

C2Di_SetCircle(false);

for (cur = begin; cur != end; ++cur)
{
float glyphW = scaleX*cur->width;
Expand All @@ -224,11 +226,11 @@ void C2D_DrawText(const C2D_Text* text, u32 flags, float x, float y, float z, fl

C2Di_SetTex(cur->sheet);
C2Di_Update();
C2Di_AppendVtx(glyphX, glyphY, glyphZ, cur->texcoord.left, cur->texcoord.top, 1.0f, color);
C2Di_AppendVtx(glyphX, glyphY+glyphH, glyphZ, cur->texcoord.left, cur->texcoord.bottom, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY, glyphZ, cur->texcoord.right, cur->texcoord.top, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY, glyphZ, cur->texcoord.right, cur->texcoord.top, 1.0f, color);
C2Di_AppendVtx(glyphX, glyphY+glyphH, glyphZ, cur->texcoord.left, cur->texcoord.bottom, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY+glyphH, glyphZ, cur->texcoord.right, cur->texcoord.bottom, 1.0f, color);
C2Di_AppendVtx(glyphX, glyphY, glyphZ, cur->texcoord.left, cur->texcoord.top, 0.0f, 1.0f, color);
C2Di_AppendVtx(glyphX, glyphY+glyphH, glyphZ, cur->texcoord.left, cur->texcoord.bottom, 0.0f, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY, glyphZ, cur->texcoord.right, cur->texcoord.top, 0.0f, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY, glyphZ, cur->texcoord.right, cur->texcoord.top, 0.0f, 1.0f, color);
C2Di_AppendVtx(glyphX, glyphY+glyphH, glyphZ, cur->texcoord.left, cur->texcoord.bottom, 0.0f, 1.0f, color);
C2Di_AppendVtx(glyphX+glyphW, glyphY+glyphH, glyphZ, cur->texcoord.right, cur->texcoord.bottom, 0.0f, 1.0f, color);
}
}

0 comments on commit 9a26392

Please sign in to comment.