From 89171b0d2e678e59c6e5cbe51e59f0f535a300cb Mon Sep 17 00:00:00 2001 From: Bly7 Date: Mon, 12 Sep 2016 21:35:05 -0700 Subject: [PATCH 01/29] :confetti_ball: Added .gitattributes & .gitignore files --- .gitattributes | 17 +++++++++++++++++ .gitignore | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bdb0cabc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cd2946ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk From e1b61f4037728fa1cd93b4986b9167b3a087eaac Mon Sep 17 00:00:00 2001 From: Bly7 Date: Mon, 12 Sep 2016 21:45:41 -0700 Subject: [PATCH 02/29] Initial Commit --- README.md | 1 + Source/Source Code Folder | 0 2 files changed, 1 insertion(+) create mode 100644 README.md create mode 100644 Source/Source Code Folder diff --git a/README.md b/README.md new file mode 100644 index 00000000..b5b8e9ab --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# OBJ LOADER \ No newline at end of file diff --git a/Source/Source Code Folder b/Source/Source Code Folder new file mode 100644 index 00000000..e69de29b From cca7fadb5f3db7e11ceb9bdc87fb1fbaef5acd1c Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 13:25:53 -0700 Subject: [PATCH 03/29] Load OBJ_Lader.h v1 Fully Functional header file --- .gitignore | 3 + Source/OBJ_Loader.h | 642 ++++++++++++++++++++++++++++++++++++++ Source/Source Code Folder | 0 3 files changed, 645 insertions(+) create mode 100644 Source/OBJ_Loader.h delete mode 100644 Source/Source Code Folder diff --git a/.gitignore b/.gitignore index cd2946ad..94e381f2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# Visual Studio Directory +Visual_Studio/ + # ========================= # Operating System Files # ========================= diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h new file mode 100644 index 00000000..a96a3fb7 --- /dev/null +++ b/Source/OBJ_Loader.h @@ -0,0 +1,642 @@ +#pragma once + +#include + +#include + +#include + +namespace objl +{ + struct Vector2 + { + Vector2() + { + + } + Vector2(float X_, float Y_) + { + X = X_; + Y = Y_; + } + bool operator==(const Vector2& other) const + { + return (this->X == other.X && this->Y == other.Y); + } + bool operator!=(const Vector2& other) const + { + return !(this->X == other.X && this->Y == other.Y); + } + Vector2 operator+(const Vector2& right) const + { + return Vector2(this->X + right.X, this->Y + right.Y); + } + Vector2 operator-(const Vector2& right) const + { + return Vector2(this->X - right.X, this->Y - right.Y); + } + Vector2 operator*(const float& other) const + { + return Vector2(this->X *other, this->Y * other); + } + + float X, Y = 0.0f; + }; + + struct Vector3 + { + Vector3() + { + + } + Vector3(float X_, float Y_, float Z_) + { + X = X_; + Y = Y_; + Z = Z_; + } + bool operator==(const Vector3& other) const + { + return (this->X == other.X && this->Y == other.Y && this->Z == other.Z); + } + bool operator!=(const Vector3& other) const + { + return !(this->X == other.X && this->Y == other.Y && this->Z == other.Z); + } + Vector3 operator+(const Vector3& right) const + { + return Vector3(this->X + right.X, this->Y + right.Y, this->Z + right.Z); + } + Vector3 operator-(const Vector3& right) const + { + return Vector3(this->X - right.X, this->Y - right.Y, this->Z - right.Z); + } + Vector3 operator*(const float& other) const + { + return Vector3(this->X *other, this->Y * other, this->Z - other); + } + + float X, Y, Z = 0.0f; + }; + + Vector3 operator*(const float& left, const Vector3& right) + { + return Vector3(right.X * left, right.Y * left, right.Z * left); + } + + inline void split(const std::string &in, + std::vector &out, + std::string token) + { + out.clear(); + + std::string temp; + + for (int i = 0; i < int(in.size()); i++) + { + std::string test = in.substr(i, token.size()); + + if (test == token) + { + if (!temp.empty()) + { + out.push_back(temp); + temp.clear(); + i += token.size() - 1; + } + else + { + out.push_back(""); + } + } + else if (i + token.size() >= in.size()) + { + temp += in.substr(i, token.size()); + out.push_back(temp); + break; + } + else + { + temp += in[i]; + } + } + } + + Vector3 CrossV3(const Vector3 a, const Vector3 b) + { + return Vector3(a.Y * b.Z - a.Z * b.Y, + a.Z * b.X - a.X * b.Z, + a.X * b.Y - a.Y * b.X); + } + + float MagnitudeV3(const Vector3 in) + { + return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2))); + } + + float DotV3(const Vector3 a, const Vector3 b) + { + return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z); + } + + bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) + { + // Starting vars + Vector3 u = tri2 - tri1; + Vector3 v = tri3 - tri1; + Vector3 w = point - tri1; + Vector3 n = CrossV3(u, v); + + float y = (DotV3(CrossV3(u, w), n) / DotV3(n, n)); + float b = (DotV3(CrossV3(u, w), n) / DotV3(n, n)); + float a = 1 - y - b; + + // Projected point + Vector3 p = (a * tri1) + (b * tri2) + (y * tri3); + + if (a >= 0 && a <= 1 + && b >= 0 && b <= 1 + && y >= 0 && y <= 1) + { + return true; + } + else + return false; + } + + + + float AngleBetweenV3(const Vector3 a, const Vector3 b) + { + float angle = DotV3(a, b); + angle /= (MagnitudeV3(a) * MagnitudeV3(b)); + return angle = acosf(angle); + } + + struct Vertex + { + // Position Vector + Vector3 Position; + + // Normal Vector + Vector3 Normal; + + // Texture Coordinate Vector + Vector2 TextureCoordinate; + }; + + struct Mesh + { + Mesh() + { + + } + Mesh(std::vector& _Vertices, std::vector& _Indices) + { + Vertices = _Vertices; + Indices = _Indices; + } + std::string MeshName; + std::vector Vertices; + std::vector Indices; + }; + + class Loader + { + public: + Loader() + { + + } + + bool LoadFile(std::string Path) + { + std::ifstream file(Path); + + if (!file.is_open()) + return false; + + LoadedMeshes.clear(); + LoadedVertices.clear(); + LoadedIndices.clear(); + + std::vector Positions; + std::vector TCoords; + std::vector Normals; + + std::vector Vertices; + std::vector Indices; + + bool listening = false; + std::string meshname; + + Mesh tempMesh; + + std::string curline; + while (std::getline(file, curline)) + { + if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g " || curline[0] == 'g') + { + if (!listening) + { + listening = true; + + if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g ") + { + meshname = curline.substr(2, curline.size()); + } + else + { + meshname = "unnamed"; + } + } + else + { + // Generate the mesh to put into the array + + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + + // Cleanup + Vertices.clear(); + Indices.clear(); + meshname.clear(); + + meshname = curline.substr(2, curline.size()); + } + else + { + if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g ") + { + meshname = curline.substr(2, curline.size()); + } + else + { + meshname = "unnamed"; + } + } + } + } + if (curline.substr(0, 2) == "v ") + { + std::vector spos; + Vector3 vpos; + split(curline.substr(2, curline.size() - 1), spos, " "); + + vpos.X = std::stof(spos[0]); + vpos.Y = std::stof(spos[1]); + vpos.Z = std::stof(spos[2]); + + Positions.push_back(vpos); + } + if (curline.substr(0, 3) == "vt ") + { + std::vector stex; + Vector2 vtex; + split(curline.substr(3, curline.size()), stex, " "); + + vtex.X = std::stof(stex[0]); + vtex.Y = std::stof(stex[1]); + + TCoords.push_back(vtex); + } + if (curline.substr(0, 3) == "vn ") + { + std::vector snor; + Vector3 vnor; + split(curline.substr(3, curline.size()), snor, " "); + + vnor.X = std::stof(snor[0]); + vnor.Y = std::stof(snor[1]); + vnor.Z = std::stof(snor[2]); + + Normals.push_back(vnor); + } + if (curline.substr(0, 2) == "f ") + { + // Generate the vertices + std::vector vVerts; + GenVerticesFromRawOBJ(vVerts, Positions, TCoords, Normals, curline); + + // Add Vertices + for (int i = 0; i < int(vVerts.size()); i++) + { + Vertices.push_back(vVerts[i]); + } + + std::vector iIndices; + + VertexTriangluation(iIndices, vVerts); + + // Add Indices + for (int i = 0; i < int(iIndices.size()); i++) + { + int indnum = ((Vertices.size()) - vVerts.size()) + iIndices[i]; + Indices.push_back(indnum); + } + } + } + + // Deal with last mesh + + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + } + + file.close(); + } + + std::vector LoadedMeshes; + std::vector LoadedVertices; + std::vector LoadedIndices; + + private: + void GenVerticesFromRawOBJ(std::vector& oVerts, + const std::vector& iPositions, + const std::vector& iTCoords, + const std::vector& iNormals, + std::string icurline) + { + std::vector sface, svert; + Vertex vVert; + split(icurline.substr(2, icurline.size()), sface, " "); + + bool noNormal = false; + + // For every given vertex do this + for (int i = 0; i < int(sface.size()); i++) + { + // See What type the vertex is. + int vtype; + + split(sface[i], svert, "/"); + + // Check for just position - v1 + if (svert.size() == 1) + { + // Only position + vtype = 1; + } + + // Check for position & texture - v1/vt1 + if (svert.size() == 2) + { + // Position & Texture + vtype = 2; + } + + // Check for Position, Texture and Normal - v1/vt1/vn1 + // or if Position and Normal - v1//vn1 + if (svert.size() == 3) + { + if (svert[1] != "") + { + // Position, Texture, and Normal + vtype = 4; + } + else + { + // Position & Normal + vtype = 3; + } + } + + // Calculate and store the vertex + switch (vtype) + { + case 1: // P + { + vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.TextureCoordinate = Vector2(0, 0); + noNormal = true; + oVerts.push_back(vVert); + break; + } + case 2: // P/T + { + vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.TextureCoordinate = iTCoords[stoi(svert[1]) - 1]; + noNormal = true; + oVerts.push_back(vVert); + break; + } + case 3: // P//N + { + vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.TextureCoordinate = Vector2(0, 0); + vVert.Normal = iNormals[stoi(svert[2]) - 1]; + oVerts.push_back(vVert); + break; + } + case 4: // P/T/N + { + vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.TextureCoordinate = iTCoords[stoi(svert[1]) - 1]; + vVert.Normal = iNormals[stoi(svert[2]) - 1]; + oVerts.push_back(vVert); + break; + } + default: + { + break; + } + } + } + + // take care of missing normals + // these may not be truly acurate but it is the + // best they get for not compiling a mesh with normals + if (noNormal) + { + Vector3 A = oVerts[0].Position - oVerts[1].Position; + Vector3 B = oVerts[2].Position - oVerts[1].Position; + + Vector3 normal = CrossV3(A, B); + + for (int i = 0; i < int(oVerts.size()); i++) + { + oVerts[i].Normal = normal; + } + } + } + + void VertexTriangluation(std::vector& oIndices, + const std::vector& iVerts) + { + // If there are 2 or less verts, + // no triangle can be created, + // so exit + if (iVerts.size() < 3) + { + return; + } + // If it is a triangle no need to calculate it + if (iVerts.size() == 3) + { + oIndices.push_back(0); + oIndices.push_back(1); + oIndices.push_back(2); + return; + } + + // Create a list of vertices + std::vector tVerts = iVerts; + + while (true) + { + // For every vertex + for (int i = 0; i < int(tVerts.size()); i++) + { + // pPrev = the previous vertex in the list + Vertex pPrev; + if (i == 0) + { + pPrev = tVerts[tVerts.size() - 1]; + } + else + { + pPrev = tVerts[i - 1]; + } + + // pCur = the current vertex; + Vertex pCur = tVerts[i]; + + // pNext = the next vertex in the list + Vertex pNext; + if (i == tVerts.size() - 1) + { + pNext = tVerts[0]; + } + else + { + pNext = tVerts[i + 1]; + } + + // Check to see if there are only 3 verts left + // if so this is the last triangle + if (tVerts.size() == 3) + { + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(tVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + tVerts.clear(); + break; + } + if (tVerts.size() == 4) + { + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + Vector3 tempVec; + for (int j = 0; j < int(tVerts.size()); j++) + { + if (tVerts[j].Position != pCur.Position + && tVerts[j].Position != pPrev.Position + && tVerts[j].Position != pNext.Position) + { + tempVec = tVerts[j].Position; + break; + } + } + + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + if (iVerts[j].Position == tempVec) + oIndices.push_back(j); + } + + tVerts.clear(); + break; + } + + // If Vertex is not an interior vertex + float angle = AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359); + if (angle <= 0 && angle >= 180) + continue; + + // If any vertices are within this triangle + bool inTri = false; + for (int j = 0; j < int(iVerts.size()); j++) + { + if (inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position, pNext.Position) + && iVerts[j].Position != pPrev.Position + && iVerts[j].Position != pCur.Position + && iVerts[j].Position != pNext.Position) + { + inTri = true; + break; + } + } + if (inTri) + continue; + + // Create a triangle from pCur, pPrev, pNext + for (int j = 0; j < int(iVerts.size()); j++) + { + if (iVerts[j].Position == pCur.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pPrev.Position) + oIndices.push_back(j); + if (iVerts[j].Position == pNext.Position) + oIndices.push_back(j); + } + + // Delete pCur from the list + for (int j = 0; j < int(tVerts.size()); j++) + { + if (tVerts[j].Position == pCur.Position) + { + tVerts.erase(tVerts.begin() + j); + break; + } + } + + // reset i to the start + // -1 since loop will add 1 to it + i = -1; + } + + // if no triangles were created + if (oIndices.size() == 0) + break; + + // if no more vertices + if (tVerts.size() == 0) + break; + } + } + }; +} \ No newline at end of file diff --git a/Source/Source Code Folder b/Source/Source Code Folder deleted file mode 100644 index e69de29b..00000000 From 4dbab790dd3a1138f7ad58a66f34bc04751260df Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 14:26:00 -0700 Subject: [PATCH 04/29] Commented and Modified Layout of OBJ_Loader.h --- Source/OBJ_Loader.h | 317 +++++++++++++++++++++++++++++--------------- 1 file changed, 207 insertions(+), 110 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index a96a3fb7..5579eddc 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -1,178 +1,123 @@ #pragma once +// Vector - STD Vector/Array Library #include +// String - STD String Library #include +// fStream - STD File I/O Library #include +// Namespace: OBJL +// +// Description: The namespace that holds eveyrthing that +// is needed and used for the OBJ Model Loader namespace objl { + // Structure: Vector2 + // + // Description: A 2D Vector that Holds Positional Data struct Vector2 { + // Default Constructor Vector2() { - + X = 0.0f; + Y = 0.0f; } + // Variable Set Constructor Vector2(float X_, float Y_) { X = X_; Y = Y_; } + // Bool Equals Operator Overload bool operator==(const Vector2& other) const { return (this->X == other.X && this->Y == other.Y); } + // Bool Not Equals Operator Overload bool operator!=(const Vector2& other) const { return !(this->X == other.X && this->Y == other.Y); } + // Addition Operator Overload Vector2 operator+(const Vector2& right) const { return Vector2(this->X + right.X, this->Y + right.Y); } + // Subtraction Operator Overload Vector2 operator-(const Vector2& right) const { return Vector2(this->X - right.X, this->Y - right.Y); } + // Float Multiplication Operator Overload Vector2 operator*(const float& other) const { return Vector2(this->X *other, this->Y * other); } - float X, Y = 0.0f; + // Positional Variables + float X; + float Y; }; + // Structure: Vector3 + // + // Description: A 3D Vector that Holds Positional Data struct Vector3 { + // Default Constructor Vector3() { - + X = 0.0f; + Y = 0.0f; + Z = 0.0f; } + // Variable Set Constructor Vector3(float X_, float Y_, float Z_) { X = X_; Y = Y_; Z = Z_; } + // Bool Equals Operator Overload bool operator==(const Vector3& other) const { return (this->X == other.X && this->Y == other.Y && this->Z == other.Z); } + // Bool Not Equals Operator Overload bool operator!=(const Vector3& other) const { return !(this->X == other.X && this->Y == other.Y && this->Z == other.Z); } + // Addition Operator Overload Vector3 operator+(const Vector3& right) const { return Vector3(this->X + right.X, this->Y + right.Y, this->Z + right.Z); } + // Subtraction Operator Overload Vector3 operator-(const Vector3& right) const { return Vector3(this->X - right.X, this->Y - right.Y, this->Z - right.Z); } + // Float Multiplication Operator Overload Vector3 operator*(const float& other) const { return Vector3(this->X *other, this->Y * other, this->Z - other); } - float X, Y, Z = 0.0f; + // Positional Variables + float X; + float Y; + float Z; }; - Vector3 operator*(const float& left, const Vector3& right) - { - return Vector3(right.X * left, right.Y * left, right.Z * left); - } - - inline void split(const std::string &in, - std::vector &out, - std::string token) - { - out.clear(); - - std::string temp; - - for (int i = 0; i < int(in.size()); i++) - { - std::string test = in.substr(i, token.size()); - - if (test == token) - { - if (!temp.empty()) - { - out.push_back(temp); - temp.clear(); - i += token.size() - 1; - } - else - { - out.push_back(""); - } - } - else if (i + token.size() >= in.size()) - { - temp += in.substr(i, token.size()); - out.push_back(temp); - break; - } - else - { - temp += in[i]; - } - } - } - - Vector3 CrossV3(const Vector3 a, const Vector3 b) - { - return Vector3(a.Y * b.Z - a.Z * b.Y, - a.Z * b.X - a.X * b.Z, - a.X * b.Y - a.Y * b.X); - } - - float MagnitudeV3(const Vector3 in) - { - return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2))); - } - - float DotV3(const Vector3 a, const Vector3 b) - { - return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z); - } - - bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) - { - // Starting vars - Vector3 u = tri2 - tri1; - Vector3 v = tri3 - tri1; - Vector3 w = point - tri1; - Vector3 n = CrossV3(u, v); - - float y = (DotV3(CrossV3(u, w), n) / DotV3(n, n)); - float b = (DotV3(CrossV3(u, w), n) / DotV3(n, n)); - float a = 1 - y - b; - - // Projected point - Vector3 p = (a * tri1) + (b * tri2) + (y * tri3); - - if (a >= 0 && a <= 1 - && b >= 0 && b <= 1 - && y >= 0 && y <= 1) - { - return true; - } - else - return false; - } - - - - float AngleBetweenV3(const Vector3 a, const Vector3 b) - { - float angle = DotV3(a, b); - angle /= (MagnitudeV3(a) * MagnitudeV3(b)); - return angle = acosf(angle); - } - + // Structure: Vertex + // + // Description: Model Vertex object that holds + // a Position, Normal, and Texture Coordinate struct Vertex { // Position Vector @@ -185,30 +130,162 @@ namespace objl Vector2 TextureCoordinate; }; + // Structure: Mesh + // + // Description: A Simple Mesh Object that holds + // a name, a vertex list, and an index list struct Mesh { + // Default Constructor Mesh() { } + // Variable Set Constructor Mesh(std::vector& _Vertices, std::vector& _Indices) { Vertices = _Vertices; Indices = _Indices; } + // Mesh Name std::string MeshName; + // Vertex List std::vector Vertices; + // Index List std::vector Indices; }; + // Namespace: Math + // + // Description: The namespace that holds all of the math + // functions need for OBJL + namespace math + { + // Vector3 Cross Product + Vector3 CrossV3(const Vector3 a, const Vector3 b) + { + return Vector3(a.Y * b.Z - a.Z * b.Y, + a.Z * b.X - a.X * b.Z, + a.X * b.Y - a.Y * b.X); + } + + // Vector3 Magnitude Calculation + float MagnitudeV3(const Vector3 in) + { + return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2))); + } + + // Vector3 DotProduct + float DotV3(const Vector3 a, const Vector3 b) + { + return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z); + } + + // Angle between 2 Vector3 Objects + float AngleBetweenV3(const Vector3 a, const Vector3 b) + { + float angle = DotV3(a, b); + angle /= (MagnitudeV3(a) * MagnitudeV3(b)); + return angle = acosf(angle); + } + } + + // Namespace: Algorithm + // + // Description: The namespace that holds all of the + // Algorithms needed for OBJL + namespace algorithm + { + // Vector3 Multiplication Opertor Overload + Vector3 operator*(const float& left, const Vector3& right) + { + return Vector3(right.X * left, right.Y * left, right.Z * left); + } + + // Check to see if a Vector3 Point is within a 3 Vector3 Triangle + bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) + { + // Starting vars + Vector3 u = tri2 - tri1; + Vector3 v = tri3 - tri1; + Vector3 w = point - tri1; + Vector3 n = math::CrossV3(u, v); + + float y = (math::DotV3(math::CrossV3(u, w), n) / math::DotV3(n, n)); + float b = (math::DotV3(math::CrossV3(u, w), n) / math::DotV3(n, n)); + float a = 1 - y - b; + + // Projected point + Vector3 p = (a * tri1) + (b * tri2) + (y * tri3); + + if (a >= 0 && a <= 1 + && b >= 0 && b <= 1 + && y >= 0 && y <= 1) + { + return true; + } + else + return false; + } + + // Split a String into a string array at a given token + inline void split(const std::string &in, + std::vector &out, + std::string token) + { + out.clear(); + + std::string temp; + + for (int i = 0; i < int(in.size()); i++) + { + std::string test = in.substr(i, token.size()); + + if (test == token) + { + if (!temp.empty()) + { + out.push_back(temp); + temp.clear(); + i += token.size() - 1; + } + else + { + out.push_back(""); + } + } + else if (i + token.size() >= in.size()) + { + temp += in.substr(i, token.size()); + out.push_back(temp); + break; + } + else + { + temp += in[i]; + } + } + } + } + + // Class: Loader + // + // Description: The OBJ Model Loader class Loader { public: + // Default Constructor Loader() { } + // Load a file into the loader + // + // If file is loaded return true + // + // If the file is unable to be found + // or unable to be loaded return false bool LoadFile(std::string Path) { std::ifstream file(Path); @@ -217,8 +294,8 @@ namespace objl return false; LoadedMeshes.clear(); - LoadedVertices.clear(); - LoadedIndices.clear(); + //LoadedVertices.clear(); + //LoadedIndices.clear(); std::vector Positions; std::vector TCoords; @@ -235,6 +312,7 @@ namespace objl std::string curline; while (std::getline(file, curline)) { + // Generate a Mesh Object or Prepare for an object to be created if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g " || curline[0] == 'g') { if (!listening) @@ -283,11 +361,12 @@ namespace objl } } } + // Generate a Vertex Position if (curline.substr(0, 2) == "v ") { std::vector spos; Vector3 vpos; - split(curline.substr(2, curline.size() - 1), spos, " "); + algorithm::split(curline.substr(2, curline.size() - 1), spos, " "); vpos.X = std::stof(spos[0]); vpos.Y = std::stof(spos[1]); @@ -295,22 +374,24 @@ namespace objl Positions.push_back(vpos); } + // Generate a Vertex Texture Coordinate if (curline.substr(0, 3) == "vt ") { std::vector stex; Vector2 vtex; - split(curline.substr(3, curline.size()), stex, " "); + algorithm::split(curline.substr(3, curline.size()), stex, " "); vtex.X = std::stof(stex[0]); vtex.Y = std::stof(stex[1]); TCoords.push_back(vtex); } + // Generate a Vertex Normal; if (curline.substr(0, 3) == "vn ") { std::vector snor; Vector3 vnor; - split(curline.substr(3, curline.size()), snor, " "); + algorithm::split(curline.substr(3, curline.size()), snor, " "); vnor.X = std::stof(snor[0]); vnor.Y = std::stof(snor[1]); @@ -318,6 +399,7 @@ namespace objl Normals.push_back(vnor); } + // Generate a Face (vertices & indices) if (curline.substr(0, 2) == "f ") { // Generate the vertices @@ -328,6 +410,7 @@ namespace objl for (int i = 0; i < int(vVerts.size()); i++) { Vertices.push_back(vVerts[i]); + } std::vector iIndices; @@ -339,6 +422,7 @@ namespace objl { int indnum = ((Vertices.size()) - vVerts.size()) + iIndices[i]; Indices.push_back(indnum); + } } } @@ -356,13 +440,24 @@ namespace objl } file.close(); + + if (LoadedMeshes.empty())//&& LoadedVertices.empty() && LoadedIndices.empty()) + { + return false; + } + else + { + return true; + } } std::vector LoadedMeshes; - std::vector LoadedVertices; - std::vector LoadedIndices; + //std::vector LoadedVertices; + //std::vector LoadedIndices; private: + // Generate vertices from a list of positions, + // tcoords, normals and a face line void GenVerticesFromRawOBJ(std::vector& oVerts, const std::vector& iPositions, const std::vector& iTCoords, @@ -371,7 +466,7 @@ namespace objl { std::vector sface, svert; Vertex vVert; - split(icurline.substr(2, icurline.size()), sface, " "); + algorithm::split(icurline.substr(2, icurline.size()), sface, " "); bool noNormal = false; @@ -381,7 +476,7 @@ namespace objl // See What type the vertex is. int vtype; - split(sface[i], svert, "/"); + algorithm::split(sface[i], svert, "/"); // Check for just position - v1 if (svert.size() == 1) @@ -463,7 +558,7 @@ namespace objl Vector3 A = oVerts[0].Position - oVerts[1].Position; Vector3 B = oVerts[2].Position - oVerts[1].Position; - Vector3 normal = CrossV3(A, B); + Vector3 normal = math::CrossV3(A, B); for (int i = 0; i < int(oVerts.size()); i++) { @@ -472,6 +567,8 @@ namespace objl } } + // Triangulate a list of vertices into a face by printing + // inducies corresponding with triangles within it void VertexTriangluation(std::vector& oIndices, const std::vector& iVerts) { @@ -583,7 +680,7 @@ namespace objl } // If Vertex is not an interior vertex - float angle = AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359); + float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359); if (angle <= 0 && angle >= 180) continue; @@ -591,7 +688,7 @@ namespace objl bool inTri = false; for (int j = 0; j < int(iVerts.size()); j++) { - if (inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position, pNext.Position) + if (algorithm::inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position, pNext.Position) && iVerts[j].Position != pPrev.Position && iVerts[j].Position != pCur.Position && iVerts[j].Position != pNext.Position) From 5bb850db3130bc6836f2f7d747bcb1747fe7278b Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 14:29:18 -0700 Subject: [PATCH 05/29] Added a Destructor in Loader Class --- Source/OBJ_Loader.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 5579eddc..07af0090 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -279,6 +279,10 @@ namespace objl { } + ~Loader() + { + LoadedMeshes.clear(); + } // Load a file into the loader // From 4526d8478eef63cadd0daee1436018fa41be18e8 Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 14:42:19 -0700 Subject: [PATCH 06/29] Modified README File --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5b8e9ab..a5662a82 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# OBJ LOADER \ No newline at end of file +## OBJ LOADER + +# Quick Description + +OBJ Loader is a simple .obj File Loader + +# Slightly Longer Description + +OBJ Loader is a simple .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. \ No newline at end of file From e39dde53f7189f064fd95636e4accdba76e9592b Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 15:10:59 -0700 Subject: [PATCH 07/29] Added LICENSE, Modified README and OBJ_LOADER.h --- LICENSE.md | 21 ++++++++++++++++++ README.md | 53 +++++++++++++++++++++++++++++++++++++++++---- Source/OBJ_Loader.h | 5 +++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..7b47e98e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Robert Smith + +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. \ No newline at end of file diff --git a/README.md b/README.md index a5662a82..ba5d09ac 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,54 @@ -## OBJ LOADER +# OBJ LOADER -# Quick Description +## Quick Description OBJ Loader is a simple .obj File Loader -# Slightly Longer Description +## Slightly Longer Description -OBJ Loader is a simple .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. \ No newline at end of file +OBJ Loader is a simple .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. + +## Quick Use Guide + +1. Include OBJ_Loader.h #include "OBJ_Loader.h" +2. Create a Loader Object objl::Loader loader +3. Load a File Into the Loader Object loader.LoadFile("path_to_object_and_name.obj") +4. Get What you want out of the Mesh Objects cout << loader.LoadedMeshes[0].MeshName << endl + +## Classes & Structures + +These are all of the included classes and only the relevant members and methods. There are others to find if you want but these are all you will need to know to operate. + +### Mesh + +std::string MeshName The Mesh Name given in the .obj +std::vector Vertices Vertex List +std::vector Indices Index List + +### Vertex + +Vector3 Position Position vector +Vector3 Normal Normal vector +Vector2 TextureCoordinate Texture Coordinate vector + +### Vector3 + +float X, Y, Z XYZ Position Variables + +### Vector2 + +float X, Y XY Position Variables + +### Loader + +bool LoadFile(sstd::string Path) Load a file from a path + Return true if found and loaded + return false if not + +std::vector LoadedMeshes Loaded Mesh Objects + + + +## License + +OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE.md \ No newline at end of file diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 07af0090..9fdb4dab 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -292,6 +292,11 @@ namespace objl // or unable to be loaded return false bool LoadFile(std::string Path) { + // If the file is not an .obj file return false + if (Path.substr(Path.size() - 4, 4) != ".obj") + return false; + + std::ifstream file(Path); if (!file.is_open()) From 2a5865c17a82eb4866d12953c9fb2983851763da Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 15:16:59 -0700 Subject: [PATCH 08/29] Updated README --- README.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ba5d09ac..a2489c36 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ OBJ Loader is a simple .obj model file loader that will take in a path to a file ## Quick Use Guide -1. Include OBJ_Loader.h #include "OBJ_Loader.h" -2. Create a Loader Object objl::Loader loader -3. Load a File Into the Loader Object loader.LoadFile("path_to_object_and_name.obj") -4. Get What you want out of the Mesh Objects cout << loader.LoadedMeshes[0].MeshName << endl +1. Include OBJ_Loader.h: '#include "OBJ_Loader.h"' +2. Create a Loader Object: 'objl::Loader loader' +3. Load a File Into the Loader Object: 'loader.LoadFile("path_to_object_and_name.obj")' +4. Get What you want out of the Mesh Objects: 'cout << loader.LoadedMeshes[0].MeshName << endl' ## Classes & Structures @@ -21,31 +21,29 @@ These are all of the included classes and only the relevant members and methods. ### Mesh -std::string MeshName The Mesh Name given in the .obj -std::vector Vertices Vertex List -std::vector Indices Index List +'std::string MeshName' : The Mesh Name given in the .obj +'std::vector Vertices' : Vertex List +'std::vector Indices' : Index List ### Vertex -Vector3 Position Position vector -Vector3 Normal Normal vector -Vector2 TextureCoordinate Texture Coordinate vector +'Vector3 Position' : Position vector +'Vector3 Normal' : Normal vector +'Vector2 TextureCoordinate' : Texture Coordinate vector ### Vector3 -float X, Y, Z XYZ Position Variables +'float X, Y, Z' : XYZ Position Variables ### Vector2 -float X, Y XY Position Variables +'float X, Y' : XY Position Variables ### Loader -bool LoadFile(sstd::string Path) Load a file from a path - Return true if found and loaded - return false if not +'bool LoadFile(sstd::string Path)' : Load a file from a path. Return true if found and loaded. Return false if not -std::vector LoadedMeshes Loaded Mesh Objects +'std::vector LoadedMeshes' : Loaded Mesh Objects From bb25f3ac32c0b5b233ea26f644c642188313262a Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 15:23:23 -0700 Subject: [PATCH 09/29] Update README --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a2489c36..83ba123f 100644 --- a/README.md +++ b/README.md @@ -21,29 +21,31 @@ These are all of the included classes and only the relevant members and methods. ### Mesh -'std::string MeshName' : The Mesh Name given in the .obj -'std::vector Vertices' : Vertex List -'std::vector Indices' : Index List +1. std::string MeshName + The Mesh Name given in the .obj +2. std::vector Vertices + Vertex List +3. std::vector Indices + Index List ### Vertex -'Vector3 Position' : Position vector -'Vector3 Normal' : Normal vector -'Vector2 TextureCoordinate' : Texture Coordinate vector +1. Vector3 Position : Position vector +2. Vector3 Normal : Normal vector +3. Vector2 TextureCoordinate : Texture Coordinate vector ### Vector3 -'float X, Y, Z' : XYZ Position Variables +1. float X, Y, Z : XYZ Position Variables ### Vector2 -'float X, Y' : XY Position Variables +1. float X, Y : XY Position Variables ### Loader -'bool LoadFile(sstd::string Path)' : Load a file from a path. Return true if found and loaded. Return false if not - -'std::vector LoadedMeshes' : Loaded Mesh Objects +1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not +2. std::vector LoadedMeshes : Loaded Mesh Objects From c81bacb276f58df7f9ed085afa8d5dfa51015a8f Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 15:28:05 -0700 Subject: [PATCH 10/29] Update REAMDE Again --- README.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 83ba123f..64227f0b 100644 --- a/README.md +++ b/README.md @@ -21,31 +21,28 @@ These are all of the included classes and only the relevant members and methods. ### Mesh -1. std::string MeshName - The Mesh Name given in the .obj -2. std::vector Vertices - Vertex List -3. std::vector Indices - Index List +1. std::string MeshName : The Mesh Name given in the .obj +2. std::vector Vertices : Vertex List +3. std::vector Indices : Index List ### Vertex -1. Vector3 Position : Position vector -2. Vector3 Normal : Normal vector -3. Vector2 TextureCoordinate : Texture Coordinate vector +1. Vector3 Position : Position vector +2. Vector3 Normal : Normal vector +3. Vector2 TextureCoordinate : Texture Coordinate vector ### Vector3 -1. float X, Y, Z : XYZ Position Variables +1. float X, Y, Z : Position Variables ### Vector2 -1. float X, Y : XY Position Variables +1. float X, Y : Position Variables ### Loader -1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not -2. std::vector LoadedMeshes : Loaded Mesh Objects +1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not +2. std::vector LoadedMeshes : Loaded Mesh Objects From 2ae9befd649b5d98621226344ff383d7124b91aa Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 15:40:44 -0700 Subject: [PATCH 11/29] Modified README and LICENSE --- LICENSE.md => LICENSE | 0 README.md | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) rename LICENSE.md => LICENSE (100%) diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.md b/README.md index 64227f0b..9b7cda6d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ -# OBJ LOADER +# RS OBJ LOADER + +The quick to use single header OBJ loader ## Quick Description -OBJ Loader is a simple .obj File Loader +OBJ Loader is a simple, header only, .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. This does not load any material data. Only Vertices, Indicies, and Meshes. Good for any prototyping where you need a quick .obj model loaded. + +## Prerequisites + +OBJ Loader was made with compatibility in mind. So you only need the header file to get going as it uses only STD and self made data structures. -## Slightly Longer Description +## Installation -OBJ Loader is a simple .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. +All you need to to is copy over the OBJ_Loader.h header file, include it in your solution, and you are good to go. ## Quick Use Guide @@ -44,8 +50,10 @@ These are all of the included classes and only the relevant members and methods. 1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not 2. std::vector LoadedMeshes : Loaded Mesh Objects +## Credits +Robert Smith ## License -OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE.md \ No newline at end of file +OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE \ No newline at end of file From ebfddb6e5dd87f5cdfd722ab0ef15273b4d04828 Mon Sep 17 00:00:00 2001 From: Bly7 Date: Tue, 13 Sep 2016 16:14:00 -0700 Subject: [PATCH 12/29] Added Examples, Modified README and LICENSE --- LICENSE => LICENSE.txt | 0 README.md | 8 +- Source/OBJ_Loader.h | 2 + examples/1 - LoadAndPrint/box_stack.obj | 86 +++++++++++++++++++ examples/1 - LoadAndPrint/e1_loadandprint.cpp | 86 +++++++++++++++++++ 5 files changed, 180 insertions(+), 2 deletions(-) rename LICENSE => LICENSE.txt (100%) create mode 100644 examples/1 - LoadAndPrint/box_stack.obj create mode 100644 examples/1 - LoadAndPrint/e1_loadandprint.cpp diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/README.md b/README.md index 9b7cda6d..ff4cc2a8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RS OBJ LOADER +# OBJ LOADER The quick to use single header OBJ loader @@ -12,7 +12,11 @@ OBJ Loader was made with compatibility in mind. So you only need the header file ## Installation -All you need to to is copy over the OBJ_Loader.h header file, include it in your solution, and you are good to go. +All you need to to is copy over the OBJ_Loader.h header file, include it in your solution, and you are good to go. It is located within the source folder. + +## Examples + +Examples are found within the examples folder. In order to compile these you will need to link OBJ_Loader.h to the compiler in since it is not included within the example folders. ## Quick Use Guide diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 9fdb4dab..759592f0 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -1,3 +1,5 @@ +// OBJ_Loader.h - A Single Header OBJ Model Loader + #pragma once // Vector - STD Vector/Array Library diff --git a/examples/1 - LoadAndPrint/box_stack.obj b/examples/1 - LoadAndPrint/box_stack.obj new file mode 100644 index 00000000..c455d7f0 --- /dev/null +++ b/examples/1 - LoadAndPrint/box_stack.obj @@ -0,0 +1,86 @@ +# Blender v2.76 (sub 0) OBJ File: 'box_stack.blend' +# www.blender.org +mtllib box_stack.mtl +o mTop_cTop +v 0.234105 2.076805 0.234105 +v 0.234105 2.545015 0.234105 +v -0.234105 2.076805 0.234105 +v -0.234105 2.545015 0.234105 +v 0.234105 2.076805 -0.234105 +v 0.234105 2.545015 -0.234105 +v -0.234105 2.076805 -0.234105 +v -0.234105 2.545015 -0.234105 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 -0.000000 +vt 0.000000 -0.000000 +vn 0.000000 0.000000 1.000000 +vn -1.000000 0.000000 0.000000 +vn -0.000000 0.000000 -1.000000 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_top +s off +f 2/1/1 4/2/1 3/3/1 1/4/1 +f 4/1/2 8/2/2 7/3/2 3/4/2 +f 8/2/3 6/1/3 5/4/3 7/3/3 +f 6/2/4 2/1/4 1/4/4 5/3/4 +f 1/4/5 3/1/5 7/2/5 5/3/5 +f 6/3/6 8/2/6 4/1/6 2/4/6 +o mMid_cMid +v 0.000000 1.004430 0.754531 +v 0.000000 2.071499 0.754531 +v -0.754531 1.004430 0.000000 +v -0.754531 2.071499 0.000000 +v 0.754531 1.004430 -0.000000 +v 0.754531 2.071499 -0.000000 +v -0.000000 1.004430 -0.754531 +v -0.000000 2.071499 -0.754531 +vt 0.003117 1.000000 +vt 1.003117 1.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn -0.707100 0.000000 0.707100 +vn -0.707100 0.000000 -0.707100 +vn 0.707100 0.000000 -0.707100 +vn 0.707100 0.000000 0.707100 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_mid +s off +f 10/5/7 12/6/7 11/7/7 9/8/7 +f 12/9/8 16/10/8 15/7/8 11/8/8 +f 16/10/9 14/9/9 13/8/9 15/7/9 +f 14/10/10 10/9/10 9/8/10 13/7/10 +f 9/8/11 11/9/11 15/10/11 13/7/11 +f 14/7/12 16/10/12 12/9/12 10/8/12 +o mBot_cBot +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl mat_bot +s off +f 18/11/13 20/12/13 19/13/13 17/14/13 +f 20/11/14 24/12/14 23/13/14 19/14/14 +f 24/12/15 22/11/15 21/14/15 23/13/15 +f 22/12/16 18/11/16 17/14/16 21/13/16 +f 17/14/17 19/11/17 23/12/17 21/13/17 +f 22/13/18 24/12/18 20/11/18 18/14/18 diff --git a/examples/1 - LoadAndPrint/e1_loadandprint.cpp b/examples/1 - LoadAndPrint/e1_loadandprint.cpp new file mode 100644 index 00000000..dfa3e3ae --- /dev/null +++ b/examples/1 - LoadAndPrint/e1_loadandprint.cpp @@ -0,0 +1,86 @@ +// Example 1: Load and Print +// +// Load the data from the .obj then print it into a file +// called e1Out.txt + +// Iostream - STD I/O Library +#include + +// fStream - STD File I/O Library +#include + +// OBJ_Loader - .obj Loader +#include "OBJ_Loader.h" + +// Main function +int main(int argc, char* argv[]) +{ + // Initialize Loader + objl::Loader Loader; + + // Load .obj File + bool loadout = Loader.LoadFile("box_stack.obj"); + + // Check to see if it loaded + + // If so continue + if (loadout) + { + // Create/Open e1Out.txt + std::ofstream file("e1Out.txt"); + + // Go through each loaded mesh and out its contents + for (int i = 0; i < Loader.LoadedMeshes.size(); i++) + { + // Copy one of the loaded meshes to be our current mesh + objl::Mesh curMesh = Loader.LoadedMeshes[i]; + + // Print Mesh Name + file << "Mesh " << i << ": " << curMesh.MeshName << "\n"; + + // Print Vertices + file << "Vertices:\n"; + + // Go through each vertex and print its number, + // position, normal, and texture coordinate + for (int j = 0; j < curMesh.Vertices.size(); j++) + { + file << "V" << j << ": " << + "P(" << curMesh.Vertices[j].Position.X << ", " << curMesh.Vertices[j].Position.Y << ", " << curMesh.Vertices[j].Position.Z << ") " << + "N(" << curMesh.Vertices[j].Normal.X << ", " << curMesh.Vertices[j].Normal.Y << ", " << curMesh.Vertices[j].Normal.Z << ") " << + "TC(" << curMesh.Vertices[j].TextureCoordinate.X << ", " << curMesh.Vertices[j].TextureCoordinate.Y << ")\n"; + } + + // Print Indices + file << "Indices:\n"; + + // Go through every 3rd index and print the + // triangle that these indices represent + for (int j = 0; j < curMesh.Indices.size(); j += 3) + { + file << "T" << j / 3 << ": " << curMesh.Indices[j] << ", " << curMesh.Indices[j + 1] << ", " << curMesh.Indices[j + 2] << "\n"; + } + + // Leave a space to separate from the next mesh + file << "\n"; + } + + // Close File + file.close(); + } + // If not output an error + else + { + // Create/Open e1Out.txt + std::ofstream file("e1Out.txt"); + + // Output Error + file << "Failed to Load File. May have failed to find it or it was not an .obj file.\n"; + + // Close File + file.close(); + } + + // Exit the program + return 0; +} \ No newline at end of file From 9d039937079a566a151e7d684109023c897ae2a3 Mon Sep 17 00:00:00 2001 From: Bly7 Date: Wed, 14 Sep 2016 11:24:38 -0700 Subject: [PATCH 13/29] Update README --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff4cc2a8..6c69e736 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,21 @@ Robert Smith ## License -OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE \ No newline at end of file +OBJ Loader uses the MIT license. The MIT license allows free use and modification of the software as long as they provide credit back to me and don't hold me liable for anything that may go wrong. If you want to read the license in full look into the file entitled: LICENSE + +## Version Information + +### Current + +Version 1.0 + +### Upcoming Features + +1. Load Material Data + +### Version 1.0 + +1. Base Implementation Done +2. README Created +3. LICENSE Created +4. Mesh Index and Vertex Load Implemented \ No newline at end of file From ef94a05031200dbbafbbac8a6a2da1e3dba709ce Mon Sep 17 00:00:00 2001 From: Bly7 Date: Wed, 14 Sep 2016 17:41:51 -0700 Subject: [PATCH 14/29] Implement Material Loading, Updated example 1, Modifed README --- README.md | 54 +++- Source/OBJ_Loader.h | 270 +++++++++++++++++- examples/1 - LoadAndPrint/box_stack.mtl | 32 +++ examples/1 - LoadAndPrint/e1_loadandprint.cpp | 19 +- 4 files changed, 354 insertions(+), 21 deletions(-) create mode 100644 examples/1 - LoadAndPrint/box_stack.mtl diff --git a/README.md b/README.md index 6c69e736..0e396f6c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The quick to use single header OBJ loader ## Quick Description -OBJ Loader is a simple, header only, .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. This does not load any material data. Only Vertices, Indicies, and Meshes. Good for any prototyping where you need a quick .obj model loaded. +OBJ Loader is a simple, header only, .obj model file loader that will take in a path to a file, load it into the Loader class object, then allow you to get the data from each mesh loaded. This will load each mesh within the model with the corresponding data such as vertices, indices, and material. Plus a large array of vertices, indices and materials which you can do whatever you want with. ## Prerequisites @@ -29,11 +29,13 @@ Examples are found within the examples folder. In order to compile these you wil These are all of the included classes and only the relevant members and methods. There are others to find if you want but these are all you will need to know to operate. -### Mesh +### Vector2 -1. std::string MeshName : The Mesh Name given in the .obj -2. std::vector Vertices : Vertex List -3. std::vector Indices : Index List +1. float X, Y : Position Variables + +### Vector3 + +1. float X, Y, Z : Position Variables ### Vertex @@ -41,13 +43,28 @@ These are all of the included classes and only the relevant members and methods. 2. Vector3 Normal : Normal vector 3. Vector2 TextureCoordinate : Texture Coordinate vector -### Vector3 - -1. float X, Y, Z : Position Variables +### Material + +1. std::string name : Name of loaded material +2. Vector3 Ka : Ambient color +3. Vector3 Kd : Diffuse color +4. Vector3 Ks : Specular color +5. float Ns : Specular exponent +6. float Ni : Optical density +7. float d : Dissolve variable +8. int illum : Immuniation variable +9. std::string map_Ka : Ambient texture map name +10. std::string map_Kd : Diffuse texture map name +11. std::string map_Ks : Specular texture map name +12. std::string map_d : Alpha texture map name +13. std::string map_bump : Bump map name -### Vector2 +### Mesh -1. float X, Y : Position Variables +1. std::string MeshName : The Mesh Name given in the .obj +2. std::vector Vertices : Vertex List +3. std::vector Indices : Index List +4. Material MeshMaterial : Material assigned to this mesh ### Loader @@ -64,17 +81,24 @@ OBJ Loader uses the MIT license. The MIT license allows free use and modificatio ## Version Information -### Current +### Current Version -Version 1.0 +Version 2.0 ### Upcoming Features -1. Load Material Data +1. Bug Fixing +2. Adding more variables + +### Previous Versions -### Version 1.0 +#### Version 1.0 (September-13-2016) 1. Base Implementation Done 2. README Created 3. LICENSE Created -4. Mesh Index and Vertex Load Implemented \ No newline at end of file +4. Mesh Index and Vertex Load Implemented + +#### Version 2.0 (September-14-2016) + +1. Now Reads Material Data \ No newline at end of file diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 759592f0..bd653fc7 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -132,6 +132,47 @@ namespace objl Vector2 TextureCoordinate; }; + struct Material + { + Material() + { + name; + Ns = 0.0f; + Ni = 0.0f; + d = 0.0f; + illum = 0; + } + + // Material Name + std::string name; + // Ambient Color + Vector3 Ka; + // Diffuse Color + Vector3 Kd; + // Specular Color + Vector3 Ks; + // Specular Exponent + float Ns; + // Optical Density + float Ni; + // Dissolve + float d; + // Illumination + int illum; + // Ambient Texture Map + std::string map_Ka; + // Diffuse Texture Map + std::string map_Kd; + // Specular Texture Map + std::string map_Ks; + // Specular Hightlight Map + std::string map_Ns; + // Alpha Texture Map + std::string map_d; + // Bump Map + std::string map_bump; + }; + // Structure: Mesh // // Description: A Simple Mesh Object that holds @@ -155,6 +196,9 @@ namespace objl std::vector Vertices; // Index List std::vector Indices; + + // Material + Material MeshMaterial; }; // Namespace: Math @@ -305,8 +349,8 @@ namespace objl return false; LoadedMeshes.clear(); - //LoadedVertices.clear(); - //LoadedIndices.clear(); + LoadedVertices.clear(); + LoadedIndices.clear(); std::vector Positions; std::vector TCoords; @@ -315,6 +359,8 @@ namespace objl std::vector Vertices; std::vector Indices; + std::vector MeshMatNames; + bool listening = false; std::string meshname; @@ -422,6 +468,7 @@ namespace objl { Vertices.push_back(vVerts[i]); + LoadedVertices.push_back(vVerts[i]); } std::vector iIndices; @@ -434,7 +481,40 @@ namespace objl int indnum = ((Vertices.size()) - vVerts.size()) + iIndices[i]; Indices.push_back(indnum); + indnum = ((LoadedVertices.size()) - vVerts.size()) + iIndices[i]; + LoadedIndices.push_back(indnum); + + } + } + // Get Mesh Material Name + if (curline.substr(0, 7) == "usemtl ") + { + MeshMatNames.push_back(curline.substr(7, curline.size())); + } + // Load Materials + if (curline.substr(0, 7) == "mtllib ") + { + // Generate LoadedMaterial + + // Generate a path to the material file + std::vector temp; + algorithm::split(Path, temp, "/"); + + std::string pathtomat = ""; + + if (temp.size() != 1) + { + for (int i = 0; i < temp.size()-1; i++) + { + pathtomat += temp[i] + "/"; + } } + + + pathtomat += curline.substr(7, curline.size()); + + // Load Materials + LoadMaterials(pathtomat); } } @@ -452,7 +532,24 @@ namespace objl file.close(); - if (LoadedMeshes.empty())//&& LoadedVertices.empty() && LoadedIndices.empty()) + // Set Materials for each Mesh + for (int i = 0; i < MeshMatNames.size(); i++) + { + std::string matname = MeshMatNames[i]; + + // Find corresponding material name in loaded materials + // when found copy material variables into mesh material + for (int j = 0; j < LoadedMaterials.size(); j++) + { + if (LoadedMaterials[j].name == matname) + { + LoadedMeshes[i].MeshMaterial = LoadedMaterials[j]; + break; + } + } + } + + if (LoadedMeshes.empty() && LoadedVertices.empty() && LoadedIndices.empty()) { return false; } @@ -463,8 +560,9 @@ namespace objl } std::vector LoadedMeshes; - //std::vector LoadedVertices; - //std::vector LoadedIndices; + std::vector LoadedVertices; + std::vector LoadedIndices; + std::vector LoadedMaterials; private: // Generate vertices from a list of positions, @@ -746,5 +844,167 @@ namespace objl break; } } + + // Load Materials from .mtl file + bool LoadMaterials(std::string path) + { + // If the file is not a material file return false + if (path.substr(path.size() - 4, path.size()) != ".mtl") + return false; + + std::ifstream file(path); + + // If the file is not found return false + if (!file.is_open()) + return false; + + Material tempMaterial; + + bool listening = false; + + // Go through each line looking for material variables + std::string curline; + while (std::getline(file, curline)) + { + // new material and material name + if (curline.substr(0, 7) == "newmtl ") + { + if (!listening) + { + listening = true; + + if (curline.size() > 7) + { + tempMaterial.name = curline.substr(7, curline.size()); + } + else + { + tempMaterial.name = "none"; + } + } + else + { + // Generate the material + + // Push Back loaded Material + LoadedMaterials.push_back(tempMaterial); + + // Clear Loaded Material + tempMaterial = Material(); + + if (curline.size() > 7) + { + tempMaterial.name = curline.substr(7, curline.size()); + } + else + { + tempMaterial.name = "none"; + } + } + } + // Ambient Color + if (curline.substr(0, 3) == "Ka ") + { + std::vector temp; + algorithm::split(curline.substr(3, curline.size()), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Ka.X = std::stof(temp[0]); + tempMaterial.Ka.Y = std::stof(temp[1]); + tempMaterial.Ka.Z = std::stof(temp[2]); + } + // Diffuse Color + if (curline.substr(0, 3) == "Kd ") + { + std::vector temp; + algorithm::split(curline.substr(3, curline.size()), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Kd.X = std::stof(temp[0]); + tempMaterial.Kd.Y = std::stof(temp[1]); + tempMaterial.Kd.Z = std::stof(temp[2]); + } + // Specular Color + if (curline.substr(0, 3) == "Ks ") + { + std::vector temp; + algorithm::split(curline.substr(3, curline.size()), temp, " "); + + if (temp.size() != 3) + continue; + + tempMaterial.Ks.X = std::stof(temp[0]); + tempMaterial.Ks.Y = std::stof(temp[1]); + tempMaterial.Ks.Z = std::stof(temp[2]); + } + // Specular Exponent + if (curline.substr(0, 3) == "Ns ") + { + tempMaterial.Ns = std::stof(curline.substr(3, curline.size())); + } + // Optical Density + if (curline.substr(0, 3) == "Ni ") + { + tempMaterial.Ni = std::stof(curline.substr(3, curline.size())); + } + // Dissolve + if (curline.substr(0, 2) == "d ") + { + tempMaterial.d = std::stof(curline.substr(2, curline.size())); + } + // Illumination + if (curline.substr(0, 6) == "illum ") + { + tempMaterial.illum = std::stoi(curline.substr(6, curline.size())); + } + // Ambient Texture Map + if (curline.substr(0, 7) == "map_Ka ") + { + tempMaterial.map_Ka = curline.substr(7, curline.size()); + } + // Diffuse Texture Map + if (curline.substr(0, 7) == "map_Kd ") + { + tempMaterial.map_Kd = curline.substr(7, curline.size()); + } + // Specular Texture Map + if (curline.substr(0, 7) == "map_Ks ") + { + tempMaterial.map_Ks = curline.substr(7, curline.size()); + } + // Specular Hightlight Map + if (curline.substr(0, 7) == "map_Ns ") + { + tempMaterial.map_Ns = curline.substr(7, curline.size()); + } + // Alpha Texture Map + if (curline.substr(0, 6) == "map_d ") + { + tempMaterial.map_d = curline.substr(6, curline.size()); + } + // Bump Map + if (curline.substr(0, 9) == "map_bump ") + { + tempMaterial.map_bump = curline.substr(9, curline.size()); + } + } + + // Deal with last material + + // Push Back loaded Material + LoadedMaterials.push_back(tempMaterial); + + // Test to see if anything was loaded + // If not return false + if (LoadedMaterials.empty()) + return false; + // If so return true + else + return true; + } }; } \ No newline at end of file diff --git a/examples/1 - LoadAndPrint/box_stack.mtl b/examples/1 - LoadAndPrint/box_stack.mtl new file mode 100644 index 00000000..71357186 --- /dev/null +++ b/examples/1 - LoadAndPrint/box_stack.mtl @@ -0,0 +1,32 @@ +# Blender MTL File: 'box_stack.blend' +# Material Count: 3 + +newmtl mat_bot +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl mat_mid +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl mat_top +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/examples/1 - LoadAndPrint/e1_loadandprint.cpp b/examples/1 - LoadAndPrint/e1_loadandprint.cpp index dfa3e3ae..0a12bec5 100644 --- a/examples/1 - LoadAndPrint/e1_loadandprint.cpp +++ b/examples/1 - LoadAndPrint/e1_loadandprint.cpp @@ -19,7 +19,9 @@ int main(int argc, char* argv[]) objl::Loader Loader; // Load .obj File - bool loadout = Loader.LoadFile("box_stack.obj"); + bool loadout = Loader.LoadFile("box_stack/box_stack.obj"); + + //bool loadout = Loader.LoadFile("crysis_nano/nanosuit.obj"); // Check to see if it loaded @@ -61,6 +63,21 @@ int main(int argc, char* argv[]) file << "T" << j / 3 << ": " << curMesh.Indices[j] << ", " << curMesh.Indices[j + 1] << ", " << curMesh.Indices[j + 2] << "\n"; } + // Print Material + file << "Material: " << curMesh.MeshMaterial.name << "\n"; + file << "Ambient Color: " << curMesh.MeshMaterial.Ka.X << ", " << curMesh.MeshMaterial.Ka.Y << ", " << curMesh.MeshMaterial.Ka.Z << "\n"; + file << "Diffuse Color: " << curMesh.MeshMaterial.Kd.X << ", " << curMesh.MeshMaterial.Kd.Y << ", " << curMesh.MeshMaterial.Kd.Z << "\n"; + file << "Specular Color: " << curMesh.MeshMaterial.Ks.X << ", " << curMesh.MeshMaterial.Ks.Y << ", " << curMesh.MeshMaterial.Ks.Z << "\n"; + file << "Specular Exponent: " << curMesh.MeshMaterial.Ns << "\n"; + file << "Optical Density: " << curMesh.MeshMaterial.Ni << "\n"; + file << "Dissolve: " << curMesh.MeshMaterial.d << "\n"; + file << "Illumination: " << curMesh.MeshMaterial.illum << "\n"; + file << "Ambient Texture Map: " << curMesh.MeshMaterial.map_Ka << "\n"; + file << "Diffuse Texture Map: " << curMesh.MeshMaterial.map_Kd << "\n"; + file << "Specular Texture Map: " << curMesh.MeshMaterial.map_Ks << "\n"; + file << "Alpha Texture Map: " << curMesh.MeshMaterial.map_d << "\n"; + file << "Bump Map: " << curMesh.MeshMaterial.map_bump << "\n"; + // Leave a space to separate from the next mesh file << "\n"; } From 5b151f83dfb27187639fe211a32b434b3ff29d34 Mon Sep 17 00:00:00 2001 From: Bly7 Date: Wed, 14 Sep 2016 17:45:06 -0700 Subject: [PATCH 15/29] Update Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e396f6c..c9446ec2 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ These are all of the included classes and only the relevant members and methods. 5. float Ns : Specular exponent 6. float Ni : Optical density 7. float d : Dissolve variable -8. int illum : Immuniation variable +8. int illum : Illumination variable 9. std::string map_Ka : Ambient texture map name 10. std::string map_Kd : Diffuse texture map name 11. std::string map_Ks : Specular texture map name From a37253303776f427eb38d023f221df4b2733b623 Mon Sep 17 00:00:00 2001 From: Bly7 Date: Wed, 14 Sep 2016 17:55:51 -0700 Subject: [PATCH 16/29] Update readme and comments --- README.md | 3 +++ Source/OBJ_Loader.h | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9446ec2..5734f966 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,9 @@ These are all of the included classes and only the relevant members and methods. 1. bool LoadFile(std::string Path) : Load a file from a path. Return true if found and loaded. Return false if not 2. std::vector LoadedMeshes : Loaded Mesh Objects +3. std::vector LoadedVertices : Loaded Vertex Objects +4. std::vector LoadedIndices : Loaded Index Positions +5. std::vector LoadedMaterials : Loaded Material Objects ## Credits diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index bd653fc7..caada652 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -558,10 +558,14 @@ namespace objl return true; } } - + + // Loaded Mesh Objects std::vector LoadedMeshes; + // Loaded Vertex Objects std::vector LoadedVertices; + // Loaded Index Positions std::vector LoadedIndices; + // Loaded Material Objects std::vector LoadedMaterials; private: From c3a4b49cecfdc4a787afb80a86b97e95db15d9ba Mon Sep 17 00:00:00 2001 From: Bly7 Date: Wed, 14 Sep 2016 18:03:33 -0700 Subject: [PATCH 17/29] Update Example 1, Fix Bump Map Bug --- Source/OBJ_Loader.h | 2 +- examples/1 - LoadAndPrint/e1_loadandprint.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index caada652..bf6cf29c 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -991,7 +991,7 @@ namespace objl tempMaterial.map_d = curline.substr(6, curline.size()); } // Bump Map - if (curline.substr(0, 9) == "map_bump ") + if (curline.substr(0, 9) == "map_Bump ") { tempMaterial.map_bump = curline.substr(9, curline.size()); } diff --git a/examples/1 - LoadAndPrint/e1_loadandprint.cpp b/examples/1 - LoadAndPrint/e1_loadandprint.cpp index 0a12bec5..ced2187c 100644 --- a/examples/1 - LoadAndPrint/e1_loadandprint.cpp +++ b/examples/1 - LoadAndPrint/e1_loadandprint.cpp @@ -19,9 +19,7 @@ int main(int argc, char* argv[]) objl::Loader Loader; // Load .obj File - bool loadout = Loader.LoadFile("box_stack/box_stack.obj"); - - //bool loadout = Loader.LoadFile("crysis_nano/nanosuit.obj"); + bool loadout = Loader.LoadFile("box_stack.obj"); // Check to see if it loaded From 85eac88ea94a577ac738c196c12f3233d3e5fa71 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Mon, 27 Mar 2017 09:49:42 +0200 Subject: [PATCH 18/29] added more general parsing (ignoring additional spaces or tabs) --- Source/OBJ_Loader.h | 158 +++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 62 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index bf6cf29c..37950bf7 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -293,7 +293,7 @@ namespace objl { out.push_back(temp); temp.clear(); - i += token.size() - 1; + i += (int)token.size() - 1; } else { @@ -312,6 +312,40 @@ namespace objl } } } + + // Get tail of string after first token and possibly following spaces + inline std::string tail(const std::string &in) + { + size_t token_start = in.find_first_not_of(" \t"); + size_t space_start = in.find_first_of(" \t", token_start); + size_t space_end = in.find_first_not_of(" \t", space_start); + if (space_end != std::string::npos) + { + return in.substr(space_end); + } + return ""; + } + + // Get first token of string + inline std::string firstToken(const std::string &in) + { + if (in.empty()) + { + return ""; + } + size_t token_start = in.find_first_not_of(" \t"); + size_t token_end = in.find_first_of(" \t", token_start); + if (token_start != std::string::npos && token_end != std::string::npos) + { + std::string t = in.substr(token_start, token_end); + return in.substr(token_start, token_end - token_start); + } + else if (token_start != std::string::npos) + { + return in.substr(token_start); + } + return ""; + } } // Class: Loader @@ -342,7 +376,7 @@ namespace objl if (Path.substr(Path.size() - 4, 4) != ".obj") return false; - + std::ifstream file(Path); if (!file.is_open()) @@ -370,15 +404,15 @@ namespace objl while (std::getline(file, curline)) { // Generate a Mesh Object or Prepare for an object to be created - if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g " || curline[0] == 'g') + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g" || curline[0] == 'g') { if (!listening) { listening = true; - if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g ") + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g") { - meshname = curline.substr(2, curline.size()); + meshname = algorithm::tail(curline); } else { @@ -403,13 +437,13 @@ namespace objl Indices.clear(); meshname.clear(); - meshname = curline.substr(2, curline.size()); + meshname = algorithm::tail(curline); } else { - if (curline.substr(0, 2) == "o " || curline.substr(0, 2) == "g ") + if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g") { - meshname = curline.substr(2, curline.size()); + meshname = algorithm::tail(curline); } else { @@ -419,11 +453,11 @@ namespace objl } } // Generate a Vertex Position - if (curline.substr(0, 2) == "v ") + if (algorithm::firstToken(curline) == "v") { std::vector spos; Vector3 vpos; - algorithm::split(curline.substr(2, curline.size() - 1), spos, " "); + algorithm::split(algorithm::tail(curline), spos, " "); vpos.X = std::stof(spos[0]); vpos.Y = std::stof(spos[1]); @@ -432,11 +466,11 @@ namespace objl Positions.push_back(vpos); } // Generate a Vertex Texture Coordinate - if (curline.substr(0, 3) == "vt ") + if (algorithm::firstToken(curline) == "vt") { std::vector stex; Vector2 vtex; - algorithm::split(curline.substr(3, curline.size()), stex, " "); + algorithm::split(algorithm::tail(curline), stex, " "); vtex.X = std::stof(stex[0]); vtex.Y = std::stof(stex[1]); @@ -444,11 +478,11 @@ namespace objl TCoords.push_back(vtex); } // Generate a Vertex Normal; - if (curline.substr(0, 3) == "vn ") + if (algorithm::firstToken(curline) == "vn") { std::vector snor; Vector3 vnor; - algorithm::split(curline.substr(3, curline.size()), snor, " "); + algorithm::split(algorithm::tail(curline), snor, " "); vnor.X = std::stof(snor[0]); vnor.Y = std::stof(snor[1]); @@ -457,7 +491,7 @@ namespace objl Normals.push_back(vnor); } // Generate a Face (vertices & indices) - if (curline.substr(0, 2) == "f ") + if (algorithm::firstToken(curline) == "f") { // Generate the vertices std::vector vVerts; @@ -478,21 +512,21 @@ namespace objl // Add Indices for (int i = 0; i < int(iIndices.size()); i++) { - int indnum = ((Vertices.size()) - vVerts.size()) + iIndices[i]; + unsigned int indnum = (unsigned int)((Vertices.size()) - vVerts.size()) + iIndices[i]; Indices.push_back(indnum); - indnum = ((LoadedVertices.size()) - vVerts.size()) + iIndices[i]; + indnum = (unsigned int)((LoadedVertices.size()) - vVerts.size()) + iIndices[i]; LoadedIndices.push_back(indnum); } } // Get Mesh Material Name - if (curline.substr(0, 7) == "usemtl ") + if (algorithm::firstToken(curline) == "usemtl") { - MeshMatNames.push_back(curline.substr(7, curline.size())); + MeshMatNames.push_back(algorithm::tail(curline)); } // Load Materials - if (curline.substr(0, 7) == "mtllib ") + if (algorithm::firstToken(curline) == "mtllib") { // Generate LoadedMaterial @@ -504,14 +538,14 @@ namespace objl if (temp.size() != 1) { - for (int i = 0; i < temp.size()-1; i++) + for (int i = 0; i < temp.size() - 1; i++) { pathtomat += temp[i] + "/"; } } - pathtomat += curline.substr(7, curline.size()); + pathtomat += algorithm::tail(curline); // Load Materials LoadMaterials(pathtomat); @@ -558,7 +592,7 @@ namespace objl return true; } } - + // Loaded Mesh Objects std::vector LoadedMeshes; // Loaded Vertex Objects @@ -579,7 +613,7 @@ namespace objl { std::vector sface, svert; Vertex vVert; - algorithm::split(icurline.substr(2, icurline.size()), sface, " "); + algorithm::split(algorithm::tail(icurline), sface, " "); bool noNormal = false; @@ -626,7 +660,7 @@ namespace objl { case 1: // P { - vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; vVert.TextureCoordinate = Vector2(0, 0); noNormal = true; oVerts.push_back(vVert); @@ -634,25 +668,25 @@ namespace objl } case 2: // P/T { - vVert.Position = iPositions[stoi(svert[0]) - 1]; - vVert.TextureCoordinate = iTCoords[stoi(svert[1]) - 1]; + vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; + vVert.TextureCoordinate = iTCoords[std::abs(std::stoi(svert[1])) - 1]; noNormal = true; oVerts.push_back(vVert); break; } case 3: // P//N { - vVert.Position = iPositions[stoi(svert[0]) - 1]; + vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; vVert.TextureCoordinate = Vector2(0, 0); - vVert.Normal = iNormals[stoi(svert[2]) - 1]; + vVert.Normal = iNormals[std::abs(std::stoi(svert[2])) - 1]; oVerts.push_back(vVert); break; } case 4: // P/T/N { - vVert.Position = iPositions[stoi(svert[0]) - 1]; - vVert.TextureCoordinate = iTCoords[stoi(svert[1]) - 1]; - vVert.Normal = iNormals[stoi(svert[2]) - 1]; + vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; + vVert.TextureCoordinate = iTCoords[std::abs(std::stoi(svert[1])) - 1]; + vVert.Normal = iNormals[std::abs(std::stoi(svert[2])) - 1]; oVerts.push_back(vVert); break; } @@ -871,7 +905,7 @@ namespace objl while (std::getline(file, curline)) { // new material and material name - if (curline.substr(0, 7) == "newmtl ") + if (algorithm::firstToken(curline) == "newmtl") { if (!listening) { @@ -879,7 +913,7 @@ namespace objl if (curline.size() > 7) { - tempMaterial.name = curline.substr(7, curline.size()); + tempMaterial.name = algorithm::tail(curline); } else { @@ -898,7 +932,7 @@ namespace objl if (curline.size() > 7) { - tempMaterial.name = curline.substr(7, curline.size()); + tempMaterial.name = algorithm::tail(curline); } else { @@ -907,10 +941,10 @@ namespace objl } } // Ambient Color - if (curline.substr(0, 3) == "Ka ") + if (algorithm::firstToken(curline) == "Ka") { std::vector temp; - algorithm::split(curline.substr(3, curline.size()), temp, " "); + algorithm::split(algorithm::tail(curline), temp, " "); if (temp.size() != 3) continue; @@ -920,10 +954,10 @@ namespace objl tempMaterial.Ka.Z = std::stof(temp[2]); } // Diffuse Color - if (curline.substr(0, 3) == "Kd ") + if (algorithm::firstToken(curline) == "Kd") { std::vector temp; - algorithm::split(curline.substr(3, curline.size()), temp, " "); + algorithm::split(algorithm::tail(curline), temp, " "); if (temp.size() != 3) continue; @@ -933,10 +967,10 @@ namespace objl tempMaterial.Kd.Z = std::stof(temp[2]); } // Specular Color - if (curline.substr(0, 3) == "Ks ") + if (algorithm::firstToken(curline) == "Ks") { std::vector temp; - algorithm::split(curline.substr(3, curline.size()), temp, " "); + algorithm::split(algorithm::tail(curline), temp, " "); if (temp.size() != 3) continue; @@ -946,54 +980,54 @@ namespace objl tempMaterial.Ks.Z = std::stof(temp[2]); } // Specular Exponent - if (curline.substr(0, 3) == "Ns ") + if (algorithm::firstToken(curline) == "Ns") { - tempMaterial.Ns = std::stof(curline.substr(3, curline.size())); + tempMaterial.Ns = std::stof(algorithm::tail(curline)); } // Optical Density - if (curline.substr(0, 3) == "Ni ") + if (algorithm::firstToken(curline) == "Ni") { - tempMaterial.Ni = std::stof(curline.substr(3, curline.size())); + tempMaterial.Ni = std::stof(algorithm::tail(curline)); } // Dissolve - if (curline.substr(0, 2) == "d ") + if (algorithm::firstToken(curline) == "d") { - tempMaterial.d = std::stof(curline.substr(2, curline.size())); + tempMaterial.d = std::stof(algorithm::tail(curline)); } // Illumination - if (curline.substr(0, 6) == "illum ") + if (algorithm::firstToken(curline) == "illum") { - tempMaterial.illum = std::stoi(curline.substr(6, curline.size())); + tempMaterial.illum = std::stoi(algorithm::tail(curline)); } // Ambient Texture Map - if (curline.substr(0, 7) == "map_Ka ") + if (algorithm::firstToken(curline) == "map_Ka") { - tempMaterial.map_Ka = curline.substr(7, curline.size()); + tempMaterial.map_Ka = algorithm::tail(curline); } // Diffuse Texture Map - if (curline.substr(0, 7) == "map_Kd ") + if (algorithm::firstToken(curline) == "map_Kd") { - tempMaterial.map_Kd = curline.substr(7, curline.size()); + tempMaterial.map_Kd = algorithm::tail(curline); } // Specular Texture Map - if (curline.substr(0, 7) == "map_Ks ") + if (algorithm::firstToken(curline) == "map_Ks") { - tempMaterial.map_Ks = curline.substr(7, curline.size()); + tempMaterial.map_Ks = algorithm::tail(curline); } // Specular Hightlight Map - if (curline.substr(0, 7) == "map_Ns ") + if (algorithm::firstToken(curline) == "map_Ns") { - tempMaterial.map_Ns = curline.substr(7, curline.size()); + tempMaterial.map_Ns = algorithm::tail(curline); } // Alpha Texture Map - if (curline.substr(0, 6) == "map_d ") + if (algorithm::firstToken(curline) == "map_d") { - tempMaterial.map_d = curline.substr(6, curline.size()); + tempMaterial.map_d = algorithm::tail(curline); } // Bump Map - if (curline.substr(0, 9) == "map_Bump ") + if (algorithm::firstToken(curline) == "map_Bump") { - tempMaterial.map_bump = curline.substr(9, curline.size()); + tempMaterial.map_bump = algorithm::tail(curline); } } @@ -1011,4 +1045,4 @@ namespace objl return true; } }; -} \ No newline at end of file +} From cb9fd4cbac491bed7a1f4e573f69d4d0b56afa5e Mon Sep 17 00:00:00 2001 From: schtiefel Date: Mon, 27 Mar 2017 09:52:40 +0200 Subject: [PATCH 19/29] added console output to show progress (for large models) --- Source/OBJ_Loader.h | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 37950bf7..31949a86 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -11,6 +11,9 @@ // fStream - STD File I/O Library #include +// Print progress to console while loading (large models) +#define OBJL_CONSOLE_OUTPUT + // Namespace: OBJL // // Description: The namespace that holds eveyrthing that @@ -400,6 +403,11 @@ namespace objl Mesh tempMesh; + #ifdef OBJL_CONSOLE_OUTPUT + unsigned int outputIndicator = 0; + const unsigned int outputEveryNth = 1000; + #endif + std::string curline; while (std::getline(file, curline)) { @@ -451,6 +459,10 @@ namespace objl } } } + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl << "- part: " << meshname << std::endl; + outputIndicator = 0; + #endif } // Generate a Vertex Position if (algorithm::firstToken(curline) == "v") @@ -464,6 +476,13 @@ namespace objl vpos.Z = std::stof(spos[2]); Positions.push_back(vpos); + + #ifdef OBJL_CONSOLE_OUTPUT + if (++outputIndicator >= outputEveryNth) { + outputIndicator = 0; + std::cout << "\r- vertices: " << Positions.size(); + } + #endif } // Generate a Vertex Texture Coordinate if (algorithm::firstToken(curline) == "vt") @@ -476,6 +495,13 @@ namespace objl vtex.Y = std::stof(stex[1]); TCoords.push_back(vtex); + + #ifdef OBJL_CONSOLE_OUTPUT + if (++outputIndicator >= outputEveryNth) { + outputIndicator = 0; + std::cout << "\r- texture coords: " << TCoords.size(); + } + #endif } // Generate a Vertex Normal; if (algorithm::firstToken(curline) == "vn") @@ -489,6 +515,13 @@ namespace objl vnor.Z = std::stof(snor[2]); Normals.push_back(vnor); + + #ifdef OBJL_CONSOLE_OUTPUT + if (++outputIndicator >= outputEveryNth) { + outputIndicator = 0; + std::cout << "\r- normals: " << Normals.size(); + } + #endif } // Generate a Face (vertices & indices) if (algorithm::firstToken(curline) == "f") @@ -519,11 +552,22 @@ namespace objl LoadedIndices.push_back(indnum); } + + #ifdef OBJL_CONSOLE_OUTPUT + if (++outputIndicator >= outputEveryNth) { + outputIndicator = 0; + std::cout << "\r- triangles: " << (Vertices.size() / 3); + } + #endif } // Get Mesh Material Name if (algorithm::firstToken(curline) == "usemtl") { MeshMatNames.push_back(algorithm::tail(curline)); + + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl << "- use material: " << MeshMatNames.back() << std::endl; + #endif } // Load Materials if (algorithm::firstToken(curline) == "mtllib") @@ -547,6 +591,10 @@ namespace objl pathtomat += algorithm::tail(curline); + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl << "- find materials in: " << pathtomat << std::endl; + #endif + // Load Materials LoadMaterials(pathtomat); } From 50dc6614bb388a7d578ff82806fb4d08d006b6a0 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Tue, 28 Mar 2017 10:44:16 +0200 Subject: [PATCH 20/29] condensed console output (one line per mesh) --- Source/OBJ_Loader.h | 54 +++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 31949a86..4c01f19e 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -404,13 +404,29 @@ namespace objl Mesh tempMesh; #ifdef OBJL_CONSOLE_OUTPUT - unsigned int outputIndicator = 0; const unsigned int outputEveryNth = 1000; + unsigned int outputIndicator = outputEveryNth; #endif std::string curline; while (std::getline(file, curline)) { + #ifdef OBJL_CONSOLE_OUTPUT + if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1) + { + if (!meshname.empty()) + { + std::cout + << "\r- " << meshname + << "\t| vertices > " << Positions.size() + << "\t| texcoords > " << TCoords.size() + << "\t| normals > " << Normals.size() + << "\t| triangles > " << (Vertices.size() / 3) + << (!MeshMatNames.empty() ? "\t| material: " + MeshMatNames.back() : ""); + } + } + #endif + // Generate a Mesh Object or Prepare for an object to be created if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g" || curline[0] == 'g') { @@ -460,7 +476,7 @@ namespace objl } } #ifdef OBJL_CONSOLE_OUTPUT - std::cout << std::endl << "- part: " << meshname << std::endl; + std::cout << std::endl; outputIndicator = 0; #endif } @@ -476,13 +492,6 @@ namespace objl vpos.Z = std::stof(spos[2]); Positions.push_back(vpos); - - #ifdef OBJL_CONSOLE_OUTPUT - if (++outputIndicator >= outputEveryNth) { - outputIndicator = 0; - std::cout << "\r- vertices: " << Positions.size(); - } - #endif } // Generate a Vertex Texture Coordinate if (algorithm::firstToken(curline) == "vt") @@ -495,13 +504,6 @@ namespace objl vtex.Y = std::stof(stex[1]); TCoords.push_back(vtex); - - #ifdef OBJL_CONSOLE_OUTPUT - if (++outputIndicator >= outputEveryNth) { - outputIndicator = 0; - std::cout << "\r- texture coords: " << TCoords.size(); - } - #endif } // Generate a Vertex Normal; if (algorithm::firstToken(curline) == "vn") @@ -515,13 +517,6 @@ namespace objl vnor.Z = std::stof(snor[2]); Normals.push_back(vnor); - - #ifdef OBJL_CONSOLE_OUTPUT - if (++outputIndicator >= outputEveryNth) { - outputIndicator = 0; - std::cout << "\r- normals: " << Normals.size(); - } - #endif } // Generate a Face (vertices & indices) if (algorithm::firstToken(curline) == "f") @@ -552,13 +547,6 @@ namespace objl LoadedIndices.push_back(indnum); } - - #ifdef OBJL_CONSOLE_OUTPUT - if (++outputIndicator >= outputEveryNth) { - outputIndicator = 0; - std::cout << "\r- triangles: " << (Vertices.size() / 3); - } - #endif } // Get Mesh Material Name if (algorithm::firstToken(curline) == "usemtl") @@ -566,7 +554,7 @@ namespace objl MeshMatNames.push_back(algorithm::tail(curline)); #ifdef OBJL_CONSOLE_OUTPUT - std::cout << std::endl << "- use material: " << MeshMatNames.back() << std::endl; + outputIndicator = 0; #endif } // Load Materials @@ -600,6 +588,10 @@ namespace objl } } + #ifdef OBJL_CONSOLE_OUTPUT + std::cout << std::endl; + #endif + // Deal with last mesh if (!Indices.empty() && !Vertices.empty()) From a351b2f202c29dc4841593265785feab2b7be934 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Tue, 28 Mar 2017 10:48:46 +0200 Subject: [PATCH 21/29] splitting meshes(groups) on 'usemtl' to support multiple textures on one mesh (mesh splits are enumerated via suffix '_n'). Accepting 'map_bump' in addition to 'map_Bump' --- Source/OBJ_Loader.h | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 4c01f19e..d1c1d93b 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -553,6 +553,30 @@ namespace objl { MeshMatNames.push_back(algorithm::tail(curline)); + // Create new Mesh, if Material changes within a group + if (!Indices.empty() && !Vertices.empty()) + { + // Create Mesh + tempMesh = Mesh(Vertices, Indices); + tempMesh.MeshName = meshname; + int i = 2; + while(1) { + tempMesh.MeshName = meshname + "_" + std::to_string(i); + + for (auto &m : LoadedMeshes) + if (m.MeshName == tempMesh.MeshName) + continue; + break; + } + + // Insert Mesh + LoadedMeshes.push_back(tempMesh); + + // Cleanup + Vertices.clear(); + Indices.clear(); + } + #ifdef OBJL_CONSOLE_OUTPUT outputIndicator = 0; #endif @@ -1065,7 +1089,7 @@ namespace objl tempMaterial.map_d = algorithm::tail(curline); } // Bump Map - if (algorithm::firstToken(curline) == "map_Bump") + if (algorithm::firstToken(curline) == "map_Bump" || algorithm::firstToken(curline) == "map_bump") { tempMaterial.map_bump = algorithm::tail(curline); } From 5de5f4c59eab431383c718e6dd17d114562662f8 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Fri, 7 Apr 2017 14:44:11 +0200 Subject: [PATCH 22/29] Added support for relative (negative) indices in face definitions --- Source/OBJ_Loader.h | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index d1c1d93b..71967c37 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -349,6 +349,18 @@ namespace objl } return ""; } + + // Get element at given index position + template + inline const T & getElement(const std::vector &elements, std::string &index) + { + int idx = std::stoi(index); + if (idx < 0) + idx = int(elements.size()) + idx; + else + idx--; + return elements[idx]; + } } // Class: Loader @@ -724,7 +736,7 @@ namespace objl { case 1: // P { - vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; + vVert.Position = algorithm::getElement(iPositions, svert[0]); vVert.TextureCoordinate = Vector2(0, 0); noNormal = true; oVerts.push_back(vVert); @@ -732,25 +744,25 @@ namespace objl } case 2: // P/T { - vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; - vVert.TextureCoordinate = iTCoords[std::abs(std::stoi(svert[1])) - 1]; + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]); noNormal = true; oVerts.push_back(vVert); break; } case 3: // P//N { - vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; + vVert.Position = algorithm::getElement(iPositions, svert[0]); vVert.TextureCoordinate = Vector2(0, 0); - vVert.Normal = iNormals[std::abs(std::stoi(svert[2])) - 1]; + vVert.Normal = algorithm::getElement(iNormals, svert[2]); oVerts.push_back(vVert); break; } case 4: // P/T/N { - vVert.Position = iPositions[std::abs(std::stoi(svert[0])) - 1]; - vVert.TextureCoordinate = iTCoords[std::abs(std::stoi(svert[1])) - 1]; - vVert.Normal = iNormals[std::abs(std::stoi(svert[2])) - 1]; + vVert.Position = algorithm::getElement(iPositions, svert[0]); + vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]); + vVert.Normal = algorithm::getElement(iNormals, svert[2]); oVerts.push_back(vVert); break; } From a4678c2dfc87806ce407b1023141bd7f5b9eda09 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Tue, 11 Apr 2017 13:59:59 +0200 Subject: [PATCH 23/29] Fixed typo in token extraction (was 'tab' instead of 'space') --- Source/OBJ_Loader.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 71967c37..e02748ea 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -332,20 +332,19 @@ namespace objl // Get first token of string inline std::string firstToken(const std::string &in) { - if (in.empty()) + if (!in.empty()) { - return ""; - } - size_t token_start = in.find_first_not_of(" \t"); - size_t token_end = in.find_first_of(" \t", token_start); - if (token_start != std::string::npos && token_end != std::string::npos) - { - std::string t = in.substr(token_start, token_end); - return in.substr(token_start, token_end - token_start); - } - else if (token_start != std::string::npos) - { - return in.substr(token_start); + size_t token_start = in.find_first_not_of(" \t"); + size_t token_end = in.find_first_of(" \t", token_start); + if (token_start != std::string::npos && token_end != std::string::npos) + { + std::string t = in.substr(token_start, token_end); + return in.substr(token_start, token_end - token_start); + } + else if (token_start != std::string::npos) + { + return in.substr(token_start); + } } return ""; } From 426d6180e59721b1b86e3cbcae6aa7060eae5130 Mon Sep 17 00:00:00 2001 From: schtiefel Date: Tue, 11 Apr 2017 14:28:47 +0200 Subject: [PATCH 24/29] remove trailing spaces while parsing tokens, eg. for texture files --- Source/OBJ_Loader.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index e02748ea..f6ba9c36 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -321,10 +321,15 @@ namespace objl { size_t token_start = in.find_first_not_of(" \t"); size_t space_start = in.find_first_of(" \t", token_start); - size_t space_end = in.find_first_not_of(" \t", space_start); - if (space_end != std::string::npos) + size_t tail_start = in.find_first_not_of(" \t", space_start); + size_t tail_end = in.find_last_not_of(" \t"); + if (tail_start != std::string::npos && tail_end != std::string::npos) { - return in.substr(space_end); + return in.substr(tail_start, tail_end - tail_start + 1); + } + else if (tail_start != std::string::npos) + { + return in.substr(tail_start); } return ""; } @@ -338,7 +343,6 @@ namespace objl size_t token_end = in.find_first_of(" \t", token_start); if (token_start != std::string::npos && token_end != std::string::npos) { - std::string t = in.substr(token_start, token_end); return in.substr(token_start, token_end - token_start); } else if (token_start != std::string::npos) From 1045ad4946340ec89c81f9aea75e81e7da924c01 Mon Sep 17 00:00:00 2001 From: Robert S Date: Thu, 1 Feb 2018 14:10:04 -0800 Subject: [PATCH 25/29] Added token "bump" for bump maps. It was brought to my attention that I didn't include the token "bump" along with "map_bump" for bump maps. Now this supports both implementations. --- Source/OBJ_Loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index f6ba9c36..7f007590 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -1104,7 +1104,7 @@ namespace objl tempMaterial.map_d = algorithm::tail(curline); } // Bump Map - if (algorithm::firstToken(curline) == "map_Bump" || algorithm::firstToken(curline) == "map_bump") + if (algorithm::firstToken(curline) == "map_Bump" || algorithm::firstToken(curline) == "map_bump" || algorithm::firstToken(curline) == "bump") { tempMaterial.map_bump = algorithm::tail(curline); } From d8a1e700f0e9d242d6957cfeaa29bbdec7115578 Mon Sep 17 00:00:00 2001 From: Robert S Date: Thu, 1 Feb 2018 14:14:34 -0800 Subject: [PATCH 26/29] Added math.h includes. For some reason math.h includes were not included. That has been fixed. --- Source/OBJ_Loader.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 7f007590..172f1427 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -11,6 +11,9 @@ // fStream - STD File I/O Library #include +// Math.h - STD math Library +#include + // Print progress to console while loading (large models) #define OBJL_CONSOLE_OUTPUT From d3999e739a8affeae47db061b5a2fe43c028dd44 Mon Sep 17 00:00:00 2001 From: Robert S Date: Tue, 6 Feb 2018 11:34:48 -0800 Subject: [PATCH 27/29] Added Iostream not sure why it was missing. --- Source/OBJ_Loader.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 172f1427..1c81c847 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -2,6 +2,9 @@ #pragma once +// Iostream - STD I/O Library +#include + // Vector - STD Vector/Array Library #include From f4ece22066ec7c27a4b534fa5a9b731647f5c276 Mon Sep 17 00:00:00 2001 From: Bobby S Date: Tue, 17 Jul 2018 15:26:52 -0700 Subject: [PATCH 28/29] Rewrote the inTriangle test and added a few functions... Rewrote the inTriangle point test and added a few functions that would be of use within it. This fixed that function and should be more efficent. --- Source/OBJ_Loader.h | 72 +++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index f6ba9c36..3eeb66fe 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -110,7 +110,12 @@ namespace objl // Float Multiplication Operator Overload Vector3 operator*(const float& other) const { - return Vector3(this->X *other, this->Y * other, this->Z - other); + return Vector3(this->X * other, this->Y * other, this->Z * other); + } + // Float Division Operator Overload + Vector3 operator/(const float& other) const + { + return Vector3(this->X / other, this->Y / other, this->Z / other); } // Positional Variables @@ -237,6 +242,13 @@ namespace objl angle /= (MagnitudeV3(a) * MagnitudeV3(b)); return angle = acosf(angle); } + + // Projection Calculation of a onto b + Vector3 ProjV3(const Vector3 a, const Vector3 b) + { + Vector3 bn = b / MagnitudeV3(b); + return bn * DotV3(a, bn); + } } // Namespace: Algorithm @@ -251,28 +263,50 @@ namespace objl return Vector3(right.X * left, right.Y * left, right.Z * left); } + // A test to see if P1 is on the same side as P2 of a line segment ab + bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) + { + Vector3 cp1 = math::CrossV3(b - a, p1 - a); + Vector3 cp2 = math::CrossV3(b - a, p2 - a); + + if (math::DotV3(cp1, cp2) >= 0) + return true; + else + return false; + } + + // Generate a cross produect normal for a triangle + Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3) + { + Vector3 u = t2 - t1; + Vector3 v = t3 - t1; + + Vector3 normal = math::CrossV3(u,v); + + return normal; + } + // Check to see if a Vector3 Point is within a 3 Vector3 Triangle bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) { - // Starting vars - Vector3 u = tri2 - tri1; - Vector3 v = tri3 - tri1; - Vector3 w = point - tri1; - Vector3 n = math::CrossV3(u, v); - - float y = (math::DotV3(math::CrossV3(u, w), n) / math::DotV3(n, n)); - float b = (math::DotV3(math::CrossV3(u, w), n) / math::DotV3(n, n)); - float a = 1 - y - b; - - // Projected point - Vector3 p = (a * tri1) + (b * tri2) + (y * tri3); - - if (a >= 0 && a <= 1 - && b >= 0 && b <= 1 - && y >= 0 && y <= 1) - { + // Test to see if it is within an infinite prism that the triangle outlines. + bool within_tri_prisim = SameSide(point, tri1, tri2, tri3) && SameSide(point, tri2, tri1, tri3) + && SameSide(point, tri3, tri1, tri2); + + // If it isn't it will never be on the triangle + if (!within_tri_prisim) + return false; + + // Calulate Triangle's Normal + Vector3 n = GenTriNormal(tri1, tri2, tri3); + + // Project the point onto this normal + Vector3 proj = math::ProjV3(point, n); + + // If the distance from the triangle to the point is 0 + // it lies on the triangle + if (math::MagnitudeV3(proj) == 0) return true; - } else return false; } From 0aa487eb10389192fc140a4df901cc7f414500c3 Mon Sep 17 00:00:00 2001 From: Daniel Frenzel Date: Sun, 1 Nov 2020 15:50:06 +0100 Subject: [PATCH 29/29] Inline function, because of trouble when used in header only and/or template libraries. Signed-off-by: Daniel Frenzel --- Source/OBJ_Loader.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/OBJ_Loader.h b/Source/OBJ_Loader.h index 0b7d010d..7abd6563 100644 --- a/Source/OBJ_Loader.h +++ b/Source/OBJ_Loader.h @@ -222,7 +222,7 @@ namespace objl namespace math { // Vector3 Cross Product - Vector3 CrossV3(const Vector3 a, const Vector3 b) + inline Vector3 CrossV3(const Vector3 a, const Vector3 b) { return Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, @@ -230,19 +230,19 @@ namespace objl } // Vector3 Magnitude Calculation - float MagnitudeV3(const Vector3 in) + inline float MagnitudeV3(const Vector3 in) { return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2))); } // Vector3 DotProduct - float DotV3(const Vector3 a, const Vector3 b) + inline float DotV3(const Vector3 a, const Vector3 b) { return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z); } // Angle between 2 Vector3 Objects - float AngleBetweenV3(const Vector3 a, const Vector3 b) + inline float AngleBetweenV3(const Vector3 a, const Vector3 b) { float angle = DotV3(a, b); angle /= (MagnitudeV3(a) * MagnitudeV3(b)); @@ -250,7 +250,7 @@ namespace objl } // Projection Calculation of a onto b - Vector3 ProjV3(const Vector3 a, const Vector3 b) + inline Vector3 ProjV3(const Vector3 a, const Vector3 b) { Vector3 bn = b / MagnitudeV3(b); return bn * DotV3(a, bn); @@ -264,13 +264,13 @@ namespace objl namespace algorithm { // Vector3 Multiplication Opertor Overload - Vector3 operator*(const float& left, const Vector3& right) + inline Vector3 operator*(const float& left, const Vector3& right) { return Vector3(right.X * left, right.Y * left, right.Z * left); } // A test to see if P1 is on the same side as P2 of a line segment ab - bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) + inline bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) { Vector3 cp1 = math::CrossV3(b - a, p1 - a); Vector3 cp2 = math::CrossV3(b - a, p2 - a); @@ -282,7 +282,7 @@ namespace objl } // Generate a cross produect normal for a triangle - Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3) + inline Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3) { Vector3 u = t2 - t1; Vector3 v = t3 - t1; @@ -293,7 +293,7 @@ namespace objl } // Check to see if a Vector3 Point is within a 3 Vector3 Triangle - bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) + inline bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) { // Test to see if it is within an infinite prism that the triangle outlines. bool within_tri_prisim = SameSide(point, tri1, tri2, tri3) && SameSide(point, tri2, tri1, tri3)