diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index fbc7c26..2eda4c5 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -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 +#include + +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 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 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 @@ -120,8 +163,8 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me scan++; } - i32 f32Len = (i32)((size_t)scan - (size_t)f32StartPtr); - v4.e[vIndex++] = (f32)atof(f32StartPtr); // Dqn_StrToF32(f32StartPtr, f32Len); + i32 f32Len = (i32)((size_t)scan - (size_t)f32StartPtr); + 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,54 +1589,81 @@ 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 *= DQN_ABS(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); + + i32 textureAIndex = face.textureIndexArray.data[0]; + i32 textureBIndex = face.textureIndexArray.data[1]; + i32 textureCIndex = face.textureIndexArray.data[2]; + + 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) { - 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); + } + else + { + DTRRender_TexturedTriangle(&renderBuffer, screenVA, screenVB, screenVC, texA, texB, + texC, &state->objTex, 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); - } -#endif + 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); } diff --git a/src/DTRenderer.h b/src/DTRenderer.h index e41cece..ebc37f4 100644 --- a/src/DTRenderer.h +++ b/src/DTRenderer.h @@ -10,9 +10,9 @@ typedef void DTR_UpdateFunction(struct PlatformRenderBuffer *const renderBuffer, typedef struct WavefrontModelFace { - DqnArray vertexArray; - DqnArray textureArray; - DqnArray normalArray; + DqnArray vertexIndexArray; + DqnArray textureIndexArray; + DqnArray normalIndexArray; } WavefrontModelFace; typedef struct WavefrontModel @@ -28,7 +28,7 @@ typedef struct WavefrontModel typedef struct WavefrontObj { DqnArray geometryArray; - DqnArray texUVArray; + DqnArray textureArray; DqnArray normalArray; WavefrontModel model; @@ -53,8 +53,9 @@ typedef struct DTRBitmap typedef struct DTRState { - DTRFont font; - DTRBitmap bitmap; + DTRFont font; + DTRBitmap bitmap; WavefrontObj obj; + DTRBitmap objTex; } DTRState; #endif diff --git a/src/DTRendererDebug.cpp b/src/DTRendererDebug.cpp index e869e6d..e561abb 100644 --- a/src/DTRendererDebug.cpp +++ b/src/DTRendererDebug.cpp @@ -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); diff --git a/src/DTRendererDebug.h b/src/DTRendererDebug.h index daf5474..55da968 100644 --- a/src/DTRendererDebug.h +++ b/src/DTRendererDebug.h @@ -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, diff --git a/src/DTRendererPlatform.h b/src/DTRendererPlatform.h index 65ff57b..c2cd850 100644 --- a/src/DTRendererPlatform.h +++ b/src/DTRendererPlatform.h @@ -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; @@ -76,7 +91,8 @@ typedef struct PlatformInput bool canUseSSE2; bool canUseRdtsc; - PlatformAPI api; + PlatformAPI api; + PlatformMouse mouse; union { KeyState key[key_count]; struct diff --git a/src/DTRendererRender.cpp b/src/DTRendererRender.cpp index 04b2698..1311adb 100644 --- a/src/DTRendererRender.cpp +++ b/src/DTRendererRender.cpp @@ -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 currZValue = renderBuffer->zBuffer[zBufferIndex]; + 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, } } } - - diff --git a/src/DTRendererRender.h b/src/DTRendererRender.h index c37d1c8..5687099 100644 --- a/src/DTRendererRender.h +++ b/src/DTRendererRender.h @@ -59,11 +59,12 @@ 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 (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); +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_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); #endif diff --git a/src/Win32DTRenderer.cpp b/src/Win32DTRenderer.cpp index d332088..1b59683 100644 --- a/src/Win32DTRenderer.cpp +++ b/src/Win32DTRenderer.cpp @@ -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 +#include // For GET_X|Y_LPARAM(), mouse input #include // 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; @@ -588,8 +633,9 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, } } - frameTimeInS = DqnTime_NowInS() - startFrameTimeInS; - f32 msPerFrame = 1000.0f * (f32)frameTimeInS; + 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); } diff --git a/src/dqn.h b/src/dqn.h index 3e70f3a..8ecf064 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -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 - #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 diff --git a/src/external/tests/stb_image_write.h b/src/external/tests/stb_image_write.h new file mode 100644 index 0000000..df62339 --- /dev/null +++ b/src/external/tests/stb_image_write.h @@ -0,0 +1,1092 @@ +/* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (j != 0) ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/external/tests/tinyrenderer/geometry.h b/src/external/tests/tinyrenderer/geometry.h new file mode 100644 index 0000000..46ee7b3 --- /dev/null +++ b/src/external/tests/tinyrenderer/geometry.h @@ -0,0 +1,221 @@ +#ifndef __GEOMETRY_H__ +#define __GEOMETRY_H__ +#include +#include +#include +#include + +template class mat; + +template struct vec { + vec() { for (size_t i=DIM; i--; data_[i] = T()); } + T& operator[](const size_t i) { assert(i struct vec<2,T> { + vec() : x(T()), y(T()) {} + vec(T X, T Y) : x(X), y(Y) {} + template 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 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 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 T operator*(const vec& lhs, const vec& rhs) { + T ret = T(); + for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); + return ret; +} + + +templatevec operator+(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]+=rhs[i]); + return lhs; +} + +templatevec operator-(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]-=rhs[i]); + return lhs; +} + +template vec operator*(vec lhs, const U& rhs) { + for (size_t i=DIM; i--; lhs[i]*=rhs); + return lhs; +} + +template vec operator/(vec lhs, const U& rhs) { + for (size_t i=DIM; i--; lhs[i]/=rhs); + return lhs; +} + +template vec embed(const vec &v, T fill=1) { + vec ret; + for (size_t i=LEN; i--; ret[i]=(i vec proj(const vec &v) { + vec ret; + for (size_t i=LEN; i--; ret[i]=v[i]); + return ret; +} + +template 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 std::ostream& operator<<(std::ostream& out, vec& v) { + for(unsigned int i=0; i struct dt { + static T det(const mat& src) { + T ret=0; + for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i)); + return ret; + } +}; + +template struct dt<1,T> { + static T det(const mat<1,1,T>& src) { + return src[0][0]; + } +}; + +///////////////////////////////////////////////////////////////////////////////// + +template class mat { + vec rows[DimRows]; +public: + mat() {} + + vec& operator[] (const size_t idx) { + assert(idx& operator[] (const size_t idx) const { + assert(idx col(const size_t idx) const { + assert(idx ret; + for (size_t i=DimRows; i--; ret[i]=rows[i][idx]); + return ret; + } + + void set_col(size_t idx, vec v) { + assert(idx identity() { + mat 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::det(*this); + } + + mat get_minor(size_t row, size_t col) const { + mat ret; + for (size_t i=DimRows-1; i--; ) + for (size_t j=DimCols-1;j--; ret[i][j]=rows[i adjugate() const { + mat ret; + for (size_t i=DimRows; i--; ) + for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j)); + return ret; + } + + mat invert_transpose() { + mat ret = adjugate(); + T tmp = ret[0]*rows[0]; + return ret/tmp; + } + + mat invert() { + return invert_transpose().transpose(); + } + + mat transpose() { + mat ret; + for (size_t i=DimCols; i--; ret[i]=this->col(i)); + return ret; + } +}; + +///////////////////////////////////////////////////////////////////////////////// + +template vec operator*(const mat& lhs, const vec& rhs) { + vec ret; + for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs); + return ret; +} + +templatemat operator*(const mat& lhs, const mat& rhs) { + mat result; + for (size_t i=R1; i--; ) + for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j)); + return result; +} + +templatemat operator/(mat lhs, const T& rhs) { + for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs); + return lhs; +} + +template std::ostream& operator<<(std::ostream& out, mat& m) { + for (size_t i=0; i 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__ + diff --git a/src/external/tests/tinyrenderer/model.cpp b/src/external/tests/tinyrenderer/model.cpp new file mode 100644 index 0000000..3e458e0 --- /dev/null +++ b/src/external/tests/tinyrenderer/model.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#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 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 Model::face(int idx) { + return faces_[idx]; +} + +Vec3f Model::vert(int i) { + return verts_[i]; +} + diff --git a/src/external/tests/tinyrenderer/model.h b/src/external/tests/tinyrenderer/model.h new file mode 100644 index 0000000..00346ea --- /dev/null +++ b/src/external/tests/tinyrenderer/model.h @@ -0,0 +1,20 @@ +#ifndef __MODEL_H__ +#define __MODEL_H__ + +#include +#include "geometry.h" + +class Model { +private: + std::vector verts_; + std::vector > faces_; +public: + Model(const char *filename); + ~Model(); + int nverts(); + int nfaces(); + Vec3f vert(int i); + std::vector face(int idx); +}; + +#endif //__MODEL_H__