diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index 3153b2d..d327f75 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -11,13 +11,64 @@ #include -typedef struct ModelFace +typedef struct WavefrontModelFace { - i32 numVertex; - i32 *vertexIndex; - i32 *textureIndex; - i32 *normalIndex; -} ModelFace; + DqnArray vertexArray; + DqnArray textureArray; + DqnArray normalArray; +} WavefrontModelFace; + +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)); + + return result; +} + +typedef struct WavefrontModel +{ + // TODO(doyle): Fixed size + char *groupName[16]; + i32 groupNameIndex; + i32 groupSmoothing; + + DqnArray faces; +} WavefrontModel; + +typedef struct WavefrontObj +{ + DqnArray geometryArray; + DqnArray texUVArray; + DqnArray normalArray; + + WavefrontModel model; +} WavefrontObj; + +FILE_SCOPE bool ObjWaveFrontInit(WavefrontObj *const obj, const i32 vertexInitCapacity = 1000, + const i32 faceInitCapacity = 200) +{ + if (!obj) return false; + + bool initialised = false; + + initialised |= DqnArray_Init(&obj->geometryArray, vertexInitCapacity); + initialised |= DqnArray_Init(&obj->texUVArray, 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->normalArray); + DqnArray_Free(&obj->model.faces); + } + + return initialised; +} FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const memory, const char *const path) @@ -40,11 +91,6 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me return false; } -#if 0 - DqnV3 *vertex; - ModelFace *face; -#endif - enum WavefrontVertexType { WavefrontVertexType_Invalid, WavefrontVertexType_Geometric, @@ -52,17 +98,21 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me 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;) + WavefrontObj obj = {}; + if (!ObjWaveFrontInit(&obj)) { - switch (*scan) + DqnMemBuffer_EndTempRegion(tmpMemRegion); + return false; + } + + for (char *scan = (char *)rawBytes; scan && scan < ((char *)rawBytes + fileSize);) + { + switch (DqnChar_ToLower(*scan)) { - // Vertex Format: v[ |t|n|p] f32 f32 f32 [f32] + //////////////////////////////////////////////////////////////////// + // Polygonal Free Form Statement + //////////////////////////////////////////////////////////////////// + // Vertex Format: v[ |t|n|p] x y z [w] case 'v': { scan++; @@ -70,14 +120,15 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me enum WavefrontVertexType type = WavefrontVertexType_Invalid; - if (*scan == ' ') type = WavefrontVertexType_Geometric; - else if (*scan == 't' || *scan == 'n') + char identifier = DqnChar_ToLower(*scan); + if (identifier == ' ') type = WavefrontVertexType_Geometric; + else if (identifier == 't' || identifier == 'n') { scan++; - if (*scan == 't') type = WavefrontVertexType_Texture; - else type = WavefrontVertexType_Normal; + if (identifier == 't') type = WavefrontVertexType_Texture; + else type = WavefrontVertexType_Normal; } - else DQN_ASSERT(DQN_INVALID_CODE_PATH); + else DQN_ASSERT(DQN_INVALID_CODE_PATH); i32 vIndex = 0; DqnV4 v4 = {0, 0, 0, 1.0f}; @@ -109,15 +160,15 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me DQN_ASSERT(vIndex == 3 || vIndex == 4); if (type == WavefrontVertexType_Geometric) { - DqnArray_Push(&vGeometricArray, v4); + DqnArray_Push(&obj.geometryArray, v4); } else if (type == WavefrontVertexType_Texture) { - DqnArray_Push(&vTextureArray, v4.xyz); + DqnArray_Push(&obj.texUVArray, v4.xyz); } else if (type == WavefrontVertexType_Normal) { - DqnArray_Push(&vNormalArray, v4.xyz); + DqnArray_Push(&obj.normalArray, v4.xyz); } else { @@ -126,9 +177,175 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me } break; - // Face Format: i32/i32/i32 i32/i32/i32 i32/i32/i32 + //////////////////////////////////////////////////////////////////// + // Polygonal Geometry + //////////////////////////////////////////////////////////////////// + // Vertex numbers can be negative to reference a relative offset to + // the vertex which means the relative order of the vertices + // specified in the file, i.e. + + // v 0.000000 2.000000 2.000000 + // v 0.000000 0.000000 2.000000 + // v 2.000000 0.000000 2.000000 + // v 2.000000 2.000000 2.000000 + // f -4 -3 -2 -1 + + // Point Format: p v1 v2 v3 ... + // Each point is one vertex. + case 'p': + { + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + break; + + // Line Format: l v1/vt1 v2/vt2 v3/vt3 ... + // Texture vertex is optional. Minimum of two vertex numbers, no + // limit on maximum. + case 'l': + { + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + break; + + // Face Format: f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ... + // Minimum of three vertexes, no limit on maximum. vt, vn are + // optional. But specification of vt, vn must be consistent if given + // across all the vertices for the line. + + // For example, to specify only the vertex and vertex normal + // reference numbers, you would enter: + + // f 1//1 2//2 3//3 4//4 case 'f': { + scan++; + while (scan && (*scan == ' ' || *scan == '\n')) scan++; + if (!scan) continue; + + WavefrontModelFace face = ObjWavefrontModelFaceInit(); + i32 numVertexesParsed = 0; + bool moreVertexesToParse = true; + while (moreVertexesToParse) + { + enum WavefrontVertexType type = WavefrontVertexType_Geometric; + + // Read a vertexes 3 attributes v, vt, vn + for (i32 i = 0; i < 3; i++) + { + char *numStartPtr = scan; + while (scan && DqnChar_IsDigit(*scan)) + scan++; + + i32 numLen = (i32)((size_t)scan - (size_t)numStartPtr); + if (numLen > 0) + { + i32 index = (i32)Dqn_StrToI64(numStartPtr, numLen); + + if (type == WavefrontVertexType_Geometric) + { + DQN_ASSERT(DqnArray_Push(&face.vertexArray, index)); + } + else if (type == WavefrontVertexType_Texture) + { + DQN_ASSERT(DqnArray_Push(&face.textureArray, index)); + } + else if (type == WavefrontVertexType_Normal) + { + DQN_ASSERT(DqnArray_Push(&face.normalArray, index)); + } + } + + if (scan) scan++; + type = (enum WavefrontVertexType)((i32)type + 1); + } + numVertexesParsed++; + + if (scan) + { + // Move to next "non-empty" character + while (scan && (*scan == ' ' || *scan == '\n')) + scan++; + + // If it isn't a digit, then we've read all the + // vertexes for this face + if (!scan || (scan && !DqnChar_IsDigit(*scan))) + { + 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)); + } + break; + + //////////////////////////////////////////////////////////////////// + // Misc + //////////////////////////////////////////////////////////////////// + + // Group Name Format: g group_name1 group_name2 + // This is optional, if multiple groups are specified, then the + // following elements belong to all groups. The default group name + // is "default" + case 'g': + { + scan++; + while (scan && (*scan == ' ' || *scan == '\n')) scan++; + + if (!scan) continue; + + // Iterate to end of the name, i.e. move ptr to first space + char *namePtr = scan; + while (scan && (*scan != ' ' && *scan != '\n')) + scan++; + + if (scan) + { + i32 nameLen = (i32)((size_t)scan - (size_t)namePtr); + DQN_ASSERT(obj.model.groupNameIndex + 1 < DQN_ARRAY_COUNT(obj.model.groupName)); + + DQN_ASSERT(!obj.model.groupName[obj.model.groupNameIndex]); + obj.model.groupName[obj.model.groupNameIndex++] = (char *)DqnMemBuffer_Allocate( + &memory->permanentBuffer, (nameLen + 1) * sizeof(char)); + + for (i32 i = 0; i < nameLen; i++) + obj.model.groupName[obj.model.groupNameIndex - 1][i] = namePtr[i]; + + while (scan && (*scan == ' ' || *scan == '\n')) + scan++; + } + } + break; + + // Smoothing Group: s group_number + // Sets the smoothing group for the elements that follow it. If it's + // not to be used it can be specified as "off" or a value of 0. + case 's': + { + // Advance to first non space char after identifier + scan++; + while (scan && *scan == ' ' || *scan == '\n') scan++; + + if (scan && DqnChar_IsDigit(*scan)) + { + char *numStartPtr = scan; + while (scan && (*scan != ' ' && *scan != '\n')) + { + DQN_ASSERT(DqnChar_IsDigit(*scan)); + scan++; + } + + i32 numLen = (i32)((size_t)scan - (size_t)numStartPtr); + i32 groupSmoothing = (i32)Dqn_StrToI64(numStartPtr, numLen); + obj.model.groupSmoothing = groupSmoothing; + } + + while (scan && *scan == ' ' || *scan == '\n') scan++; } break; diff --git a/src/dqn.h b/src/dqn.h index ac49694..8c13e4f 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -507,8 +507,15 @@ typedef union DqnV3 f32 e[3]; } DqnV3; -// Create a vector using ints and typecast to floats -DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); +typedef union DqnV3i +{ + struct { i32 x, y, z; }; + struct { i32 r, g, b; }; + i32 e[3]; +} DqnV3i; + +// DqnV3 +DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnV3 DqnV3_Add (DqnV3 a, DqnV3 b); @@ -532,6 +539,10 @@ DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_S DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); } DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); } +// DqnV3i +DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z); +DQN_FILE_SCOPE DqnV3i DqnV3i_3f(f32 x, f32 y, f32 z); + //////////////////////////////////////////////////////////////////////////////// // Vec4 //////////////////////////////////////////////////////////////////////////////// @@ -2096,6 +2107,18 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Cross(DqnV3 a, DqnV3 b) return result; } +DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z) +{ + DqnV3i result = {x, y, z}; + return result; +} + +DQN_FILE_SCOPE DqnV3i DqnV3i_3f(f32 x, f32 y, f32 z) +{ + DqnV3i result = {(i32)x, (i32)y, (i32)z}; + return result; +} + //////////////////////////////////////////////////////////////////////////////// // Vec4 ////////////////////////////////////////////////////////////////////////////////