From ce539d49037c68009e8450f89873bda141d28581 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Sat, 20 May 2017 22:56:57 +1000 Subject: [PATCH] Start parser for Wavefront 3D Object file format --- src/DTRenderer.cpp | 223 ++++++++++++++++++++++++++++++++++++++++++++- src/dqn.h | 132 ++++++++++++++++++++++++--- 2 files changed, 343 insertions(+), 12 deletions(-) diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index 60d30c2..3153b2d 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -11,6 +11,153 @@ #include +typedef struct ModelFace +{ + i32 numVertex; + i32 *vertexIndex; + i32 *textureIndex; + i32 *normalIndex; +} ModelFace; + +FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const memory, + const char *const path) +{ + if (!memory || ! path) return false; + + PlatformFile file = {}; + if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read)) + return false; // TODO(doyle): Logging + + DqnTempBuffer tmpMemRegion = DqnMemBuffer_BeginTempRegion(&memory->transientBuffer); + u8 *rawBytes = (u8 *)DqnMemBuffer_Allocate(&memory->transientBuffer, file.size); + size_t bytesRead = api.FileRead(&file, rawBytes, file.size); + size_t fileSize = file.size; + api.FileClose(&file); + if (bytesRead != file.size) + { + // TODO(doyle): Logging + DqnMemBuffer_EndTempRegion(tmpMemRegion); + return false; + } + +#if 0 + DqnV3 *vertex; + ModelFace *face; +#endif + + enum WavefrontVertexType { + WavefrontVertexType_Invalid, + WavefrontVertexType_Geometric, + WavefrontVertexType_Texture, + WavefrontVertexType_Normal, + }; + + DqnArray vGeometricArray = {}; + DqnArray vTextureArray = {}; + DqnArray vNormalArray = {}; + DQN_ASSERT(DqnArray_Init(&vGeometricArray, 1000) && DqnArray_Init(&vTextureArray, 1000) && + DqnArray_Init(&vNormalArray, 1000)); + + for (char *scan = (char *)rawBytes; scan;) + { + switch (*scan) + { + // Vertex Format: v[ |t|n|p] f32 f32 f32 [f32] + case 'v': + { + scan++; + DQN_ASSERT(scan); + + enum WavefrontVertexType type = WavefrontVertexType_Invalid; + + if (*scan == ' ') type = WavefrontVertexType_Geometric; + else if (*scan == 't' || *scan == 'n') + { + scan++; + if (*scan == 't') type = WavefrontVertexType_Texture; + else type = WavefrontVertexType_Normal; + } + else DQN_ASSERT(DQN_INVALID_CODE_PATH); + + i32 vIndex = 0; + DqnV4 v4 = {0, 0, 0, 1.0f}; + + // Progress to first non space character after vertex identifier + for (; scan && *scan == ' '; scan++) + if (!scan) DQN_ASSERT(DQN_INVALID_CODE_PATH); + + for (;;) + { + char *f32StartPtr = scan; + for (; *scan != ' ' && *scan != '\n';) + { + DQN_ASSERT(DqnChar_IsDigit(*scan) || (*scan == '.') || (*scan == '-') || + *scan == 'e'); + scan++; + } + + 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++; + + if (!scan) break; + if (!(DqnChar_IsDigit(*scan) || *scan == '-')) break; + } + + DQN_ASSERT(vIndex == 3 || vIndex == 4); + if (type == WavefrontVertexType_Geometric) + { + DqnArray_Push(&vGeometricArray, v4); + } + else if (type == WavefrontVertexType_Texture) + { + DqnArray_Push(&vTextureArray, v4.xyz); + } + else if (type == WavefrontVertexType_Normal) + { + DqnArray_Push(&vNormalArray, v4.xyz); + } + else + { + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + } + break; + + // Face Format: i32/i32/i32 i32/i32/i32 i32/i32/i32 + case 'f': + { + } + break; + + // Comment + case '#': + { + // Skip comment line until new line + while (scan && *scan != '\n') + scan++; + + // Skip new lines and any leading white spaces + while (scan && (*scan == '\n' || *scan == ' ')) + scan++; + } + break; + + default: + { + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + break; + } + } + + DqnMemBuffer_EndTempRegion(tmpMemRegion); + + return true; +} + FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api, PlatformMemory *const memory, DTRFont *const font, const char *const path, @@ -1031,6 +1178,78 @@ void CompAssignment(PlatformRenderBuffer *const renderBuffer, PlatformInput *con } } +FILE_SCOPE void TestStrToF32Converter() +{ + const f32 EPSILON = 0.001f; + const char a[] = "-0.66248"; + f32 vA = Dqn_StrToF32(a, DQN_ARRAY_COUNT(a)); + DQN_ASSERT(DQN_ABS(vA) - DQN_ABS(-0.66248f) < EPSILON); + + const char b[] = "-0.632053"; + f32 vB = Dqn_StrToF32(b, DQN_ARRAY_COUNT(b)); + DQN_ASSERT(DQN_ABS(vB) - DQN_ABS(-0.632053f) < EPSILON); + + const char c[] = "-0.244271"; + f32 vC = Dqn_StrToF32(c, DQN_ARRAY_COUNT(c)); + DQN_ASSERT(DQN_ABS(vC) - DQN_ABS(-0.244271f) < EPSILON); + + const char d[] = "-0.511812"; + f32 vD = Dqn_StrToF32(d, DQN_ARRAY_COUNT(d)); + DQN_ASSERT(DQN_ABS(vD) - DQN_ABS(-0.511812f) < EPSILON); + + const char e[] = "-0.845392"; + f32 vE = Dqn_StrToF32(e, DQN_ARRAY_COUNT(e)); + DQN_ASSERT(DQN_ABS(vE) - DQN_ABS(-0.845392f) < EPSILON); + + const char f[] = "0.127809"; + f32 vF = Dqn_StrToF32(f, DQN_ARRAY_COUNT(f)); + DQN_ASSERT(DQN_ABS(vF) - DQN_ABS(-0.127809f) < EPSILON); + + const char g[] = "0.532"; + f32 vG = Dqn_StrToF32(g, DQN_ARRAY_COUNT(g)); + DQN_ASSERT(DQN_ABS(vG) - DQN_ABS(-0.532f) < EPSILON); + + const char h[] = "0.923"; + f32 vH = Dqn_StrToF32(h, DQN_ARRAY_COUNT(h)); + DQN_ASSERT(DQN_ABS(vH) - DQN_ABS(-0.923f) < EPSILON); + + const char i[] = "0.000"; + f32 vI = Dqn_StrToF32(i, DQN_ARRAY_COUNT(i)); + DQN_ASSERT(DQN_ABS(vI) - DQN_ABS(-0.000f) < EPSILON); + + const char j[] = "0.000283538"; + f32 vJ = Dqn_StrToF32(j, DQN_ARRAY_COUNT(j)); + DQN_ASSERT(DQN_ABS(vJ) - DQN_ABS(-0.000283538f) < EPSILON); + + const char k[] = "-1.25"; + f32 vK = Dqn_StrToF32(k, DQN_ARRAY_COUNT(k)); + DQN_ASSERT(DQN_ABS(vK) - DQN_ABS(-1.25f) < EPSILON); + + const char l[] = "0.286843"; + f32 vL = Dqn_StrToF32(l, DQN_ARRAY_COUNT(l)); + DQN_ASSERT(DQN_ABS(vL) - DQN_ABS(-0.286843f) < EPSILON); + + const char m[] = "-0.406"; + f32 vM = Dqn_StrToF32(m, DQN_ARRAY_COUNT(m)); + DQN_ASSERT(DQN_ABS(vM) - DQN_ABS(-0.406f) < EPSILON); + + const char n[] = "-0.892"; + f32 vN = Dqn_StrToF32(n, DQN_ARRAY_COUNT(n)); + DQN_ASSERT(DQN_ABS(vN) - DQN_ABS(-0.892f) < EPSILON); + + const char o[] = "0.201"; + f32 vO = Dqn_StrToF32(o, DQN_ARRAY_COUNT(o)); + DQN_ASSERT(DQN_ABS(vO) - DQN_ABS(-0.201f) < EPSILON); + + const char p[] = "1.25"; + f32 vP = Dqn_StrToF32(p, DQN_ARRAY_COUNT(p)); + DQN_ASSERT(DQN_ABS(vP) - DQN_ABS(1.25f) < EPSILON); + + const char q[] = "9.64635e-05"; + f32 vQ = Dqn_StrToF32(q, DQN_ARRAY_COUNT(q)); + DQN_ASSERT(DQN_ABS(vQ) - DQN_ABS(9.64635e-05) < EPSILON); +} + extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer, PlatformInput *const input, PlatformMemory *const memory) @@ -1045,6 +1264,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer, DTR_DEBUG_TIMED_FUNCTION(); if (!memory->isInit) { + TestStrToF32Converter(); DTR_DEBUG_TIMED_BLOCK("DTR_Update Memory Initialisation"); // NOTE(doyle): Do premultiply ourselves stbi_set_unpremultiply_on_load(true); @@ -1067,8 +1287,9 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const renderBuffer, &memory->transientBuffer); int x = 5; DqnMemBuffer_EndTempRegion(tmp); - } + ObjWavefrontLoad(input->api, memory, "african_head.obj"); + } DTRRender_Clear(renderBuffer, DqnV3_3f(0, 0, 0)); #if 1 diff --git a/src/dqn.h b/src/dqn.h index 4280f35..ac49694 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -28,9 +28,9 @@ typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; -typedef int32_t i64; +typedef int64_t i64; typedef int32_t i32; -typedef int64_t i16; +typedef int16_t i16; typedef double f64; typedef float f32; @@ -419,8 +419,8 @@ bool DqnArray_RemoveStable(DqnArray *array, u64 index) //////////////////////////////////////////////////////////////////////////////// // Math //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE f32 DqnMath_Lerp(f32 a, f32 t, f32 b); -DQN_FILE_SCOPE f32 DqnMath_Sqrtf(f32 a); +DQN_FILE_SCOPE f32 DqnMath_Lerp (f32 a, f32 t, f32 b); +DQN_FILE_SCOPE f32 DqnMath_Sqrtf (f32 a); DQN_FILE_SCOPE f32 DqnMath_Clampf(f32 val, f32 min, f32 max); //////////////////////////////////////////////////////////////////////////////// @@ -535,11 +535,14 @@ DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_E //////////////////////////////////////////////////////////////////////////////// // Vec4 //////////////////////////////////////////////////////////////////////////////// -typedef union DqnV4 -{ +typedef union DqnV4 { struct { f32 x, y, z, w; }; + DqnV3 xyz; + struct { f32 r, g, b, a; }; - f32 e[4]; + DqnV3 rgb; + + f32 e[4]; DqnV2 v2[2]; } DqnV4; @@ -626,12 +629,16 @@ DQN_FILE_SCOPE i32 Dqn_StrFindFirstOccurence(const char *const src, const i32 s DQN_FILE_SCOPE bool Dqn_StrHasSubstring (const char *const src, const i32 srcLen, const char *const find, const i32 findLen); #define DQN_32BIT_NUM_MAX_STR_SIZE 11 -#define DQN_64BIT_NUM_MAX_STR_SIZE 20 +#define DQN_64BIT_NUM_MAX_STR_SIZE 21 // Return the len of the derived string. If buf is NULL and or bufSize is 0 the // function returns the required string length for the integer. DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize); + DQN_FILE_SCOPE i64 Dqn_StrToI64(const char *const buf, const i32 bufSize); +// WARNING: Not robust, precision errors and whatnot but good enough! +DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize); + // Both return the number of bytes read, return 0 if invalid codepoint or UTF8 DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *dest, u32 character); DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character); @@ -2535,7 +2542,6 @@ DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize) return 1; } - // NOTE(doyle): Max 32bit integer (+-)2147483647 i32 charIndex = 0; bool negative = false; if (value < 0) negative = true; @@ -2546,12 +2552,30 @@ DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize) charIndex++; } - i32 val = DQN_ABS(value); + bool lastDigitDecremented = false; + i64 val = DQN_ABS(value); + if (val < 0) + { + // TODO(doyle): This will occur if we are checking the smallest number + // possible in i64 since the range of negative numbers is one more than + // it is for positives, so ABS will fail. + lastDigitDecremented = true; + val = DQN_ABS(val - 1); + DQN_ASSERT(val >= 0); + } + if (validBuffer) { + if (lastDigitDecremented) + { + i64 rem = (val % 10) + 1; + buf[charIndex++] = (u8)rem + '0'; + val /= 10; + } + while (val != 0 && charIndex < bufSize) { - i32 rem = val % 10; + i64 rem = val % 10; buf[charIndex++] = (u8)rem + '0'; val /= 10; } @@ -2616,6 +2640,92 @@ DQN_FILE_SCOPE i64 Dqn_StrToI64(const char *const buf, const i32 bufSize) return result; } +DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize) +{ + if (!buf || bufSize == 0) return 0; + + i32 index = 0; + bool isNegative = false; + if (buf[index] == '-') + { + index++; + isNegative = true; + } + + bool isPastDecimal = false; + i32 numDigitsAfterDecimal = 0; + i32 rawNumber = 0; + + f32 digitShiftValue = 1.0f; + f32 digitShiftMultiplier = 0.1f; + for (i32 i = index; i < bufSize; i++) + { + char ch = buf[i]; + if (ch == '.') + { + isPastDecimal = true; + continue; + } + // Handle scientific notation + else if (ch == 'e') + { + bool digitShiftIsPositive = true; + if (i < bufSize) + { + if (buf[i + 1] == '-') digitShiftIsPositive = false; + DQN_ASSERT(buf[i + 1] == '-' || buf[i + 1] == '+'); + i += 2; + } + + i32 exponentPow = 0; + bool scientificNotation = false; + while (i < bufSize) + { + scientificNotation = true; + char exponentCh = buf[i]; + if (DqnChar_IsDigit(exponentCh)) + { + exponentPow *= 10; + exponentPow += (buf[i] - '0'); + } + else + { + i = bufSize; + } + + i++; + } + + // NOTE(doyle): If exponent not specified but this branch occurred, + // the float string has a malformed scientific notation in the + // string, i.e. "e" followed by no number. + DQN_ASSERT(scientificNotation); + + numDigitsAfterDecimal += exponentPow; + if (digitShiftIsPositive) digitShiftMultiplier = 10.0f; + } + else if (DqnChar_IsDigit(ch)) + { + numDigitsAfterDecimal += (i32)isPastDecimal; + rawNumber *= 10; + rawNumber += (ch - '0'); + } + else + { + break; + } + } + + for (i32 i = 0; i < numDigitsAfterDecimal; i++) + digitShiftValue *= digitShiftMultiplier; + + f32 result = (f32)rawNumber; + if (numDigitsAfterDecimal > 0) result *= digitShiftValue; + if (isNegative) result *= -1; + + return result; +} + /* Encoding The following byte sequences are used to represent a character. The sequence