Prepare triangle rendering for zbuffer support
This commit is contained in:
parent
e922efb897
commit
d42d6ef5bf
@ -52,8 +52,9 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read))
|
||||
return false; // TODO(doyle): Logging
|
||||
|
||||
// TODO(doyle): Make arrays use given memory not malloc
|
||||
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
u8 *rawBytes = (u8 *)DqnMemStack_Allocate(&memory->transMemStack, file.size);
|
||||
u8 *rawBytes = (u8 *)DqnMemStack_Push(&memory->transMemStack, file.size);
|
||||
size_t bytesRead = api.FileRead(&file, rawBytes, file.size);
|
||||
size_t fileSize = file.size;
|
||||
api.FileClose(&file);
|
||||
@ -284,7 +285,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
DQN_ASSERT(obj->model.groupNameIndex + 1 < DQN_ARRAY_COUNT(obj->model.groupName));
|
||||
|
||||
DQN_ASSERT(!obj->model.groupName[obj->model.groupNameIndex]);
|
||||
obj->model.groupName[obj->model.groupNameIndex++] = (char *)DqnMemStack_Allocate(
|
||||
obj->model.groupName[obj->model.groupNameIndex++] = (char *)DqnMemStack_Push(
|
||||
&memory->permMemStack, (nameLen + 1) * sizeof(char));
|
||||
|
||||
for (i32 i = 0; i < nameLen; i++)
|
||||
@ -371,7 +372,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
|
||||
return false; // TODO(doyle): Logging
|
||||
|
||||
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
u8 *fontBuf = (u8 *)DqnMemStack_Allocate(&memory->transMemStack, file.size);
|
||||
u8 *fontBuf = (u8 *)DqnMemStack_Push(&memory->transMemStack, file.size);
|
||||
size_t bytesRead = api.FileRead(&file, fontBuf, file.size);
|
||||
api.FileClose(&file);
|
||||
if (bytesRead != file.size)
|
||||
@ -392,7 +393,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Pack font data to bitmap
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
loadedFont.bitmap = (u8 *)DqnMemStack_Allocate(
|
||||
loadedFont.bitmap = (u8 *)DqnMemStack_Push(
|
||||
&memory->permMemStack,
|
||||
(size_t)(loadedFont.bitmapDim.w * loadedFont.bitmapDim.h));
|
||||
|
||||
@ -405,7 +406,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
|
||||
i32 numCodepoints =
|
||||
(i32)((codepointRange.max + 1) - codepointRange.min);
|
||||
|
||||
loadedFont.atlas = (stbtt_packedchar *)DqnMemStack_Allocate(
|
||||
loadedFont.atlas = (stbtt_packedchar *)DqnMemStack_Push(
|
||||
&memory->permMemStack, numCodepoints * sizeof(stbtt_packedchar));
|
||||
stbtt_PackFontRange(&fontPackContext, fontBuf, 0,
|
||||
STBTT_POINT_SIZE(sizeInPt), (i32)codepointRange.min,
|
||||
@ -463,7 +464,7 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
DqnTempMemStack tempBuffer = DqnMemStack_BeginTempRegion(transMemStack);
|
||||
{
|
||||
u8 *const rawData =
|
||||
(u8 *)DqnMemStack_Allocate(transMemStack, file.size);
|
||||
(u8 *)DqnMemStack_Push(transMemStack, file.size);
|
||||
size_t bytesRead = api.FileRead(&file, rawData, file.size);
|
||||
api.FileClose(&file);
|
||||
|
||||
@ -511,7 +512,7 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
}
|
||||
|
||||
// #include <algorithm>
|
||||
void CompAssignment(PlatformRenderBuffer *const renderBuffer, PlatformInput *const input,
|
||||
void CompAssignment(DTRRenderBuffer *const renderBuffer, PlatformInput *const input,
|
||||
PlatformMemory *const memory)
|
||||
{
|
||||
#if 1
|
||||
@ -1441,10 +1442,13 @@ FILE_SCOPE void TestStrToF32Converter()
|
||||
DQN_ASSERT(DQN_ABS(vQ) - DQN_ABS(9.64635e-05) < EPSILON);
|
||||
}
|
||||
|
||||
extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer,
|
||||
extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
PlatformInput *const input,
|
||||
PlatformMemory *const memory)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Initialisation
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DTRState *state = (DTRState *)memory->context;
|
||||
if (input->executableReloaded)
|
||||
{
|
||||
@ -1463,7 +1467,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer,
|
||||
|
||||
memory->isInit = true;
|
||||
memory->context =
|
||||
DqnMemStack_Allocate(&memory->permMemStack, sizeof(DTRState));
|
||||
DqnMemStack_Push(&memory->permMemStack, sizeof(DTRState));
|
||||
DQN_ASSERT(memory->context);
|
||||
|
||||
state = (DTRState *)memory->context;
|
||||
@ -1473,126 +1477,149 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer,
|
||||
&memory->transMemStack);
|
||||
|
||||
DTRBitmap test = {};
|
||||
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->permMemStack);
|
||||
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
BitmapLoad(input->api, &test, "byte_read_check.bmp",
|
||||
&memory->transMemStack);
|
||||
DqnMemStack_EndTempRegion(tmp);
|
||||
|
||||
DQN_ASSERT(ObjWavefrontLoad(input->api, memory, "african_head.obj", &state->obj));
|
||||
}
|
||||
DTRRender_Clear(renderBuffer, DqnV3_3f(0.0f, 0.0f, 0.0f));
|
||||
|
||||
const DqnV3 LIGHT = DqnV3_3i(0, 0, -1);
|
||||
const f32 MODEL_SCALE = DQN_MIN(renderBuffer->width, renderBuffer->height) * 0.5f;
|
||||
WavefrontObj *const waveObj = &state->obj;
|
||||
DqnV2 modelP = DqnV2_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
||||
DqnTempMemStack transMemTmpRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
|
||||
for (i32 i = 0; i < waveObj->model.faces.count; i++)
|
||||
DTRRenderBuffer renderBuffer = {};
|
||||
renderBuffer.width = platformRenderBuffer->width;
|
||||
renderBuffer.height = platformRenderBuffer->height;
|
||||
renderBuffer.bytesPerPixel = platformRenderBuffer->bytesPerPixel;
|
||||
renderBuffer.memory = (u8 *)platformRenderBuffer->memory;
|
||||
renderBuffer.zBuffer = (f32 *)DqnMemStack_Push(
|
||||
&memory->transMemStack,
|
||||
platformRenderBuffer->width * platformRenderBuffer->height * sizeof(*renderBuffer.zBuffer));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Update and Render
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DTRRender_Clear(&renderBuffer, DqnV3_3f(0.0f, 0.0f, 0.0f));
|
||||
|
||||
#if 1
|
||||
DqnV4 colorRed = DqnV4_4f(0.8f, 0, 0, 1);
|
||||
DqnV2i bufferMidP = DqnV2i_2f(renderBuffer.width * 0.5f, renderBuffer.height * 0.5f);
|
||||
f32 rotation = (f32)input->timeNowInS * 0.25f;
|
||||
|
||||
// Triangle Drawing
|
||||
{
|
||||
WavefrontModelFace face = waveObj->model.faces.data[i];
|
||||
DQN_ASSERT(face.vertexArray.count == 3);
|
||||
i32 vertAIndex = face.vertexArray.data[0];
|
||||
i32 vertBIndex = face.vertexArray.data[1];
|
||||
i32 vertCIndex = face.vertexArray.data[2];
|
||||
DqnV4 redTransparent = DqnV4_4f(1, 0, 0, 0.5f);
|
||||
|
||||
DqnV4 vertA = waveObj->geometryArray.data[vertAIndex];
|
||||
DqnV4 vertB = waveObj->geometryArray.data[vertBIndex];
|
||||
DqnV4 vertC = waveObj->geometryArray.data[vertCIndex];
|
||||
i32 boundsOffset = 100;
|
||||
DqnV3 t0[3] = {DqnV3_3i(10, 70, 0), DqnV3_3i(50, 160, 0), DqnV3_3i(70, 80, 0)};
|
||||
DqnV3 t1[3] = {DqnV3_3i(180, 50, 0), DqnV3_3i(150, 1, 0), DqnV3_3i(70, 180, 0)};
|
||||
DqnV3 t2[3] = {DqnV3_3i(180, 150, 0), DqnV3_3i(120, 160, 0), DqnV3_3i(130, 180, 0)};
|
||||
DqnV3 t3[3] = {DqnV3_3i(boundsOffset, boundsOffset, 0),
|
||||
DqnV3_3i(bufferMidP.w, renderBuffer.height - boundsOffset, 0),
|
||||
DqnV3_3i(renderBuffer.width - boundsOffset, boundsOffset, 0)};
|
||||
DqnV3 t4[3] = {DqnV3_3i(100, 150, 0), DqnV3_3i(200, 150, 0), DqnV3_3i(200, 250, 0)};
|
||||
DqnV3 t5[3] = {DqnV3_3i(300, 150, 0), DqnV3_3i(201, 150, 0), DqnV3_3i(200, 250, 0)};
|
||||
|
||||
DqnV4 vertAB = vertB - vertA;
|
||||
DqnV4 vertAC = vertC - vertA;
|
||||
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
|
||||
DTRRenderTransform rotatingXform = DTRRender_DefaultTriangleTransform();
|
||||
rotatingXform.rotation = rotation;
|
||||
|
||||
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), LIGHT);
|
||||
if (intensity > 0)
|
||||
DTRRender_Triangle(&renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
||||
DTRRender_Triangle(&renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
||||
DTRRender_Triangle(&renderBuffer, t3[0], t3[1], t3[2], colorRed, rotatingXform);
|
||||
DTRRender_Triangle(&renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
||||
DTRRender_Triangle(&renderBuffer, t4[0], t4[1], t4[2], colorRed);
|
||||
DTRRender_Triangle(&renderBuffer, t5[0], t5[1], t5[2], colorRed);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Draw Loaded Model
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
const DqnV3 LIGHT = DqnV3_3i(0, 0, -1);
|
||||
const f32 MODEL_SCALE = DQN_MIN(renderBuffer.width, renderBuffer.height) * 0.5f;
|
||||
WavefrontObj *const waveObj = &state->obj;
|
||||
DqnV3 modelP = DqnV3_3f(renderBuffer.width * 0.5f, renderBuffer.height * 0.5f, 0);
|
||||
|
||||
for (i32 i = 0; i < waveObj->model.faces.count; i++)
|
||||
{
|
||||
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
|
||||
modelCol.rgb *= intensity;
|
||||
WavefrontModelFace face = waveObj->model.faces.data[i];
|
||||
DQN_ASSERT(face.vertexArray.count == 3);
|
||||
i32 vertAIndex = face.vertexArray.data[0];
|
||||
i32 vertBIndex = face.vertexArray.data[1];
|
||||
i32 vertCIndex = face.vertexArray.data[2];
|
||||
|
||||
DqnV2 screenVertA = (vertA.xy * MODEL_SCALE) + modelP;
|
||||
DqnV2 screenVertB = (vertB.xy * MODEL_SCALE) + modelP;
|
||||
DqnV2 screenVertC = (vertC.xy * MODEL_SCALE) + modelP;
|
||||
DqnV4 vertA = waveObj->geometryArray.data[vertAIndex];
|
||||
DqnV4 vertB = waveObj->geometryArray.data[vertBIndex];
|
||||
DqnV4 vertC = waveObj->geometryArray.data[vertCIndex];
|
||||
|
||||
// TODO(doyle): Why do we need rounding here? Maybe it's because
|
||||
// I don't do any interpolation in the triangle routine for jagged
|
||||
// edges.
|
||||
screenVertA.x = (f32)(i32)(screenVertA.x + 0.5f);
|
||||
screenVertA.y = (f32)(i32)(screenVertA.y + 0.5f);
|
||||
screenVertB.x = (f32)(i32)(screenVertB.x + 0.5f);
|
||||
screenVertB.y = (f32)(i32)(screenVertB.y + 0.5f);
|
||||
screenVertC.x = (f32)(i32)(screenVertC.x + 0.5f);
|
||||
screenVertC.y = (f32)(i32)(screenVertC.y + 0.5f);
|
||||
DqnV4 vertAB = vertB - vertA;
|
||||
DqnV4 vertAC = vertC - vertA;
|
||||
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
|
||||
|
||||
DTRRender_Triangle(renderBuffer, screenVertA, screenVertB, screenVertC, modelCol);
|
||||
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), LIGHT);
|
||||
if (intensity > 0)
|
||||
{
|
||||
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
|
||||
modelCol.rgb *= intensity;
|
||||
|
||||
DqnV3 screenVA = (vertA.xyz * MODEL_SCALE) + modelP;
|
||||
DqnV3 screenVB = (vertB.xyz * MODEL_SCALE) + modelP;
|
||||
DqnV3 screenVC = (vertC.xyz * MODEL_SCALE) + modelP;
|
||||
|
||||
// TODO(doyle): Why do we need rounding here? Maybe it's because
|
||||
// I don't do any interpolation in the triangle routine for jagged
|
||||
// edges.
|
||||
screenVA.x = (f32)(i32)(screenVA.x + 0.5f);
|
||||
screenVA.y = (f32)(i32)(screenVA.y + 0.5f);
|
||||
screenVB.x = (f32)(i32)(screenVB.x + 0.5f);
|
||||
screenVB.y = (f32)(i32)(screenVB.y + 0.5f);
|
||||
screenVC.x = (f32)(i32)(screenVC.x + 0.5f);
|
||||
screenVC.y = (f32)(i32)(screenVC.y + 0.5f);
|
||||
|
||||
DTRRender_Triangle(&renderBuffer, screenVA, screenVB, screenVC, modelCol);
|
||||
|
||||
#if 0
|
||||
DqnV4 wireColor = DqnV4_1f(1.0f);
|
||||
for (i32 j = 0; j < 3; j++)
|
||||
{
|
||||
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVertA), DqnV2i_V2(screenVertB), wireColor);
|
||||
}
|
||||
DqnV4 wireColor = DqnV4_1f(1.0f);
|
||||
for (i32 j = 0; j < 3; j++)
|
||||
{
|
||||
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVertA), DqnV2i_V2(screenVertB), wireColor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
DqnV4 colorRed = DqnV4_4f(0.8f, 0, 0, 1);
|
||||
DqnV2i bufferMidP =
|
||||
DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f);
|
||||
i32 boundsOffset = 100;
|
||||
|
||||
DqnV2 t0[3] = {DqnV2_2i(10, 70), DqnV2_2i(50, 160), DqnV2_2i(70, 80)};
|
||||
DqnV2 t1[3] = {DqnV2_2i(180, 50), DqnV2_2i(150, 1), DqnV2_2i(70, 180)};
|
||||
DqnV2 t2[3] = {DqnV2_2i(180, 150), DqnV2_2i(120, 160), DqnV2_2i(130, 180)};
|
||||
LOCAL_PERSIST DqnV2 t3[3] = {
|
||||
DqnV2_2i(boundsOffset, boundsOffset),
|
||||
DqnV2_2i(bufferMidP.w, renderBuffer->height - boundsOffset),
|
||||
DqnV2_2i(renderBuffer->width - boundsOffset, boundsOffset)};
|
||||
|
||||
DTRRender_Triangle(renderBuffer, t0[0], t0[1], t0[2], colorRed);
|
||||
DTRRender_Triangle(renderBuffer, t1[0], t1[1], t1[2], colorRed);
|
||||
DTRRender_Triangle(renderBuffer, t2[0], t2[1], t2[2], colorRed);
|
||||
|
||||
DqnV2 t4[3] = {DqnV2_2i(100, 150), DqnV2_2i(200, 150), DqnV2_2i(200, 250)};
|
||||
DqnV2 t5[3] = {DqnV2_2i(300, 150), DqnV2_2i(201, 150), DqnV2_2i(200, 250)};
|
||||
DTRRender_Triangle(renderBuffer, t4[0], t4[1], t4[2], colorRed);
|
||||
DTRRender_Triangle(renderBuffer, t5[0], t5[1], t5[2], colorRed);
|
||||
|
||||
DqnV4 colorRedHalfA = DqnV4_4f(1, 0, 0, 0.1f);
|
||||
LOCAL_PERSIST f32 rotation = 0;
|
||||
rotation += input->deltaForFrame * 0.25f;
|
||||
|
||||
DTRRenderTransform defaultTransform = DTRRender_DefaultTransform();
|
||||
defaultTransform.rotation = rotation + 45;
|
||||
|
||||
DTRRender_Rectangle(renderBuffer, DqnV2_1f(300.0f), DqnV2_1f(300 + 100.0f), DqnV4_4f(0, 1.0f, 1.0f, 1.0f),
|
||||
defaultTransform);
|
||||
|
||||
// Rotating triangle
|
||||
// Rect drawing
|
||||
{
|
||||
DTRRenderTransform triTransform = DTRRender_DefaultTriangleTransform();
|
||||
triTransform.rotation = rotation;
|
||||
DTRRender_Triangle(renderBuffer, t3[0], t3[1], t3[2], colorRedHalfA, triTransform);
|
||||
DTRRenderTransform transform = DTRRender_DefaultTransform();
|
||||
transform.rotation = rotation + 45;
|
||||
|
||||
DTRRender_Rectangle(&renderBuffer, DqnV2_1f(300.0f), DqnV2_1f(300 + 100.0f),
|
||||
DqnV4_4f(0, 1.0f, 1.0f, 1.0f), transform);
|
||||
}
|
||||
|
||||
// Bitmap drawing
|
||||
{
|
||||
DTRRenderTransform transform = DTRRender_DefaultTransform();
|
||||
transform.scale = DqnV2_1f(2.0f);
|
||||
|
||||
DqnV2 fontP = DqnV2_2i(200, 180);
|
||||
DTRRender_Text(renderBuffer, state->font, fontP, "hello world!", DqnV4_4f(0, 0, 0, 1));
|
||||
LOCAL_PERSIST DqnV2 bitmapP = DqnV2_2f(300, 250);
|
||||
bitmapP.x += 3.0f * sinf((f32)input->timeNowInS * 0.5f);
|
||||
|
||||
DTRRenderTransform transform = DTRRender_DefaultTransform();
|
||||
transform.rotation = 0;
|
||||
transform.scale = DqnV2_1f(2.0f);
|
||||
|
||||
LOCAL_PERSIST DqnV2 bitmapP = DqnV2_2f(300, 250);
|
||||
bitmapP.x += 3.0f * sinf((f32)input->timeNowInS * 0.5f);
|
||||
|
||||
f32 cAngle = (f32)input->timeNowInS;
|
||||
DqnV4 color = DqnV4_4f(0.5f + 0.5f * sinf(cAngle), 0.5f + 0.5f * sinf(2.9f * cAngle),
|
||||
0.5f + 0.5f * cosf(10.0f * cAngle), 1.0f);
|
||||
DTRRender_Bitmap(renderBuffer, &state->bitmap, bitmapP, transform, color);
|
||||
f32 cAngle = (f32)input->timeNowInS;
|
||||
DqnV4 color = DqnV4_4f(0.5f + 0.5f * sinf(cAngle), 0.5f + 0.5f * sinf(2.9f * cAngle),
|
||||
0.5f + 0.5f * cosf(10.0f * cAngle), 1.0f);
|
||||
DTRRender_Bitmap(&renderBuffer, &state->bitmap, bitmapP, transform, color);
|
||||
}
|
||||
|
||||
#else
|
||||
// CompAssignment(renderBuffer, input, memory);
|
||||
#endif
|
||||
DTRDebug_Update(state, renderBuffer, input, memory);
|
||||
|
||||
DTRDebug_Update(state, &renderBuffer, input, memory);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// End Update
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnMemStack_EndTempRegion(transMemTmpRegion);
|
||||
DQN_ASSERT(memory->transMemStack.tempStackCount == 0);
|
||||
DQN_ASSERT(memory->permMemStack.tempStackCount == 0);
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ FILE_SCOPE void PushMemStackText(const char *const name,
|
||||
size_t totalWastedKb = totalWasted / 1024;
|
||||
|
||||
char str[128] = {};
|
||||
Dqn_sprintf(str, "%s: %d block(s): %_$lld/%_$lld", name, numBlocks,
|
||||
totalUsed, totalSize);
|
||||
Dqn_sprintf(str, "%s: %d block(s): %_$lld/%_$lld: wasted: %_$lld", name, numBlocks,
|
||||
totalUsed, totalSize, totalWastedKb);
|
||||
|
||||
DTRRender_Text(globalDebug.renderBuffer, *globalDebug.font,
|
||||
globalDebug.displayP, str, globalDebug.displayColor);
|
||||
@ -64,7 +64,7 @@ FILE_SCOPE void PushMemStackText(const char *const name,
|
||||
}
|
||||
|
||||
void DTRDebug_Update(DTRState *const state,
|
||||
PlatformRenderBuffer *const renderBuffer,
|
||||
DTRRenderBuffer *const renderBuffer,
|
||||
PlatformInput *const input, PlatformMemory *const memory)
|
||||
{
|
||||
if (DTR_DEBUG)
|
||||
@ -80,11 +80,13 @@ void DTRDebug_Update(DTRState *const state,
|
||||
DQN_ASSERT(globalDebug.displayYOffset < 0);
|
||||
}
|
||||
|
||||
debug->totalSetPixels += debug->setPixelsPerFrame;
|
||||
debug->totalSetPixels += debug->counter[DTRDebugCounter_SetPixels];
|
||||
debug->totalSetPixels = DQN_MAX(0, debug->totalSetPixels);
|
||||
|
||||
DTRDebug_PushText("TotalSetPixels: %'lld", debug->totalSetPixels);
|
||||
DTRDebug_PushText("SetPixelsPerFrame: %'lld", debug->setPixelsPerFrame);
|
||||
DTRDebug_PushText("SetPixelsPerFrame: %'lld", debug->counter[DTRDebugCounter_SetPixels]);
|
||||
DTRDebug_PushText("TrianglesRendered: %'lld", debug->counter[DTRDebugCounter_RenderTriangle]);
|
||||
DTRDebug_PushText("");
|
||||
|
||||
// memory
|
||||
{
|
||||
@ -94,9 +96,11 @@ void DTRDebug_Update(DTRState *const state,
|
||||
|
||||
DTRDebug_PushText("SSE2Support: %s", (input->canUseSSE2) ? "true" : "false");
|
||||
|
||||
debug->setPixelsPerFrame = 0;
|
||||
debug->displayP =
|
||||
DqnV2_2i(0, debug->renderBuffer->height + globalDebug.displayYOffset);
|
||||
|
||||
for (i32 i = 0; i < DQN_ARRAY_COUNT(debug->counter); i++)
|
||||
debug->counter[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,22 +30,29 @@
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct PlatformRenderBuffer PlatformRenderBuffer;
|
||||
typedef struct DTRFont DTRFont;
|
||||
typedef struct DTRState DTRState;
|
||||
typedef struct PlatformInput PlatformInput;
|
||||
typedef struct PlatformMemory PlatformMemory;
|
||||
typedef struct DTRRenderBuffer DTRRenderBuffer;
|
||||
typedef struct DTRFont DTRFont;
|
||||
typedef struct DTRState DTRState;
|
||||
typedef struct PlatformInput PlatformInput;
|
||||
typedef struct PlatformMemory PlatformMemory;
|
||||
|
||||
enum DTRDebugCounter
|
||||
{
|
||||
DTRDebugCounter_SetPixels,
|
||||
DTRDebugCounter_RenderTriangle,
|
||||
DTRDebugCounter_Count,
|
||||
};
|
||||
|
||||
typedef struct DTRDebug
|
||||
{
|
||||
DTRFont *font;
|
||||
PlatformRenderBuffer *renderBuffer;
|
||||
DTRFont *font;
|
||||
DTRRenderBuffer *renderBuffer;
|
||||
|
||||
DqnV4 displayColor;
|
||||
DqnV2 displayP;
|
||||
i32 displayYOffset;
|
||||
|
||||
u64 setPixelsPerFrame;
|
||||
u64 counter[DTRDebugCounter_Count];
|
||||
u64 totalSetPixels;
|
||||
} DTRDebug;
|
||||
|
||||
@ -53,6 +60,16 @@ extern DTRDebug globalDebug;
|
||||
|
||||
void DTRDebug_PushText(const char *const formatStr, ...);
|
||||
void DTRDebug_Update(DTRState *const state,
|
||||
PlatformRenderBuffer *const renderBuffer,
|
||||
DTRRenderBuffer *const renderBuffer,
|
||||
PlatformInput *const input, PlatformMemory *const memory);
|
||||
|
||||
void inline DTRDebug_CounterIncrement(enum DTRDebugCounter tag)
|
||||
{
|
||||
if (DTR_DEBUG)
|
||||
{
|
||||
DQN_ASSERT(tag >= 0 && tag < DTRDebugCounter_Count);
|
||||
globalDebug.counter[tag]++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -99,7 +99,7 @@ inline DqnV4 DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(DqnV4 color)
|
||||
}
|
||||
|
||||
// IMPORTANT(doyle): Color is expected to be premultiplied already
|
||||
FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const i32 x, const i32 y,
|
||||
FILE_SCOPE inline void SetPixel(DTRRenderBuffer *const renderBuffer, const i32 x, const i32 y,
|
||||
DqnV4 color, const enum ColorSpace colorSpace = ColorSpace_SRGB)
|
||||
{
|
||||
if (!renderBuffer) return;
|
||||
@ -166,10 +166,10 @@ FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const
|
||||
(u32)(destB) << 0;
|
||||
bitmapPtr[x + (y * pitchInU32)] = pixel;
|
||||
|
||||
globalDebug.setPixelsPerFrame++;
|
||||
DTRDebug_CounterIncrement(DTRDebugCounter_SetPixels);
|
||||
}
|
||||
|
||||
void DTRRender_Text(PlatformRenderBuffer *const renderBuffer,
|
||||
void DTRRender_Text(DTRRenderBuffer *const renderBuffer,
|
||||
const DTRFont font, DqnV2 pos, const char *const text,
|
||||
DqnV4 color, i32 len)
|
||||
{
|
||||
@ -267,7 +267,7 @@ FILE_SCOPE void TransformPoints(const DqnV2 origin, DqnV2 *const pList,
|
||||
}
|
||||
}
|
||||
|
||||
void DTRRender_Line(PlatformRenderBuffer *const renderBuffer, DqnV2i a,
|
||||
void DTRRender_Line(DTRRenderBuffer *const renderBuffer, DqnV2i a,
|
||||
DqnV2i b, DqnV4 color)
|
||||
{
|
||||
if (!renderBuffer) return;
|
||||
@ -387,7 +387,7 @@ FILE_SCOPE DqnRect GetBoundingBox(const DqnV2 *const pList, const i32 numP)
|
||||
return result;
|
||||
}
|
||||
|
||||
void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max,
|
||||
void DTRRender_Rectangle(DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max,
|
||||
DqnV4 color, const DTRRenderTransform transform)
|
||||
{
|
||||
DTR_DEBUG_TIMED_FUNCTION();
|
||||
@ -484,7 +484,7 @@ void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, Dq
|
||||
}
|
||||
}
|
||||
|
||||
void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV2 p2, DqnV2 p3,
|
||||
void DTRRender_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3,
|
||||
DqnV4 color, const DTRRenderTransform transform)
|
||||
{
|
||||
DTR_DEBUG_TIMED_FUNCTION();
|
||||
@ -492,17 +492,18 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Transform vertexes
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnV2 p1p2 = p2 - p1;
|
||||
DqnV2 p1p3 = p3 - p1;
|
||||
DqnV2 p1p2Anchored = p1p2 * transform.anchor;
|
||||
DqnV2 p1p3Anchored = p1p3 * transform.anchor;
|
||||
DqnV3 p1p2 = p2 - p1;
|
||||
DqnV3 p1p3 = p3 - p1;
|
||||
|
||||
DqnV2 origin = p1 + p1p2Anchored + p1p3Anchored;
|
||||
DqnV2 pList[3] = {p1 - origin, p2 - origin, p3 - origin};
|
||||
// TODO(doyle): Transform is only in 2d right now
|
||||
DqnV2 p1p2Anchored = p1p2.xy * transform.anchor;
|
||||
DqnV2 p1p3Anchored = p1p3.xy * transform.anchor;
|
||||
DqnV2 origin = p1.xy + p1p2Anchored + p1p3Anchored;
|
||||
DqnV2 pList[3] = {p1.xy - origin, p2.xy - origin, p3.xy - origin};
|
||||
TransformPoints(origin, pList, DQN_ARRAY_COUNT(pList), transform.scale, transform.rotation);
|
||||
p1 = pList[0];
|
||||
p2 = pList[1];
|
||||
p3 = pList[2];
|
||||
p1.xy = pList[0];
|
||||
p2.xy = pList[1];
|
||||
p3.xy = pList[2];
|
||||
|
||||
color = DTRRender_SRGB1ToLinearSpaceV4(color);
|
||||
color = PreMultiplyAlpha1(color);
|
||||
@ -625,12 +626,12 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
|
||||
if (area2Times > 0)
|
||||
{
|
||||
// Clockwise swap any point to make it clockwise
|
||||
DQN_SWAP(DqnV2, p2, p3);
|
||||
DQN_SWAP(DqnV3, p2, p3);
|
||||
}
|
||||
|
||||
const DqnV2 a = p1;
|
||||
const DqnV2 b = p2;
|
||||
const DqnV2 c = p3;
|
||||
const DqnV3 a = p1;
|
||||
const DqnV3 b = p2;
|
||||
const DqnV3 c = p3;
|
||||
|
||||
DqnV2i startP = min;
|
||||
f32 signedArea1 = ((b.x - a.x) * (startP.y - a.y)) - ((b.y - a.y) * (startP.x - a.x));
|
||||
@ -678,6 +679,7 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Debug
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DTRDebug_CounterIncrement(DTRDebugCounter_RenderTriangle);
|
||||
if (DTR_DEBUG_RENDER)
|
||||
{
|
||||
// Draw Bounding box
|
||||
@ -704,14 +706,14 @@ void DTRRender_Triangle(PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV
|
||||
DqnV4 blue = DqnV4_4f(0, 0, 1, 1);
|
||||
DqnV4 purple = DqnV4_4f(1, 0, 1, 1);
|
||||
|
||||
DTRRender_Rectangle(renderBuffer, p1 - DqnV2_1f(5), p1 + DqnV2_1f(5), green);
|
||||
DTRRender_Rectangle(renderBuffer, p2 - DqnV2_1f(5), p2 + DqnV2_1f(5), blue);
|
||||
DTRRender_Rectangle(renderBuffer, p3 - DqnV2_1f(5), p3 + DqnV2_1f(5), purple);
|
||||
DTRRender_Rectangle(renderBuffer, p1.xy - DqnV2_1f(5), p1.xy + DqnV2_1f(5), green);
|
||||
DTRRender_Rectangle(renderBuffer, p2.xy - DqnV2_1f(5), p2.xy + DqnV2_1f(5), blue);
|
||||
DTRRender_Rectangle(renderBuffer, p3.xy - DqnV2_1f(5), p3.xy + DqnV2_1f(5), purple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
|
||||
void DTRRender_Bitmap(DTRRenderBuffer *const renderBuffer,
|
||||
DTRBitmap *const bitmap, DqnV2 pos,
|
||||
const DTRRenderTransform transform, DqnV4 color)
|
||||
{
|
||||
@ -723,7 +725,6 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnV2 min = pos;
|
||||
DqnV2 max = min + DqnV2_V2i(bitmap->dim);
|
||||
DTRDebug_PushText("OldRect: (%5.2f, %5.2f), (%5.2f, %5.2f)", min.x, min.y, max.x, max.y);
|
||||
|
||||
RectPoints rectPoints = TransformRectPoints(min, max, transform);
|
||||
const DqnV2 *const pList = &rectPoints.pList[0];
|
||||
@ -748,16 +749,12 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
|
||||
|
||||
DqnRect clippedDrawRect = DqnRect_ClipRect(drawRect, clip);
|
||||
DqnV2 clippedSize = DqnRect_GetSizeV2(clippedDrawRect);
|
||||
|
||||
DTRDebug_PushText("ClippedRect: (%5.2f, %5.2f), (%5.2f, %5.2f)", clippedDrawRect.min.x, clippedDrawRect.min.y, clippedDrawRect.max.x, clippedDrawRect.max.y);
|
||||
DTRDebug_PushText("ClippedSize: (%5.2f, %5.2f)", clippedSize.w, clippedSize.h);
|
||||
DTRDebug_PushText("DrawRect: (%5.2f, %5.2f), (%5.2f, %5.2f)", drawRect.min.x, drawRect.min.y, drawRect.max.x, drawRect.max.y);
|
||||
const i32 pitch = bitmap->dim.w * bitmap->bytesPerPixel;
|
||||
u8 *const bitmapPtr = (u8 *)bitmap->memory;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Setup Texture Mapping
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
const i32 pitch = bitmap->dim.w * bitmap->bytesPerPixel;
|
||||
u8 *const bitmapPtr = (u8 *)bitmap->memory;
|
||||
|
||||
const DqnV2 rectBasis = pList[RectPointsIndex_Basis];
|
||||
const DqnV2 xAxisRelToBasis = pList[RectPointsIndex_XAxis] - rectBasis;
|
||||
const DqnV2 yAxisRelToBasis = pList[RectPointsIndex_YAxis] - rectBasis;
|
||||
@ -945,7 +942,7 @@ void DTRRender_Bitmap(PlatformRenderBuffer *const renderBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
void DTRRender_Clear(PlatformRenderBuffer *const renderBuffer,
|
||||
void DTRRender_Clear(DTRRenderBuffer *const renderBuffer,
|
||||
DqnV3 color)
|
||||
{
|
||||
if (!renderBuffer) return;
|
||||
|
@ -5,12 +5,23 @@
|
||||
|
||||
#define DTRRENDER_INV_255 1.0f/255.0f
|
||||
|
||||
typedef struct PlatformRenderBuffer PlatformRenderBuffer;
|
||||
typedef struct DTRRenderBuffer DTRRenderBuffer;
|
||||
typedef struct DTRBitmap DTRBitmap;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct DTRRenderBuffer
|
||||
{
|
||||
i32 width;
|
||||
i32 height;
|
||||
i32 bytesPerPixel;
|
||||
|
||||
u8 *memory; // Format: XX RR GG BB, and has (width * height * bytesPerPixels) elements
|
||||
f32 *zBuffer; // zBuffer has (width * height) elements
|
||||
|
||||
} DTRRenderBuffer;
|
||||
|
||||
typedef struct DTRRenderTransform
|
||||
{
|
||||
f32 rotation = 0; // Rotation in degrees
|
||||
@ -48,16 +59,11 @@ inline DqnV4 DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(DqnV4 color);
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: All colors should be in the range of [0->1] where DqnV4 is a struct with 4 floats, rgba
|
||||
// Leaving len = -1 for text will make the system use strlen to determine len.
|
||||
void DTRRender_Text (PlatformRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1);
|
||||
|
||||
void DTRRender_Line (PlatformRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color);
|
||||
|
||||
void DTRRender_Rectangle(PlatformRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform());
|
||||
|
||||
void DTRRender_Triangle (PlatformRenderBuffer *const renderBuffer, DqnV2 p1, DqnV2 p2, DqnV2 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform());
|
||||
|
||||
void DTRRender_Bitmap (PlatformRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1));
|
||||
|
||||
void DTRRender_Clear (PlatformRenderBuffer *const renderBuffer, DqnV3 color);
|
||||
void DTRRender_Text (DTRRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1);
|
||||
void DTRRender_Line (DTRRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color);
|
||||
void DTRRender_Rectangle(DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform());
|
||||
void DTRRender_Triangle (DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform());
|
||||
void DTRRender_Bitmap (DTRRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1));
|
||||
void DTRRender_Clear (DTRRenderBuffer *const renderBuffer, DqnV3 color);
|
||||
|
||||
#endif
|
||||
|
@ -502,8 +502,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Platform Data Pre-amble
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DQN_ASSERT(DqnMemStack_Init(&globalPlatformMemory.permMemStack, DQN_MEGABYTE(1), true, 4) &&
|
||||
DqnMemStack_Init(&globalPlatformMemory.transMemStack, DQN_MEGABYTE(1), true, 4));
|
||||
DQN_ASSERT(DqnMemStack_Init(&globalPlatformMemory.permMemStack, DQN_MEGABYTE(4), true, 4) &&
|
||||
DqnMemStack_Init(&globalPlatformMemory.transMemStack, DQN_MEGABYTE(4), true, 4));
|
||||
|
||||
PlatformAPI platformAPI = {};
|
||||
platformAPI.FileOpen = Platform_FileOpen;
|
||||
|
18
src/dqn.h
18
src/dqn.h
@ -75,7 +75,7 @@ DQN_FILE_SCOPE void DqnMem_Free (void *memory);
|
||||
|
||||
// When an allocation requires a larger amount of memory than available in the
|
||||
// block then the MemStack will allocate a new block of sufficient size for
|
||||
// you in DqnMemStack_Allocate(..). This _DOES_ mean that there will be wasted
|
||||
// you in DqnMemStack_Push(..). This _DOES_ mean that there will be wasted
|
||||
// space at the end of each block and is a tradeoff for memory locality against
|
||||
// optimal space usage.
|
||||
|
||||
@ -88,7 +88,7 @@ DQN_FILE_SCOPE void DqnMem_Free (void *memory);
|
||||
// - InitWithFixedSize() allows you to to disable dynamic allocations and
|
||||
// sub-allocate from the initial MemStack allocation size only.
|
||||
|
||||
// 2. Use DqnMemStack_Allocate(..) to allocate memory for use.
|
||||
// 2. Use DqnMemStack_Push(..) to allocate memory for use.
|
||||
// - "Freeing" memory is dealt by creating temporary MemStacks or using the
|
||||
// BeginTempRegion and EndTempRegion functions. Specifically freeing
|
||||
// individual items is typically not generalisable in this scheme.
|
||||
@ -129,12 +129,12 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem (DqnMemStack *const stack, u8 *
|
||||
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out
|
||||
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Allocate (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack
|
||||
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock (DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
|
||||
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock (DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack
|
||||
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
|
||||
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0
|
||||
|
||||
// TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack.
|
||||
// Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original
|
||||
@ -1453,7 +1453,7 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack,
|
||||
return false;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Allocate(DqnMemStack *const stack, size_t size)
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size)
|
||||
{
|
||||
if (!stack || size == 0) return NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user