Fix zbuffer bug clipping at poly's behind 0, 0
Mistakenly use FLT_MIN as the largest negative number possible, where we should be using -FLT_MAX.
This commit is contained in:
parent
a03de5de80
commit
4eb54c09b1
@ -6,17 +6,60 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "external/stb_image.h"
|
||||
|
||||
// #define DTR_DEBUG_RENDER_FONT_BITMAP
|
||||
#ifdef DTR_DEBUG_RENDER_FONT_BITMAP
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "external/tests/stb_image_write.h"
|
||||
#endif
|
||||
|
||||
#define DQN_IMPLEMENTATION
|
||||
#include "dqn.h"
|
||||
|
||||
#include "external/tests/tinyrenderer/geometry.h"
|
||||
#include "external/tests/tinyrenderer/model.cpp"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
FILE_SCOPE void DebugTestWavefrontFaceAndVertexParser(WavefrontObj *const obj)
|
||||
{
|
||||
if (!obj) DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
||||
Model model = Model("african_head.obj");
|
||||
|
||||
DQN_ASSERT(obj->model.faces.count == model.nfaces());
|
||||
for (i32 i = 0; i < model.nfaces(); i++)
|
||||
{
|
||||
std::vector<i32> correctFace = model.face(i);
|
||||
WavefrontModelFace *myFace = &obj->model.faces.data[i];
|
||||
|
||||
DQN_ASSERT(myFace->vertexIndexArray.count == correctFace.size());
|
||||
|
||||
for (i32 j = 0; j < myFace->vertexIndexArray.count; j++)
|
||||
{
|
||||
// Ensure the vertex index references are correct per face
|
||||
DQN_ASSERT(myFace->vertexIndexArray.data[j] == correctFace[j]);
|
||||
|
||||
Vec3f tmp = model.vert(correctFace[j]);
|
||||
DqnV3 correctVertex = DqnV3_3f(tmp[0], tmp[1], tmp[2]);
|
||||
DqnV3 myVertex = (obj->geometryArray.data[myFace->vertexIndexArray.data[j]]).xyz;
|
||||
|
||||
// Ensure the vertex values read are correct
|
||||
for (i32 k = 0; k < DQN_ARRAY_COUNT(correctVertex.e); k++)
|
||||
{
|
||||
f32 delta = DQN_ABS(correctVertex.e[k] - myVertex.e[k]);
|
||||
DQN_ASSERT(delta < 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
|
||||
FILE_SCOPE inline WavefrontModelFace ObjWavefrontModelFaceInit(i32 capacity = 3)
|
||||
{
|
||||
WavefrontModelFace result = {};
|
||||
DQN_ASSERT(DqnArray_Init(&result.vertexArray, capacity));
|
||||
DQN_ASSERT(DqnArray_Init(&result.textureArray, capacity));
|
||||
DQN_ASSERT(DqnArray_Init(&result.normalArray, capacity));
|
||||
DQN_ASSERT(DqnArray_Init(&result.vertexIndexArray, capacity));
|
||||
DQN_ASSERT(DqnArray_Init(&result.textureIndexArray, capacity));
|
||||
DQN_ASSERT(DqnArray_Init(&result.normalIndexArray, capacity));
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -28,14 +71,14 @@ FILE_SCOPE bool ObjWaveFrontInit(WavefrontObj *const obj, const i32 vertexInitCa
|
||||
bool initialised = false;
|
||||
|
||||
initialised |= DqnArray_Init(&obj->geometryArray, vertexInitCapacity);
|
||||
initialised |= DqnArray_Init(&obj->texUVArray, vertexInitCapacity);
|
||||
initialised |= DqnArray_Init(&obj->textureArray, vertexInitCapacity);
|
||||
initialised |= DqnArray_Init(&obj->normalArray, vertexInitCapacity);
|
||||
initialised |= DqnArray_Init(&obj->model.faces, faceInitCapacity);
|
||||
|
||||
if (!initialised)
|
||||
{
|
||||
DqnArray_Free(&obj->geometryArray);
|
||||
DqnArray_Free(&obj->texUVArray);
|
||||
DqnArray_Free(&obj->textureArray);
|
||||
DqnArray_Free(&obj->normalArray);
|
||||
DqnArray_Free(&obj->model.faces);
|
||||
}
|
||||
@ -49,7 +92,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
if (!memory || !path || !obj) return false;
|
||||
|
||||
PlatformFile file = {};
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read))
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
|
||||
return false; // TODO(doyle): Logging
|
||||
|
||||
// TODO(doyle): Make arrays use given memory not malloc
|
||||
@ -121,7 +164,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
}
|
||||
|
||||
i32 f32Len = (i32)((size_t)scan - (size_t)f32StartPtr);
|
||||
v4.e[vIndex++] = (f32)atof(f32StartPtr); // Dqn_StrToF32(f32StartPtr, f32Len);
|
||||
v4.e[vIndex++] = Dqn_StrToF32(f32StartPtr, f32Len);
|
||||
DQN_ASSERT(vIndex < DQN_ARRAY_COUNT(v4.e));
|
||||
|
||||
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
|
||||
@ -137,7 +180,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
}
|
||||
else if (type == WavefrontVertexType_Texture)
|
||||
{
|
||||
DqnArray_Push(&obj->texUVArray, v4.xyz);
|
||||
DqnArray_Push(&obj->textureArray, v4.xyz);
|
||||
}
|
||||
else if (type == WavefrontVertexType_Normal)
|
||||
{
|
||||
@ -214,19 +257,19 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
{
|
||||
// NOTE(doyle): Obj format starts indexing from 1,
|
||||
// so offset by -1 to make it zero-based indexes.
|
||||
i32 index = (i32)Dqn_StrToI64(numStartPtr, numLen) - 1;
|
||||
i32 vertIndex = (i32)Dqn_StrToI64(numStartPtr, numLen) - 1;
|
||||
|
||||
if (type == WavefrontVertexType_Geometric)
|
||||
{
|
||||
DQN_ASSERT(DqnArray_Push(&face.vertexArray, index));
|
||||
DQN_ASSERT(DqnArray_Push(&face.vertexIndexArray, vertIndex));
|
||||
}
|
||||
else if (type == WavefrontVertexType_Texture)
|
||||
{
|
||||
DQN_ASSERT(DqnArray_Push(&face.textureArray, index));
|
||||
DQN_ASSERT(DqnArray_Push(&face.textureIndexArray, vertIndex));
|
||||
}
|
||||
else if (type == WavefrontVertexType_Normal)
|
||||
{
|
||||
DQN_ASSERT(DqnArray_Push(&face.normalArray, index));
|
||||
DQN_ASSERT(DqnArray_Push(&face.normalIndexArray, vertIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,11 +291,6 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me
|
||||
moreVertexesToParse = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->model.faces.count == 7470 || obj->model.faces.count == 2491)
|
||||
{
|
||||
int x = 5;
|
||||
}
|
||||
}
|
||||
DQN_ASSERT(numVertexesParsed >= 3);
|
||||
DQN_ASSERT(DqnArray_Push(&obj->model.faces, face));
|
||||
@ -368,7 +406,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
|
||||
// Load font data
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
PlatformFile file = {};
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read))
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
|
||||
return false; // TODO(doyle): Logging
|
||||
|
||||
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
@ -451,6 +489,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api,
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(doyle): Uses malloc
|
||||
FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
const char *const path,
|
||||
DqnMemStack *const transMemStack)
|
||||
@ -458,7 +497,7 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
if (!bitmap) return false;
|
||||
|
||||
PlatformFile file = {};
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read))
|
||||
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
|
||||
return false;
|
||||
|
||||
DqnTempMemStack tempBuffer = DqnMemStack_BeginTempRegion(transMemStack);
|
||||
@ -474,8 +513,10 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 FORCE_4_BPP = 4;
|
||||
bitmap->memory = stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w,
|
||||
&bitmap->dim.h, &bitmap->bytesPerPixel, 4);
|
||||
&bitmap->dim.h, NULL, FORCE_4_BPP);
|
||||
bitmap->bytesPerPixel = FORCE_4_BPP;
|
||||
}
|
||||
DqnMemStack_EndTempRegion(tempBuffer);
|
||||
if (!bitmap->memory) return false;
|
||||
@ -495,14 +536,15 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap,
|
||||
color.g = (f32)((pixel >> 8) & 0xFF);
|
||||
color.r = (f32)((pixel >> 0) & 0xFF);
|
||||
|
||||
color *= DTRRENDER_INV_255;
|
||||
color = DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(color);
|
||||
color *= 255.0f;
|
||||
DqnV4 preMulColor = color;
|
||||
preMulColor *= DTRRENDER_INV_255;
|
||||
preMulColor = DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(preMulColor);
|
||||
preMulColor *= 255.0f;
|
||||
|
||||
pixel = (((u32)color.a << 24) |
|
||||
((u32)color.b << 16) |
|
||||
((u32)color.g << 8) |
|
||||
((u32)color.r << 0));
|
||||
pixel = (((u32)preMulColor.a << 24) |
|
||||
((u32)preMulColor.b << 16) |
|
||||
((u32)preMulColor.g << 8) |
|
||||
((u32)preMulColor.r << 0));
|
||||
|
||||
pixelPtr[x] = pixel;
|
||||
}
|
||||
@ -1370,7 +1412,7 @@ void CompAssignment(DTRRenderBuffer *const renderBuffer, PlatformInput *const in
|
||||
}
|
||||
}
|
||||
|
||||
FILE_SCOPE void TestStrToF32Converter()
|
||||
FILE_SCOPE void DebugTestStrToF32Converter()
|
||||
{
|
||||
const f32 EPSILON = 0.001f;
|
||||
const char a[] = "-0.66248";
|
||||
@ -1459,7 +1501,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
DTR_DEBUG_EP_TIMED_FUNCTION();
|
||||
if (!memory->isInit)
|
||||
{
|
||||
TestStrToF32Converter();
|
||||
DebugTestStrToF32Converter();
|
||||
DTR_DEBUG_EP_TIMED_BLOCK("DTR_Update Memory Initialisation");
|
||||
// NOTE(doyle): Do premultiply ourselves
|
||||
stbi_set_unpremultiply_on_load(true);
|
||||
@ -1482,7 +1524,11 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
&memory->transMemStack);
|
||||
DqnMemStack_EndTempRegion(tmp);
|
||||
|
||||
DQN_ASSERT(BitmapLoad(input->api, &state->objTex, "african_head_diffuse.tga",
|
||||
&memory->transMemStack));
|
||||
DQN_ASSERT(ObjWavefrontLoad(input->api, memory, "african_head.obj", &state->obj));
|
||||
|
||||
DebugTestWavefrontFaceAndVertexParser(&state->obj);
|
||||
}
|
||||
|
||||
DqnTempMemStack transMemTmpRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
|
||||
@ -1502,7 +1548,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Update and Render
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DTRRender_Clear(&renderBuffer, DqnV3_3f(0.0f, 0.0f, 0.0f));
|
||||
DTRRender_Clear(&renderBuffer, DqnV3_3f(0.5f, 0.0f, 1.0f));
|
||||
|
||||
#if 1
|
||||
DqnV4 colorRed = DqnV4_4f(0.8f, 0, 0, 1);
|
||||
@ -1543,25 +1589,31 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
|
||||
for (i32 i = 0; i < waveObj->model.faces.count; i++)
|
||||
{
|
||||
if (i == 852)
|
||||
{
|
||||
int BreakHere = 0;
|
||||
}
|
||||
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];
|
||||
DQN_ASSERT(face.vertexIndexArray.count == 3);
|
||||
i32 vertAIndex = face.vertexIndexArray.data[0];
|
||||
i32 vertBIndex = face.vertexIndexArray.data[1];
|
||||
i32 vertCIndex = face.vertexIndexArray.data[2];
|
||||
|
||||
DqnV4 vertA = waveObj->geometryArray.data[vertAIndex];
|
||||
DqnV4 vertB = waveObj->geometryArray.data[vertBIndex];
|
||||
DqnV4 vertC = waveObj->geometryArray.data[vertCIndex];
|
||||
DQN_ASSERT(vertAIndex < waveObj->geometryArray.count);
|
||||
DQN_ASSERT(vertBIndex < waveObj->geometryArray.count);
|
||||
DQN_ASSERT(vertCIndex < waveObj->geometryArray.count);
|
||||
|
||||
DqnV4 vertAB = vertB - vertA;
|
||||
DqnV4 vertAC = vertC - vertA;
|
||||
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
|
||||
|
||||
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), LIGHT);
|
||||
if (intensity > 0)
|
||||
{
|
||||
if (intensity < 0) continue;
|
||||
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
|
||||
modelCol.rgb *= intensity;
|
||||
modelCol.rgb *= DQN_ABS(intensity);
|
||||
|
||||
DqnV3 screenVA = (vertA.xyz * MODEL_SCALE) + modelP;
|
||||
DqnV3 screenVB = (vertB.xyz * MODEL_SCALE) + modelP;
|
||||
@ -1577,20 +1629,41 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
screenVC.x = (f32)(i32)(screenVC.x + 0.5f);
|
||||
screenVC.y = (f32)(i32)(screenVC.y + 0.5f);
|
||||
|
||||
DTRRender_Triangle(&renderBuffer, screenVA, screenVB, screenVC, modelCol);
|
||||
i32 textureAIndex = face.textureIndexArray.data[0];
|
||||
i32 textureBIndex = face.textureIndexArray.data[1];
|
||||
i32 textureCIndex = face.textureIndexArray.data[2];
|
||||
|
||||
#if 0
|
||||
DqnV4 wireColor = DqnV4_1f(1.0f);
|
||||
for (i32 j = 0; j < 3; j++)
|
||||
DqnV2 texA = waveObj->textureArray.data[textureAIndex].xy;
|
||||
DqnV2 texB = waveObj->textureArray.data[textureBIndex].xy;
|
||||
DqnV2 texC = waveObj->textureArray.data[textureCIndex].xy;
|
||||
DQN_ASSERT(textureAIndex < waveObj->textureArray.count);
|
||||
DQN_ASSERT(textureBIndex < waveObj->textureArray.count);
|
||||
DQN_ASSERT(textureCIndex < waveObj->textureArray.count);
|
||||
|
||||
bool DEBUG_SIMPLE_MODE = false;
|
||||
if (DTR_DEBUG && DEBUG_SIMPLE_MODE)
|
||||
{
|
||||
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVertA), DqnV2i_V2(screenVertB), wireColor);
|
||||
DTRRender_Triangle(&renderBuffer, screenVA, screenVB, screenVC, modelCol);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
DTRRender_TexturedTriangle(&renderBuffer, screenVA, screenVB, screenVC, texA, texB,
|
||||
texC, &state->objTex, modelCol);
|
||||
}
|
||||
|
||||
bool DEBUG_WIREFRAME = false;
|
||||
if (DTR_DEBUG && DEBUG_WIREFRAME)
|
||||
{
|
||||
DqnV4 wireColor = DqnV4_4f(1.0f, 1.0f, 1.0f, 0.01f);
|
||||
DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVA.xy), DqnV2i_V2(screenVB.xy), wireColor);
|
||||
DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVB.xy), DqnV2i_V2(screenVC.xy), wireColor);
|
||||
DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVC.xy), DqnV2i_V2(screenVA.xy), wireColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rect drawing
|
||||
if (0)
|
||||
{
|
||||
DTRRenderTransform transform = DTRRender_DefaultTransform();
|
||||
transform.rotation = rotation + 45;
|
||||
@ -1600,6 +1673,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
}
|
||||
|
||||
// Bitmap drawing
|
||||
if (0)
|
||||
{
|
||||
DTRRenderTransform transform = DTRRender_DefaultTransform();
|
||||
transform.scale = DqnV2_1f(2.0f);
|
||||
@ -1618,11 +1692,12 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
||||
#endif
|
||||
|
||||
DTRDebug_Update(state, &renderBuffer, input, memory);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// End Update
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnMemStack_EndTempRegion(transMemTmpRegion);
|
||||
DqnMemStack_ClearCurrBlock(&memory->transMemStack, true);
|
||||
|
||||
DQN_ASSERT(memory->transMemStack.tempStackCount == 0);
|
||||
DQN_ASSERT(memory->permMemStack.tempStackCount == 0);
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ typedef void DTR_UpdateFunction(struct PlatformRenderBuffer *const renderBuffer,
|
||||
|
||||
typedef struct WavefrontModelFace
|
||||
{
|
||||
DqnArray<i32> vertexArray;
|
||||
DqnArray<i32> textureArray;
|
||||
DqnArray<i32> normalArray;
|
||||
DqnArray<i32> vertexIndexArray;
|
||||
DqnArray<i32> textureIndexArray;
|
||||
DqnArray<i32> normalIndexArray;
|
||||
} WavefrontModelFace;
|
||||
|
||||
typedef struct WavefrontModel
|
||||
@ -28,7 +28,7 @@ typedef struct WavefrontModel
|
||||
typedef struct WavefrontObj
|
||||
{
|
||||
DqnArray<DqnV4> geometryArray;
|
||||
DqnArray<DqnV3> texUVArray;
|
||||
DqnArray<DqnV3> textureArray;
|
||||
DqnArray<DqnV3> normalArray;
|
||||
|
||||
WavefrontModel model;
|
||||
@ -56,5 +56,6 @@ typedef struct DTRState
|
||||
DTRFont font;
|
||||
DTRBitmap bitmap;
|
||||
WavefrontObj obj;
|
||||
DTRBitmap objTex;
|
||||
} DTRState;
|
||||
#endif
|
||||
|
@ -3,8 +3,56 @@
|
||||
#include "DTRendererPlatform.h"
|
||||
#include "DTRendererRender.h"
|
||||
|
||||
#include "dqn.h"
|
||||
|
||||
DTRDebug globalDebug;
|
||||
|
||||
void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const transMemStack)
|
||||
{
|
||||
if (DTR_DEBUG)
|
||||
{
|
||||
PlatformAPI *const api = &globalDebug.input->api;
|
||||
PlatformFile file = {};
|
||||
|
||||
u32 permissions = (PlatformFilePermissionFlag_Read | PlatformFilePermissionFlag_Write);
|
||||
if (!api->FileOpen("zBufferDump.txt", &file, permissions,
|
||||
PlatformFileAction_CreateIfNotExist))
|
||||
{
|
||||
if (!api->FileOpen("zBufferDump.txt", &file, permissions,
|
||||
PlatformFileAction_ClearIfExist))
|
||||
{
|
||||
DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(transMemStack);
|
||||
|
||||
size_t bufSize = DQN_MEGABYTE(16);
|
||||
char *bufString = (char *)DqnMemStack_Push(transMemStack, bufSize);
|
||||
char *bufPtr = bufString;
|
||||
for (i32 i = 0; i < renderBuffer->width * renderBuffer->height; i++)
|
||||
{
|
||||
|
||||
f32 zValue = renderBuffer->zBuffer[i];
|
||||
if (zValue == DQN_F32_MIN) continue;
|
||||
i32 chWritten = Dqn_sprintf(bufPtr, "index %06d: %05.5f\n", i, zValue);
|
||||
if ((bufPtr + chWritten) > (bufString + bufSize))
|
||||
{
|
||||
size_t bufPtrAddr = (size_t)(bufPtr + chWritten);
|
||||
size_t bufStringAddr = (size_t)(bufString + bufSize);
|
||||
DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
||||
}
|
||||
bufPtr += chWritten;
|
||||
}
|
||||
|
||||
size_t writeSize = (size_t)bufPtr - (size_t)bufString;
|
||||
size_t bytesWritten = api->FileWrite(&file, (u8 *)bufString, writeSize);
|
||||
api->FileClose(&file);
|
||||
|
||||
DqnMemStack_EndTempRegion(tmpMemRegion);
|
||||
}
|
||||
}
|
||||
|
||||
void DTRDebug_PushText(const char *const formatStr, ...)
|
||||
{
|
||||
if (DTR_DEBUG)
|
||||
@ -105,6 +153,21 @@ void DTRDebug_Update(DTRState *const state,
|
||||
debug->totalSetPixels += debug->counter[DTRDebugCounter_SetPixels];
|
||||
debug->totalSetPixels = DQN_MAX(0, debug->totalSetPixels);
|
||||
|
||||
// memory
|
||||
{
|
||||
PushMemStackText("PermBuffer", &memory->permMemStack);
|
||||
PushMemStackText("TransBuffer", &memory->transMemStack);
|
||||
}
|
||||
|
||||
DTRDebug_PushText("Mouse: %d, %d", input->mouse.x, input->mouse.y);
|
||||
DTRDebug_PushText("MouseLBtn: %s", (input->mouse.leftBtn.endedDown) ? "true" : "false");
|
||||
DTRDebug_PushText("MouseRBtn: %s", (input->mouse.rightBtn.endedDown) ? "true" : "false");
|
||||
DTRDebug_PushText("");
|
||||
|
||||
DTRDebug_PushText("SSE2Support: %s", (input->canUseSSE2) ? "true" : "false");
|
||||
DTRDebug_PushText("RDTSCSupport: %s", (input->canUseRdtsc) ? "true" : "false");
|
||||
DTRDebug_PushText("");
|
||||
|
||||
DTRDebug_PushText("TotalSetPixels: %'lld", debug->totalSetPixels);
|
||||
DTRDebug_PushText("SetPixelsPerFrame: %'lld", debug->counter[DTRDebugCounter_SetPixels]);
|
||||
DTRDebug_PushText("TrianglesRendered: %'lld", debug->counter[DTRDebugCounter_RenderTriangle]);
|
||||
@ -116,15 +179,10 @@ void DTRDebug_Update(DTRState *const state,
|
||||
}
|
||||
DTRDebug_PushText("");
|
||||
|
||||
// memory
|
||||
{
|
||||
PushMemStackText("PermBuffer", &memory->permMemStack);
|
||||
PushMemStackText("TransBuffer", &memory->transMemStack);
|
||||
}
|
||||
|
||||
DTRDebug_PushText("SSE2Support: %s", (input->canUseSSE2) ? "true" : "false");
|
||||
DTRDebug_PushText("SSE2Support: %s", (input->canUseRdtsc) ? "true" : "false");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// End Debug Update
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
debug->displayP =
|
||||
DqnV2_2i(0, debug->renderBuffer->height + globalDebug.displayYOffset);
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "dqn.h"
|
||||
#define DTR_DEBUG 1
|
||||
#if DTR_DEBUG
|
||||
#define DTR_DEBUG_RENDER 0
|
||||
#define DTR_DEBUG_RENDER 1
|
||||
|
||||
#define DTR_DEBUG_PROFILING_EASY_PROFILER 0
|
||||
#if DTR_DEBUG_PROFILING_EASY_PROFILER
|
||||
@ -67,6 +67,7 @@ typedef struct DTRDebug
|
||||
|
||||
extern DTRDebug globalDebug;
|
||||
|
||||
void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const transMemStack);
|
||||
void DTRDebug_PushText(const char *const formatStr, ...);
|
||||
void DTRDebug_Update(DTRState *const state,
|
||||
DTRRenderBuffer *const renderBuffer,
|
||||
|
@ -10,6 +10,13 @@ enum PlatformFilePermissionFlag
|
||||
PlatformFilePermissionFlag_Write = (1 << 1),
|
||||
};
|
||||
|
||||
enum PlatformFileAction
|
||||
{
|
||||
PlatformFileAction_OpenOnly,
|
||||
PlatformFileAction_CreateIfNotExist,
|
||||
PlatformFileAction_ClearIfExist,
|
||||
};
|
||||
|
||||
typedef struct PlatformFile
|
||||
{
|
||||
void *handle;
|
||||
@ -17,16 +24,16 @@ typedef struct PlatformFile
|
||||
u32 permissionFlags;
|
||||
} PlatformFile;
|
||||
|
||||
typedef bool PlatformAPI_FileOpen (const char *const path, PlatformFile *const file,
|
||||
const u32 permissionFlags);
|
||||
typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf,
|
||||
const size_t bytesToRead); // Return bytes read
|
||||
typedef bool PlatformAPI_FileOpen (const char *const path, PlatformFile *const file, const u32 permissionFlags, const enum PlatformFileAction actionFlags);
|
||||
typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf, const size_t bytesToRead); // Return bytes read
|
||||
typedef size_t PlatformAPI_FileWrite(PlatformFile *const file, u8 *const buf, const size_t numBytesToWrite); // Return bytes read
|
||||
typedef void PlatformAPI_FileClose(PlatformFile *const file);
|
||||
typedef void PlatformAPI_Print (const char *const string);
|
||||
typedef struct PlatformAPI
|
||||
{
|
||||
PlatformAPI_FileOpen *FileOpen;
|
||||
PlatformAPI_FileRead *FileRead;
|
||||
PlatformAPI_FileWrite *FileWrite;
|
||||
PlatformAPI_FileClose *FileClose;
|
||||
PlatformAPI_Print *Print;
|
||||
} PlatformAPI;
|
||||
@ -68,6 +75,14 @@ typedef struct KeyState
|
||||
u32 halfTransitionCount;
|
||||
} KeyState;
|
||||
|
||||
typedef struct PlatformMouse
|
||||
{
|
||||
i32 x;
|
||||
i32 y;
|
||||
KeyState leftBtn;
|
||||
KeyState rightBtn;
|
||||
} PlatformMouse;
|
||||
|
||||
typedef struct PlatformInput
|
||||
{
|
||||
f32 deltaForFrame;
|
||||
@ -77,6 +92,7 @@ typedef struct PlatformInput
|
||||
bool canUseRdtsc;
|
||||
|
||||
PlatformAPI api;
|
||||
PlatformMouse mouse;
|
||||
union {
|
||||
KeyState key[key_count];
|
||||
struct
|
||||
|
@ -7,16 +7,11 @@
|
||||
#include "external/stb_rect_pack.h"
|
||||
#include "external/stb_truetype.h"
|
||||
|
||||
// #define DTR_DEBUG_RENDER_FONT_BITMAP
|
||||
#ifdef DTR_DEBUG_RENDER_FONT_BITMAP
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "external/stb_image_write.h"
|
||||
#endif
|
||||
|
||||
FILE_SCOPE const f32 COLOR_EPSILON = 0.9f;
|
||||
|
||||
FILE_SCOPE inline DqnV4 PreMultiplyAlpha1(const DqnV4 color)
|
||||
{
|
||||
DQN_ASSERT(color.a >= 0.0f && color.a <= 1.0f);
|
||||
DqnV4 result;
|
||||
result.r = color.r * color.a;
|
||||
result.g = color.g * color.a;
|
||||
@ -26,7 +21,10 @@ FILE_SCOPE inline DqnV4 PreMultiplyAlpha1(const DqnV4 color)
|
||||
DQN_ASSERT(result.r >= 0.0f && result.r <= 1.0f);
|
||||
DQN_ASSERT(result.g >= 0.0f && result.g <= 1.0f);
|
||||
DQN_ASSERT(result.b >= 0.0f && result.b <= 1.0f);
|
||||
DQN_ASSERT(result.a >= 0.0f && result.a <= 1.0f);
|
||||
|
||||
DQN_ASSERT(result.a >= result.r);
|
||||
DQN_ASSERT(result.a >= result.g);
|
||||
DQN_ASSERT(result.a >= result.b);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -113,10 +111,8 @@ FILE_SCOPE inline void SetPixel(DTRRenderBuffer *const renderBuffer, const i32 x
|
||||
// If some alpha is involved, we need to apply gamma correction, but if the
|
||||
// new pixel is totally opaque or invisible then we're just flat out
|
||||
// overwriting/keeping the state of the pixel so we can save cycles by skipping.
|
||||
#if 1
|
||||
bool needGammaFix = (color.a > 0.0f || color.a < 1.0f + COLOR_EPSILON) && (colorSpace == ColorSpace_SRGB);
|
||||
if (needGammaFix) color = DTRRender_SRGB1ToLinearSpaceV4(color);
|
||||
#endif
|
||||
|
||||
u32 src = bitmapPtr[x + (y * pitchInU32)];
|
||||
f32 srcR = (f32)((src >> 16) & 0xFF) * DTRRENDER_INV_255;
|
||||
@ -484,6 +480,237 @@ void DTRRender_Rectangle(DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 m
|
||||
}
|
||||
}
|
||||
|
||||
FILE_SCOPE void DebugBarycentricInternal(DqnV2 p, DqnV2 a, DqnV2 b, DqnV2 c, f32 *u, f32 *v, f32 *w)
|
||||
{
|
||||
DqnV2 v0 = b - a;
|
||||
DqnV2 v1 = c - a;
|
||||
DqnV2 v2 = p - a;
|
||||
|
||||
f32 d00 = DqnV2_Dot(v0, v0);
|
||||
f32 d01 = DqnV2_Dot(v0, v1);
|
||||
f32 d11 = DqnV2_Dot(v1, v1);
|
||||
f32 d20 = DqnV2_Dot(v2, v0);
|
||||
f32 d21 = DqnV2_Dot(v2, v1);
|
||||
f32 denom = d00 * d11 - d01 * d01;
|
||||
*v = (d11 * d20 - d01 * d21) / denom;
|
||||
*w = (d00 * d21 - d01 * d20) / denom;
|
||||
*u = 1.0f - *v - *w;
|
||||
}
|
||||
|
||||
void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3,
|
||||
DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture,
|
||||
DqnV4 color, const DTRRenderTransform transform)
|
||||
{
|
||||
DTR_DEBUG_EP_TIMED_FUNCTION();
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Transform vertexes
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnV3 p1p2 = p2 - p1;
|
||||
DqnV3 p1p3 = p3 - p1;
|
||||
|
||||
// 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.xy = pList[0];
|
||||
p2.xy = pList[1];
|
||||
p3.xy = pList[2];
|
||||
|
||||
color = DTRRender_SRGB1ToLinearSpaceV4(color);
|
||||
color = PreMultiplyAlpha1(color);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Calculate Bounding Box
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DqnV2i max = DqnV2i_2f(DQN_MAX(DQN_MAX(p1.x, p2.x), p3.x),
|
||||
DQN_MAX(DQN_MAX(p1.y, p2.y), p3.y));
|
||||
DqnV2i min = DqnV2i_2f(DQN_MIN(DQN_MIN(p1.x, p2.x), p3.x),
|
||||
DQN_MIN(DQN_MIN(p1.y, p2.y), p3.y));
|
||||
min.x = DQN_MAX(min.x, 0);
|
||||
min.y = DQN_MAX(min.y, 0);
|
||||
max.x = DQN_MIN(max.x, renderBuffer->width - 1);
|
||||
max.y = DQN_MIN(max.y, renderBuffer->height - 1);
|
||||
|
||||
f32 area2Times = ((p2.x - p1.x) * (p2.y + p1.y)) +
|
||||
((p3.x - p2.x) * (p3.y + p2.y)) +
|
||||
((p1.x - p3.x) * (p1.y + p3.y));
|
||||
if (area2Times > 0)
|
||||
{
|
||||
// Clockwise swap any point to make it clockwise
|
||||
DQN_SWAP(DqnV3, p2, p3);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Signed Area - See Render_Triangle for explanation
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
const DqnV3 a = p1;
|
||||
const DqnV3 b = p2;
|
||||
const DqnV3 c = p3;
|
||||
|
||||
DqnV2i startP = min;
|
||||
f32 oldSignedArea1 = ((b.x - a.x) * (startP.y - a.y)) - ((b.y - a.y) * (startP.x - a.x));
|
||||
f32 oldSignedArea2 = ((c.x - b.x) * (startP.y - b.y)) - ((c.y - b.y) * (startP.x - b.x));
|
||||
f32 oldSignedArea3 = ((a.x - c.x) * (startP.y - c.y)) - ((a.y - c.y) * (startP.x - c.x));
|
||||
|
||||
f32 signedArea1 = ((b.x - a.x) * (startP.y - a.y)) - ((b.y - a.y) * (startP.x - a.x));
|
||||
f32 signedArea1DeltaX = a.y - b.y;
|
||||
f32 signedArea1DeltaY = b.x - a.x;
|
||||
|
||||
f32 signedArea2 = ((c.x - b.x) * (startP.y - b.y)) - ((c.y - b.y) * (startP.x - b.x));
|
||||
f32 signedArea2DeltaX = b.y - c.y;
|
||||
f32 signedArea2DeltaY = c.x - b.x;
|
||||
|
||||
f32 signedArea3 = ((a.x - c.x) * (startP.y - c.y)) - ((a.y - c.y) * (startP.x - c.x));
|
||||
f32 signedArea3DeltaX = c.y - a.y;
|
||||
f32 signedArea3DeltaY = a.x - c.x;
|
||||
|
||||
f32 signedAreaParallelogram = signedArea1 + signedArea2 + signedArea3;
|
||||
if (signedAreaParallelogram == 0) return;
|
||||
f32 invSignedAreaParallelogram = 1 / signedAreaParallelogram;
|
||||
|
||||
DTRDebug_BeginCycleCount(DTRDebugCycleCount_RenderTriangle_Rasterise);
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Scan and Render
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
const u32 zBufferPitch = renderBuffer->width;
|
||||
const f32 BARYCENTRIC_EPSILON = 0.1f;
|
||||
|
||||
u8 *texturePtr = texture->memory;
|
||||
const u32 texturePitch = texture->bytesPerPixel * texture->dim.w;
|
||||
for (i32 bufferY = min.y; bufferY < max.y; bufferY++)
|
||||
{
|
||||
f32 signedArea1Row = signedArea1;
|
||||
f32 signedArea2Row = signedArea2;
|
||||
f32 signedArea3Row = signedArea3;
|
||||
|
||||
for (i32 bufferX = min.x; bufferX < max.x; bufferX++)
|
||||
{
|
||||
if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0)
|
||||
{
|
||||
f32 barycentricB = signedArea3Row * invSignedAreaParallelogram;
|
||||
f32 barycentricC = signedArea1Row * invSignedAreaParallelogram;
|
||||
|
||||
if (DTR_DEBUG)
|
||||
{
|
||||
const f32 EPSILON = 0.1f;
|
||||
|
||||
f32 debugSignedArea1 = ((b.x - a.x) * (bufferY - a.y)) - ((b.y - a.y) * (bufferX - a.x));
|
||||
f32 debugSignedArea2 = ((c.x - b.x) * (bufferY - b.y)) - ((c.y - b.y) * (bufferX - b.x));
|
||||
f32 debugSignedArea3 = ((a.x - c.x) * (bufferY - c.y)) - ((a.y - c.y) * (bufferX - c.x));
|
||||
|
||||
f32 deltaSignedArea1 = debugSignedArea1 - signedArea1Row;
|
||||
f32 deltaSignedArea2 = debugSignedArea2 - signedArea2Row;
|
||||
f32 deltaSignedArea3 = debugSignedArea3 - signedArea3Row;
|
||||
DQN_ASSERT(deltaSignedArea1 < EPSILON && deltaSignedArea2 < EPSILON &&
|
||||
deltaSignedArea3 < EPSILON)
|
||||
|
||||
f32 debugBarycentricA, debugBarycentricB, debugBarycentricC;
|
||||
DebugBarycentricInternal(DqnV2_2i(bufferX, bufferY), a.xy, b.xy, c.xy,
|
||||
&debugBarycentricA, &debugBarycentricB,
|
||||
&debugBarycentricC);
|
||||
|
||||
|
||||
f32 deltaBaryB = DQN_ABS(barycentricB - debugBarycentricB);
|
||||
f32 deltaBaryC = DQN_ABS(barycentricC - debugBarycentricC);
|
||||
|
||||
DQN_ASSERT(deltaBaryB < EPSILON && deltaBaryC < EPSILON)
|
||||
}
|
||||
|
||||
i32 zBufferIndex = bufferX + (bufferY * zBufferPitch);
|
||||
f32 pixelZValue = a.z + (barycentricB * (b.z - a.z)) + (barycentricC * (c.z - a.z));
|
||||
f32 currZValue = renderBuffer->zBuffer[zBufferIndex];
|
||||
DQN_ASSERT(zBufferIndex < (renderBuffer->width * renderBuffer->height));
|
||||
|
||||
if (pixelZValue > currZValue)
|
||||
{
|
||||
renderBuffer->zBuffer[zBufferIndex] = pixelZValue;
|
||||
const bool DEBUG_SAMPLE_TEXTURE = true;
|
||||
DqnV2 uv = uv1 + ((uv2 - uv1) * barycentricB) + ((uv3 - uv1) * barycentricC);
|
||||
|
||||
const f32 EPSILON = 0.1f;
|
||||
DQN_ASSERT(uv.x >= 0 && uv.x < 1.0f + EPSILON);
|
||||
DQN_ASSERT(uv.y >= 0 && uv.y < 1.0f + EPSILON);
|
||||
|
||||
uv.x = DqnMath_Clampf(uv.x, 0.0f, 1.0f);
|
||||
uv.y = DqnMath_Clampf(uv.y, 0.0f, 1.0f);
|
||||
|
||||
f32 texelXf = uv.x * texture->dim.w;
|
||||
f32 texelYf = uv.y * texture->dim.h;
|
||||
DQN_ASSERT(texelXf >= 0 && texelXf < texture->dim.w);
|
||||
DQN_ASSERT(texelYf >= 0 && texelYf < texture->dim.h);
|
||||
|
||||
i32 texelX = (i32)texelXf;
|
||||
i32 texelY = (i32)texelYf;
|
||||
|
||||
u32 texel1 = *(u32 *)(texturePtr + (texelX * texture->bytesPerPixel) +
|
||||
(texelY * texturePitch));
|
||||
|
||||
DqnV4 color1;
|
||||
color1.a = (f32)(texel1 >> 24);
|
||||
color1.b = (f32)((texel1 >> 16) & 0xFF);
|
||||
color1.g = (f32)((texel1 >> 8) & 0xFF);
|
||||
color1.r = (f32)((texel1 >> 0) & 0xFF);
|
||||
|
||||
color1 *= DTRRENDER_INV_255;
|
||||
color1 = DTRRender_SRGB1ToLinearSpaceV4(color1);
|
||||
DqnV4 blend = color * color1;
|
||||
SetPixel(renderBuffer, bufferX, bufferY, blend, ColorSpace_Linear);
|
||||
}
|
||||
}
|
||||
|
||||
signedArea1Row += signedArea1DeltaX;
|
||||
signedArea2Row += signedArea2DeltaX;
|
||||
signedArea3Row += signedArea3DeltaX;
|
||||
}
|
||||
|
||||
signedArea1 += signedArea1DeltaY;
|
||||
signedArea2 += signedArea2DeltaY;
|
||||
signedArea3 += signedArea3DeltaY;
|
||||
}
|
||||
DTRDebug_EndCycleCount(DTRDebugCycleCount_RenderTriangle_Rasterise);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Debug
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
DTRDebug_CounterIncrement(DTRDebugCounter_RenderTriangle);
|
||||
if (DTR_DEBUG_RENDER)
|
||||
{
|
||||
// Draw Bounding box
|
||||
if (0)
|
||||
{
|
||||
DTRRender_Line(renderBuffer, DqnV2i_2i(min.x, min.y), DqnV2i_2i(min.x, max.y), color);
|
||||
DTRRender_Line(renderBuffer, DqnV2i_2i(min.x, max.y), DqnV2i_2i(max.x, max.y), color);
|
||||
DTRRender_Line(renderBuffer, DqnV2i_2i(max.x, max.y), DqnV2i_2i(max.x, min.y), color);
|
||||
DTRRender_Line(renderBuffer, DqnV2i_2i(max.x, min.y), DqnV2i_2i(min.x, min.y), color);
|
||||
}
|
||||
|
||||
// Draw Triangle Coordinate Basis
|
||||
if (0)
|
||||
{
|
||||
DqnV2 xAxis = DqnV2_2f(cosf(transform.rotation), sinf(transform.rotation)) * transform.scale.x;
|
||||
DqnV2 yAxis = DqnV2_2f(-xAxis.y, xAxis.x) * transform.scale.y;
|
||||
DqnV4 coordSysColor = DqnV4_4f(0, 1, 1, 1);
|
||||
i32 axisLen = 50;
|
||||
DTRRender_Line(renderBuffer, DqnV2i_V2(origin), DqnV2i_V2(origin) + DqnV2i_V2(xAxis * axisLen), coordSysColor);
|
||||
DTRRender_Line(renderBuffer, DqnV2i_V2(origin), DqnV2i_V2(origin) + DqnV2i_V2(yAxis * axisLen), coordSysColor);
|
||||
}
|
||||
|
||||
// Draw axis point
|
||||
if (0)
|
||||
{
|
||||
DqnV4 green = DqnV4_4f(0, 1, 0, 1);
|
||||
DqnV4 blue = DqnV4_4f(0, 0, 1, 1);
|
||||
DqnV4 purple = DqnV4_4f(1, 0, 1, 1);
|
||||
|
||||
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_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3,
|
||||
DqnV4 color, const DTRRenderTransform transform)
|
||||
{
|
||||
@ -670,30 +897,17 @@ void DTRRender_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2,
|
||||
{
|
||||
if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0)
|
||||
{
|
||||
#if 1
|
||||
f32 barycentricA = signedArea2Row * invSignedAreaParallelogram;
|
||||
f32 barycentricB = signedArea3Row * invSignedAreaParallelogram;
|
||||
f32 barycentricC = signedArea1Row * invSignedAreaParallelogram;
|
||||
|
||||
if (DTR_DEBUG)
|
||||
{
|
||||
f32 barycentricSum = barycentricA + barycentricB + barycentricC;
|
||||
DQN_ASSERT((1.0f - barycentricSum) < BARYCENTRIC_EPSILON);
|
||||
}
|
||||
|
||||
f32 pixelZValue =
|
||||
(a.z * barycentricA) + (b.z * barycentricB) + (c.z * barycentricC);
|
||||
|
||||
i32 zBufferIndex = bufferX + (bufferY * zBufferPitch);
|
||||
f32 pixelZValue = a.z + (barycentricB * (b.z - a.z)) + (barycentricC * (c.z - a.z));
|
||||
f32 currZValue = renderBuffer->zBuffer[zBufferIndex];
|
||||
if (pixelZValue > currZValue)
|
||||
{
|
||||
renderBuffer->zBuffer[zBufferIndex] = pixelZValue;
|
||||
SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear);
|
||||
}
|
||||
#else
|
||||
SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear);
|
||||
#endif
|
||||
}
|
||||
|
||||
signedArea1Row += signedArea1DeltaX;
|
||||
@ -996,5 +1210,3 @@ void DTRRender_Clear(DTRRenderBuffer *const renderBuffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,6 +63,7 @@ void DTRRender_Text (DTRRenderBuffer *const renderBuffer, const DTRFont font
|
||||
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_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture, 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);
|
||||
|
||||
|
@ -40,16 +40,20 @@ void Platform_Print(const char *const string)
|
||||
OutputDebugString(string);
|
||||
}
|
||||
|
||||
bool Platform_FileOpen(const char *const path, PlatformFile *const file,
|
||||
const u32 permissionFlags)
|
||||
bool Platform_FileOpen(const char *const path, PlatformFile *const file, const u32 permissionFlags,
|
||||
const enum PlatformFileAction fileAction)
|
||||
{
|
||||
if (!path || !file) return false;
|
||||
DQN_ASSERT((permissionFlags &
|
||||
~(PlatformFilePermissionFlag_Write |
|
||||
PlatformFilePermissionFlag_Read)) == 0);
|
||||
|
||||
DQN_ASSERT((fileAction &
|
||||
~(PlatformFileAction_OpenOnly | PlatformFileAction_CreateIfNotExist |
|
||||
PlatformFileAction_ClearIfExist)) == 0);
|
||||
|
||||
DqnFile dqnFile = {};
|
||||
if (DqnFile_Open(path, &dqnFile, permissionFlags, DqnFileAction_OpenOnly))
|
||||
if (DqnFile_Open(path, &dqnFile, permissionFlags, (enum DqnFileAction)fileAction))
|
||||
{
|
||||
*file = DqnFileToPlatformFileInternal(dqnFile);
|
||||
return true;
|
||||
@ -69,6 +73,17 @@ size_t Platform_FileRead(PlatformFile *const file, u8 *const buf,
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
size_t Platform_FileWrite(PlatformFile *const file, u8 *const buf,
|
||||
const size_t numBytesToWrite)
|
||||
{
|
||||
if (!file || !buf) return 0;
|
||||
|
||||
DqnFile dqnFile = PlatformFileToDqnFileInternal(*file);
|
||||
size_t numBytesRead = DqnFile_Write(&dqnFile, buf, numBytesToWrite, 0);
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
|
||||
void Platform_FileClose(PlatformFile *const file)
|
||||
{
|
||||
if (!file) return;
|
||||
@ -81,6 +96,7 @@ void Platform_FileClose(PlatformFile *const file)
|
||||
// Win32 Layer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <Windows.h>
|
||||
#include <Windowsx.h> // For GET_X|Y_LPARAM(), mouse input
|
||||
#include <Psapi.h> // For win32 GetProcessMemoryInfo()
|
||||
typedef struct Win32RenderBitmap
|
||||
{
|
||||
@ -314,6 +330,34 @@ FILE_SCOPE void Win32ProcessMessages(HWND window, PlatformInput *input)
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
bool isDown = (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN);
|
||||
|
||||
if (msg.message == WM_LBUTTONDOWN || msg.message == WM_LBUTTONUP)
|
||||
{
|
||||
Win32UpdateKey(&input->mouse.leftBtn, isDown);
|
||||
}
|
||||
else if (msg.message == WM_RBUTTONDOWN || msg.message == WM_RBUTTONUP)
|
||||
{
|
||||
Win32UpdateKey(&input->mouse.rightBtn, isDown);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
LONG height;
|
||||
DqnWin32_GetClientDim(window, NULL, &height);
|
||||
|
||||
input->mouse.x = GET_X_LPARAM(msg.lParam);
|
||||
input->mouse.y = height - GET_Y_LPARAM(msg.lParam);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYDOWN:
|
||||
@ -431,8 +475,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
// when you blit to the screen blackness, the area that is being blitted to
|
||||
// is slightly smaller than 800x600. Windows provides a function to help
|
||||
// calculate the size you'd need by accounting for the window style.
|
||||
const u32 MIN_WIDTH = 1024;
|
||||
const u32 MIN_HEIGHT = 768;
|
||||
const u32 MIN_WIDTH = 800;
|
||||
const u32 MIN_HEIGHT = 800;
|
||||
RECT rect = {};
|
||||
rect.right = MIN_WIDTH;
|
||||
rect.bottom = MIN_HEIGHT;
|
||||
@ -508,6 +552,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
PlatformAPI platformAPI = {};
|
||||
platformAPI.FileOpen = Platform_FileOpen;
|
||||
platformAPI.FileRead = Platform_FileRead;
|
||||
platformAPI.FileWrite = Platform_FileWrite;
|
||||
platformAPI.FileClose = Platform_FileClose;
|
||||
platformAPI.Print = Platform_Print;
|
||||
|
||||
@ -590,6 +635,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
|
||||
frameTimeInS = DqnTime_NowInS() - startFrameTimeInS;
|
||||
f32 msPerFrame = 1000.0f * (f32)frameTimeInS;
|
||||
f32 framesPerSecond = 1.0f / (f32)frameTimeInS;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
@ -600,7 +646,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
|
||||
// Update title bar
|
||||
char windowTitleBuffer[128] = {};
|
||||
Dqn_sprintf(windowTitleBuffer, "drenderer - dev - %5.2f ms/f - mem %'dkb", msPerFrame,
|
||||
Dqn_sprintf(windowTitleBuffer, "drenderer - dev - %5.2f ms/f - %5.2f fps - mem %'dkb", msPerFrame, framesPerSecond,
|
||||
(u32)(memCounter.PagefileUsage / 1024.0f));
|
||||
SetWindowTextA(mainWindow, windowTitleBuffer);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ typedef int16_t i16;
|
||||
typedef double f64;
|
||||
typedef float f32;
|
||||
|
||||
#define DQN_F32_MIN FLT_MIN
|
||||
#define DQN_F32_MIN -FLT_MAX
|
||||
|
||||
#define DQN_TERABYTE(val) (DQN_GIGABYTE(val) * 1024LL)
|
||||
#define DQN_GIGABYTE(val) (DQN_MEGABYTE(val) * 1024LL)
|
||||
@ -766,7 +766,6 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max);
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
#define DQN_WIN32_ERROR_BOX(text, title) MessageBoxA(NULL, text, title, MB_OK);
|
||||
// Out is a pointer to the buffer to receive the characters.
|
||||
// outLen is the length/capacity of the out buffer
|
||||
|
1092
src/external/tests/stb_image_write.h
vendored
Normal file
1092
src/external/tests/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
221
src/external/tests/tinyrenderer/geometry.h
vendored
Normal file
221
src/external/tests/tinyrenderer/geometry.h
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
#ifndef __GEOMETRY_H__
|
||||
#define __GEOMETRY_H__
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
template<size_t DimCols,size_t DimRows,typename T> class mat;
|
||||
|
||||
template <size_t DIM, typename T> struct vec {
|
||||
vec() { for (size_t i=DIM; i--; data_[i] = T()); }
|
||||
T& operator[](const size_t i) { assert(i<DIM); return data_[i]; }
|
||||
const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; }
|
||||
private:
|
||||
T data_[DIM];
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T> struct vec<2,T> {
|
||||
vec() : x(T()), y(T()) {}
|
||||
vec(T X, T Y) : x(X), y(Y) {}
|
||||
template <class U> vec<2,T>(const vec<2,U> &v);
|
||||
T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; }
|
||||
const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; }
|
||||
|
||||
T x,y;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T> struct vec<3,T> {
|
||||
vec() : x(T()), y(T()), z(T()) {}
|
||||
vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
|
||||
template <class U> vec<3,T>(const vec<3,U> &v);
|
||||
T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
||||
const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
||||
float norm() { return std::sqrt(x*x+y*y+z*z); }
|
||||
vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
|
||||
|
||||
T x,y,z;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<size_t DIM,typename T> T operator*(const vec<DIM,T>& lhs, const vec<DIM,T>& rhs) {
|
||||
T ret = T();
|
||||
for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template<size_t DIM,typename T>vec<DIM,T> operator+(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T>vec<DIM,T> operator-(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T,typename U> vec<DIM,T> operator*(vec<DIM,T> lhs, const U& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]*=rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T,typename U> vec<DIM,T> operator/(vec<DIM,T> lhs, const U& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]/=rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t LEN,size_t DIM,typename T> vec<LEN,T> embed(const vec<DIM,T> &v, T fill=1) {
|
||||
vec<LEN,T> ret;
|
||||
for (size_t i=LEN; i--; ret[i]=(i<DIM?v[i]:fill));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t LEN,size_t DIM, typename T> vec<LEN,T> proj(const vec<DIM,T> &v) {
|
||||
vec<LEN,T> ret;
|
||||
for (size_t i=LEN; i--; ret[i]=v[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T> vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) {
|
||||
return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
|
||||
}
|
||||
|
||||
template <size_t DIM, typename T> std::ostream& operator<<(std::ostream& out, vec<DIM,T>& v) {
|
||||
for(unsigned int i=0; i<DIM; i++) {
|
||||
out << v[i] << " " ;
|
||||
}
|
||||
return out ;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<size_t DIM,typename T> struct dt {
|
||||
static T det(const mat<DIM,DIM,T>& src) {
|
||||
T ret=0;
|
||||
for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i));
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct dt<1,T> {
|
||||
static T det(const mat<1,1,T>& src) {
|
||||
return src[0][0];
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<size_t DimRows,size_t DimCols,typename T> class mat {
|
||||
vec<DimCols,T> rows[DimRows];
|
||||
public:
|
||||
mat() {}
|
||||
|
||||
vec<DimCols,T>& operator[] (const size_t idx) {
|
||||
assert(idx<DimRows);
|
||||
return rows[idx];
|
||||
}
|
||||
|
||||
const vec<DimCols,T>& operator[] (const size_t idx) const {
|
||||
assert(idx<DimRows);
|
||||
return rows[idx];
|
||||
}
|
||||
|
||||
vec<DimRows,T> col(const size_t idx) const {
|
||||
assert(idx<DimCols);
|
||||
vec<DimRows,T> ret;
|
||||
for (size_t i=DimRows; i--; ret[i]=rows[i][idx]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_col(size_t idx, vec<DimRows,T> v) {
|
||||
assert(idx<DimCols);
|
||||
for (size_t i=DimRows; i--; rows[i][idx]=v[i]);
|
||||
}
|
||||
|
||||
static mat<DimRows,DimCols,T> identity() {
|
||||
mat<DimRows,DimCols,T> ret;
|
||||
for (size_t i=DimRows; i--; )
|
||||
for (size_t j=DimCols;j--; ret[i][j]=(i==j));
|
||||
return ret;
|
||||
}
|
||||
|
||||
T det() const {
|
||||
return dt<DimCols,T>::det(*this);
|
||||
}
|
||||
|
||||
mat<DimRows-1,DimCols-1,T> get_minor(size_t row, size_t col) const {
|
||||
mat<DimRows-1,DimCols-1,T> ret;
|
||||
for (size_t i=DimRows-1; i--; )
|
||||
for (size_t j=DimCols-1;j--; ret[i][j]=rows[i<row?i:i+1][j<col?j:j+1]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
T cofactor(size_t row, size_t col) const {
|
||||
return get_minor(row,col).det()*((row+col)%2 ? -1 : 1);
|
||||
}
|
||||
|
||||
mat<DimRows,DimCols,T> adjugate() const {
|
||||
mat<DimRows,DimCols,T> ret;
|
||||
for (size_t i=DimRows; i--; )
|
||||
for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j));
|
||||
return ret;
|
||||
}
|
||||
|
||||
mat<DimRows,DimCols,T> invert_transpose() {
|
||||
mat<DimRows,DimCols,T> ret = adjugate();
|
||||
T tmp = ret[0]*rows[0];
|
||||
return ret/tmp;
|
||||
}
|
||||
|
||||
mat<DimRows,DimCols,T> invert() {
|
||||
return invert_transpose().transpose();
|
||||
}
|
||||
|
||||
mat<DimCols,DimRows,T> transpose() {
|
||||
mat<DimCols,DimRows,T> ret;
|
||||
for (size_t i=DimCols; i--; ret[i]=this->col(i));
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<size_t DimRows,size_t DimCols,typename T> vec<DimRows,T> operator*(const mat<DimRows,DimCols,T>& lhs, const vec<DimCols,T>& rhs) {
|
||||
vec<DimRows,T> ret;
|
||||
for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t R1,size_t C1,size_t C2,typename T>mat<R1,C2,T> operator*(const mat<R1,C1,T>& lhs, const mat<C1,C2,T>& rhs) {
|
||||
mat<R1,C2,T> result;
|
||||
for (size_t i=R1; i--; )
|
||||
for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<size_t DimRows,size_t DimCols,typename T>mat<DimCols,DimRows,T> operator/(mat<DimRows,DimCols,T> lhs, const T& rhs) {
|
||||
for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <size_t DimRows,size_t DimCols,class T> std::ostream& operator<<(std::ostream& out, mat<DimRows,DimCols,T>& m) {
|
||||
for (size_t i=0; i<DimRows; i++) out << m[i] << std::endl;
|
||||
return out;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef vec<2, float> Vec2f;
|
||||
typedef vec<2, int> Vec2i;
|
||||
typedef vec<3, float> Vec3f;
|
||||
typedef vec<3, int> Vec3i;
|
||||
typedef vec<4, float> Vec4f;
|
||||
typedef mat<4,4,float> Matrix;
|
||||
#endif //__GEOMETRY_H__
|
||||
|
54
src/external/tests/tinyrenderer/model.cpp
vendored
Normal file
54
src/external/tests/tinyrenderer/model.cpp
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "model.h"
|
||||
|
||||
Model::Model(const char *filename) : verts_(), faces_() {
|
||||
std::ifstream in;
|
||||
in.open (filename, std::ifstream::in);
|
||||
if (in.fail()) return;
|
||||
std::string line;
|
||||
while (!in.eof()) {
|
||||
std::getline(in, line);
|
||||
std::istringstream iss(line.c_str());
|
||||
char trash;
|
||||
if (!line.compare(0, 2, "v ")) {
|
||||
iss >> trash;
|
||||
Vec3f v;
|
||||
for (int i=0;i<3;i++) iss >> v[i];
|
||||
verts_.push_back(v);
|
||||
} else if (!line.compare(0, 2, "f ")) {
|
||||
std::vector<int> f;
|
||||
int itrash, idx;
|
||||
iss >> trash;
|
||||
while (iss >> idx >> trash >> itrash >> trash >> itrash) {
|
||||
idx--; // in wavefront obj all indices start at 1, not zero
|
||||
f.push_back(idx);
|
||||
}
|
||||
faces_.push_back(f);
|
||||
}
|
||||
}
|
||||
std::cerr << "# v# " << verts_.size() << " f# " << faces_.size() << std::endl;
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
}
|
||||
|
||||
int Model::nverts() {
|
||||
return (int)verts_.size();
|
||||
}
|
||||
|
||||
int Model::nfaces() {
|
||||
return (int)faces_.size();
|
||||
}
|
||||
|
||||
std::vector<int> Model::face(int idx) {
|
||||
return faces_[idx];
|
||||
}
|
||||
|
||||
Vec3f Model::vert(int i) {
|
||||
return verts_[i];
|
||||
}
|
||||
|
20
src/external/tests/tinyrenderer/model.h
vendored
Normal file
20
src/external/tests/tinyrenderer/model.h
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __MODEL_H__
|
||||
#define __MODEL_H__
|
||||
|
||||
#include <vector>
|
||||
#include "geometry.h"
|
||||
|
||||
class Model {
|
||||
private:
|
||||
std::vector<Vec3f> verts_;
|
||||
std::vector<std::vector<int> > faces_;
|
||||
public:
|
||||
Model(const char *filename);
|
||||
~Model();
|
||||
int nverts();
|
||||
int nfaces();
|
||||
Vec3f vert(int i);
|
||||
std::vector<int> face(int idx);
|
||||
};
|
||||
|
||||
#endif //__MODEL_H__
|
Loading…
Reference in New Issue
Block a user