Marine systems simulation
OBJ_Loader.h
1// OBJ_Loader.h - A Single Header OBJ Model Loader
2
3#pragma once
4
5// Iostream - STD I/O Library
6#include <iostream>
7
8// Vector - STD Vector/Array Library
9#include <vector>
10
11// String - STD String Library
12#include <string>
13
14// fStream - STD File I/O Library
15#include <fstream>
16
17// Math.h - STD math Library
18#include <math.h>
19
20// Print progress to console while loading (large models)
21#define OBJL_CONSOLE_OUTPUT
22
23// Namespace: OBJL
24//
25// Description: The namespace that holds eveyrthing that
26// is needed and used for the OBJ Model Loader
27namespace objl
28{
29 // Structure: Vector2
30 //
31 // Description: A 2D Vector that Holds Positional Data
32 struct Vector2
33 {
34 // Default Constructor
35 Vector2()
36 {
37 X = 0.0f;
38 Y = 0.0f;
39 }
40 // Variable Set Constructor
41 Vector2(float X_, float Y_)
42 {
43 X = X_;
44 Y = Y_;
45 }
46 // Bool Equals Operator Overload
47 bool operator==(const Vector2& other) const
48 {
49 return (this->X == other.X && this->Y == other.Y);
50 }
51 // Bool Not Equals Operator Overload
52 bool operator!=(const Vector2& other) const
53 {
54 return !(this->X == other.X && this->Y == other.Y);
55 }
56 // Addition Operator Overload
57 Vector2 operator+(const Vector2& right) const
58 {
59 return Vector2(this->X + right.X, this->Y + right.Y);
60 }
61 // Subtraction Operator Overload
62 Vector2 operator-(const Vector2& right) const
63 {
64 return Vector2(this->X - right.X, this->Y - right.Y);
65 }
66 // Float Multiplication Operator Overload
67 Vector2 operator*(const float& other) const
68 {
69 return Vector2(this->X *other, this->Y * other);
70 }
71
72 // Positional Variables
73 float X;
74 float Y;
75 };
76
77 // Structure: Vector3
78 //
79 // Description: A 3D Vector that Holds Positional Data
80 struct Vector3
81 {
82 // Default Constructor
83 Vector3()
84 {
85 X = 0.0f;
86 Y = 0.0f;
87 Z = 0.0f;
88 }
89 // Variable Set Constructor
90 Vector3(float X_, float Y_, float Z_)
91 {
92 X = X_;
93 Y = Y_;
94 Z = Z_;
95 }
96 // Bool Equals Operator Overload
97 bool operator==(const Vector3& other) const
98 {
99 return (this->X == other.X && this->Y == other.Y && this->Z == other.Z);
100 }
101 // Bool Not Equals Operator Overload
102 bool operator!=(const Vector3& other) const
103 {
104 return !(this->X == other.X && this->Y == other.Y && this->Z == other.Z);
105 }
106 // Addition Operator Overload
107 Vector3 operator+(const Vector3& right) const
108 {
109 return Vector3(this->X + right.X, this->Y + right.Y, this->Z + right.Z);
110 }
111 // Subtraction Operator Overload
112 Vector3 operator-(const Vector3& right) const
113 {
114 return Vector3(this->X - right.X, this->Y - right.Y, this->Z - right.Z);
115 }
116 // Float Multiplication Operator Overload
117 Vector3 operator*(const float& other) const
118 {
119 return Vector3(this->X * other, this->Y * other, this->Z * other);
120 }
121 // Float Division Operator Overload
122 Vector3 operator/(const float& other) const
123 {
124 return Vector3(this->X / other, this->Y / other, this->Z / other);
125 }
126
127 // Positional Variables
128 float X;
129 float Y;
130 float Z;
131 };
132
133 // Structure: Vertex
134 //
135 // Description: Model Vertex object that holds
136 // a Position, Normal, and Texture Coordinate
137 struct Vertex
138 {
139 // Position Vector
140 Vector3 Position;
141
142 // Normal Vector
143 Vector3 Normal;
144
145 // Texture Coordinate Vector
146 Vector2 TextureCoordinate;
147 };
148
149 struct Material
150 {
151 Material()
152 {
153 name;
154 Ns = 0.0f;
155 Ni = 0.0f;
156 d = 0.0f;
157 illum = 0;
158 }
159
160 // Material Name
161 std::string name;
162 // Ambient Color
163 Vector3 Ka;
164 // Diffuse Color
165 Vector3 Kd;
166 // Specular Color
167 Vector3 Ks;
168 // Specular Exponent
169 float Ns;
170 // Optical Density
171 float Ni;
172 // Dissolve
173 float d;
174 // Illumination
175 int illum;
176 // Ambient Texture Map
177 std::string map_Ka;
178 // Diffuse Texture Map
179 std::string map_Kd;
180 // Specular Texture Map
181 std::string map_Ks;
182 // Specular Hightlight Map
183 std::string map_Ns;
184 // Alpha Texture Map
185 std::string map_d;
186 // Bump Map
187 std::string map_bump;
188 };
189
190 // Structure: Mesh
191 //
192 // Description: A Simple Mesh Object that holds
193 // a name, a vertex list, and an index list
194 struct Mesh
195 {
196 // Default Constructor
197 Mesh()
198 {
199
200 }
201 // Variable Set Constructor
202 Mesh(std::vector<Vertex>& _Vertices, std::vector<unsigned int>& _Indices)
203 {
204 Vertices = _Vertices;
205 Indices = _Indices;
206 }
207 // Mesh Name
208 std::string MeshName;
209 // Vertex List
210 std::vector<Vertex> Vertices;
211 // Index List
212 std::vector<unsigned int> Indices;
213
214 // Material
215 Material MeshMaterial;
216 };
217
218 // Namespace: Math
219 //
220 // Description: The namespace that holds all of the math
221 // functions need for OBJL
222 namespace math
223 {
224 // Vector3 Cross Product
225 Vector3 CrossV3(const Vector3 a, const Vector3 b)
226 {
227 return Vector3(a.Y * b.Z - a.Z * b.Y,
228 a.Z * b.X - a.X * b.Z,
229 a.X * b.Y - a.Y * b.X);
230 }
231
232 // Vector3 Magnitude Calculation
233 float MagnitudeV3(const Vector3 in)
234 {
235 return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2)));
236 }
237
238 // Vector3 DotProduct
239 float DotV3(const Vector3 a, const Vector3 b)
240 {
241 return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
242 }
243
244 // Angle between 2 Vector3 Objects
245 float AngleBetweenV3(const Vector3 a, const Vector3 b)
246 {
247 float angle = DotV3(a, b);
248 angle /= (MagnitudeV3(a) * MagnitudeV3(b));
249 return angle = acosf(angle);
250 }
251
252 // Projection Calculation of a onto b
253 Vector3 ProjV3(const Vector3 a, const Vector3 b)
254 {
255 Vector3 bn = b / MagnitudeV3(b);
256 return bn * DotV3(a, bn);
257 }
258 }
259
260 // Namespace: Algorithm
261 //
262 // Description: The namespace that holds all of the
263 // Algorithms needed for OBJL
264 namespace algorithm
265 {
266 // Vector3 Multiplication Opertor Overload
267 Vector3 operator*(const float& left, const Vector3& right)
268 {
269 return Vector3(right.X * left, right.Y * left, right.Z * left);
270 }
271
272 // A test to see if P1 is on the same side as P2 of a line segment ab
273 bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b)
274 {
275 Vector3 cp1 = math::CrossV3(b - a, p1 - a);
276 Vector3 cp2 = math::CrossV3(b - a, p2 - a);
277
278 if (math::DotV3(cp1, cp2) >= 0)
279 return true;
280 else
281 return false;
282 }
283
284 // Generate a cross produect normal for a triangle
285 Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3)
286 {
287 Vector3 u = t2 - t1;
288 Vector3 v = t3 - t1;
289
290 Vector3 normal = math::CrossV3(u,v);
291
292 return normal;
293 }
294
295 // Check to see if a Vector3 Point is within a 3 Vector3 Triangle
296 bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3)
297 {
298 // Test to see if it is within an infinite prism that the triangle outlines.
299 bool within_tri_prisim = SameSide(point, tri1, tri2, tri3) && SameSide(point, tri2, tri1, tri3)
300 && SameSide(point, tri3, tri1, tri2);
301
302 // If it isn't it will never be on the triangle
303 if (!within_tri_prisim)
304 return false;
305
306 // Calulate Triangle's Normal
307 Vector3 n = GenTriNormal(tri1, tri2, tri3);
308
309 // Project the point onto this normal
310 Vector3 proj = math::ProjV3(point, n);
311
312 // If the distance from the triangle to the point is 0
313 // it lies on the triangle
314 if (math::MagnitudeV3(proj) == 0)
315 return true;
316 else
317 return false;
318 }
319
320 // Split a String into a string array at a given token
321 inline void split(const std::string &in,
322 std::vector<std::string> &out,
323 std::string token)
324 {
325 out.clear();
326
327 std::string temp;
328
329 for (int i = 0; i < int(in.size()); i++)
330 {
331 std::string test = in.substr(i, token.size());
332
333 if (test == token)
334 {
335 if (!temp.empty())
336 {
337 out.push_back(temp);
338 temp.clear();
339 i += (int)token.size() - 1;
340 }
341 else
342 {
343 out.push_back("");
344 }
345 }
346 else if (i + token.size() >= in.size())
347 {
348 temp += in.substr(i, token.size());
349 out.push_back(temp);
350 break;
351 }
352 else
353 {
354 temp += in[i];
355 }
356 }
357 }
358
359 // Get tail of string after first token and possibly following spaces
360 inline std::string tail(const std::string &in)
361 {
362 size_t token_start = in.find_first_not_of(" \t");
363 size_t space_start = in.find_first_of(" \t", token_start);
364 size_t tail_start = in.find_first_not_of(" \t", space_start);
365 size_t tail_end = in.find_last_not_of(" \t");
366 if (tail_start != std::string::npos && tail_end != std::string::npos)
367 {
368 return in.substr(tail_start, tail_end - tail_start + 1);
369 }
370 else if (tail_start != std::string::npos)
371 {
372 return in.substr(tail_start);
373 }
374 return "";
375 }
376
377 // Get first token of string
378 inline std::string firstToken(const std::string &in)
379 {
380 if (!in.empty())
381 {
382 size_t token_start = in.find_first_not_of(" \t");
383 size_t token_end = in.find_first_of(" \t", token_start);
384 if (token_start != std::string::npos && token_end != std::string::npos)
385 {
386 return in.substr(token_start, token_end - token_start);
387 }
388 else if (token_start != std::string::npos)
389 {
390 return in.substr(token_start);
391 }
392 }
393 return "";
394 }
395
396 // Get element at given index position
397 template <class T>
398 inline const T & getElement(const std::vector<T> &elements, std::string &index)
399 {
400 int idx = std::stoi(index);
401 if (idx < 0)
402 idx = int(elements.size()) + idx;
403 else
404 idx--;
405 return elements[idx];
406 }
407 }
408
409 // Class: Loader
410 //
411 // Description: The OBJ Model Loader
412 class Loader
413 {
414 public:
415 // Default Constructor
416 Loader()
417 {
418
419 }
420 ~Loader()
421 {
422 LoadedMeshes.clear();
423 }
424
425 // Load a file into the loader
426 //
427 // If file is loaded return true
428 //
429 // If the file is unable to be found
430 // or unable to be loaded return false
431 bool LoadFile(std::string Path)
432 {
433 // If the file is not an .obj file return false
434 if (Path.substr(Path.size() - 4, 4) != ".obj")
435 return false;
436
437
438 std::ifstream file(Path);
439
440 if (!file.is_open())
441 return false;
442
443 LoadedMeshes.clear();
444 LoadedVertices.clear();
445 LoadedIndices.clear();
446
447 std::vector<Vector3> Positions;
448 std::vector<Vector2> TCoords;
449 std::vector<Vector3> Normals;
450
451 std::vector<Vertex> Vertices;
452 std::vector<unsigned int> Indices;
453
454 std::vector<std::string> MeshMatNames;
455
456 bool listening = false;
457 std::string meshname;
458
459 Mesh tempMesh;
460
461 #ifdef OBJL_CONSOLE_OUTPUT
462 const unsigned int outputEveryNth = 1000;
463 unsigned int outputIndicator = outputEveryNth;
464 #endif
465
466 std::string curline;
467 while (std::getline(file, curline))
468 {
469 #ifdef OBJL_CONSOLE_OUTPUT
470 if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1)
471 {
472 if (!meshname.empty())
473 {
474 std::cout
475 << "\r- " << meshname
476 << "\t| vertices > " << Positions.size()
477 << "\t| texcoords > " << TCoords.size()
478 << "\t| normals > " << Normals.size()
479 << "\t| triangles > " << (Vertices.size() / 3)
480 << (!MeshMatNames.empty() ? "\t| material: " + MeshMatNames.back() : "");
481 }
482 }
483 #endif
484
485 // Generate a Mesh Object or Prepare for an object to be created
486 if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g" || curline[0] == 'g')
487 {
488 if (!listening)
489 {
490 listening = true;
491
492 if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g")
493 {
494 meshname = algorithm::tail(curline);
495 }
496 else
497 {
498 meshname = "unnamed";
499 }
500 }
501 else
502 {
503 // Generate the mesh to put into the array
504
505 if (!Indices.empty() && !Vertices.empty())
506 {
507 // Create Mesh
508 tempMesh = Mesh(Vertices, Indices);
509 tempMesh.MeshName = meshname;
510
511 // Insert Mesh
512 LoadedMeshes.push_back(tempMesh);
513
514 // Cleanup
515 Vertices.clear();
516 Indices.clear();
517 meshname.clear();
518
519 meshname = algorithm::tail(curline);
520 }
521 else
522 {
523 if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g")
524 {
525 meshname = algorithm::tail(curline);
526 }
527 else
528 {
529 meshname = "unnamed";
530 }
531 }
532 }
533 #ifdef OBJL_CONSOLE_OUTPUT
534 std::cout << std::endl;
535 outputIndicator = 0;
536 #endif
537 }
538 // Generate a Vertex Position
539 if (algorithm::firstToken(curline) == "v")
540 {
541 std::vector<std::string> spos;
542 Vector3 vpos;
543 algorithm::split(algorithm::tail(curline), spos, " ");
544
545 vpos.X = std::stof(spos[0]);
546 vpos.Y = std::stof(spos[1]);
547 vpos.Z = std::stof(spos[2]);
548
549 Positions.push_back(vpos);
550 }
551 // Generate a Vertex Texture Coordinate
552 if (algorithm::firstToken(curline) == "vt")
553 {
554 std::vector<std::string> stex;
555 Vector2 vtex;
556 algorithm::split(algorithm::tail(curline), stex, " ");
557
558 vtex.X = std::stof(stex[0]);
559 vtex.Y = std::stof(stex[1]);
560
561 TCoords.push_back(vtex);
562 }
563 // Generate a Vertex Normal;
564 if (algorithm::firstToken(curline) == "vn")
565 {
566 std::vector<std::string> snor;
567 Vector3 vnor;
568 algorithm::split(algorithm::tail(curline), snor, " ");
569
570 vnor.X = std::stof(snor[0]);
571 vnor.Y = std::stof(snor[1]);
572 vnor.Z = std::stof(snor[2]);
573
574 Normals.push_back(vnor);
575 }
576 // Generate a Face (vertices & indices)
577 if (algorithm::firstToken(curline) == "f")
578 {
579 // Generate the vertices
580 std::vector<Vertex> vVerts;
581 GenVerticesFromRawOBJ(vVerts, Positions, TCoords, Normals, curline);
582
583 // Add Vertices
584 for (int i = 0; i < int(vVerts.size()); i++)
585 {
586 Vertices.push_back(vVerts[i]);
587
588 LoadedVertices.push_back(vVerts[i]);
589 }
590
591 std::vector<unsigned int> iIndices;
592
593 VertexTriangluation(iIndices, vVerts);
594
595 // Add Indices
596 for (int i = 0; i < int(iIndices.size()); i++)
597 {
598 unsigned int indnum = (unsigned int)((Vertices.size()) - vVerts.size()) + iIndices[i];
599 Indices.push_back(indnum);
600
601 indnum = (unsigned int)((LoadedVertices.size()) - vVerts.size()) + iIndices[i];
602 LoadedIndices.push_back(indnum);
603
604 }
605 }
606 // Get Mesh Material Name
607 if (algorithm::firstToken(curline) == "usemtl")
608 {
609 MeshMatNames.push_back(algorithm::tail(curline));
610
611 // Create new Mesh, if Material changes within a group
612 if (!Indices.empty() && !Vertices.empty())
613 {
614 // Create Mesh
615 tempMesh = Mesh(Vertices, Indices);
616 tempMesh.MeshName = meshname;
617 int i = 2;
618 while(1) {
619 tempMesh.MeshName = meshname + "_" + std::to_string(i);
620
621 for (auto &m : LoadedMeshes)
622 if (m.MeshName == tempMesh.MeshName)
623 continue;
624 break;
625 }
626
627 // Insert Mesh
628 LoadedMeshes.push_back(tempMesh);
629
630 // Cleanup
631 Vertices.clear();
632 Indices.clear();
633 }
634
635 #ifdef OBJL_CONSOLE_OUTPUT
636 outputIndicator = 0;
637 #endif
638 }
639 // Load Materials
640 if (algorithm::firstToken(curline) == "mtllib")
641 {
642 // Generate LoadedMaterial
643
644 // Generate a path to the material file
645 std::vector<std::string> temp;
646 algorithm::split(Path, temp, "/");
647
648 std::string pathtomat = "";
649
650 if (temp.size() != 1)
651 {
652 for (int i = 0; i < temp.size() - 1; i++)
653 {
654 pathtomat += temp[i] + "/";
655 }
656 }
657
658
659 pathtomat += algorithm::tail(curline);
660
661 #ifdef OBJL_CONSOLE_OUTPUT
662 std::cout << std::endl << "- find materials in: " << pathtomat << std::endl;
663 #endif
664
665 // Load Materials
666 LoadMaterials(pathtomat);
667 }
668 }
669
670 #ifdef OBJL_CONSOLE_OUTPUT
671 std::cout << std::endl;
672 #endif
673
674 // Deal with last mesh
675
676 if (!Indices.empty() && !Vertices.empty())
677 {
678 // Create Mesh
679 tempMesh = Mesh(Vertices, Indices);
680 tempMesh.MeshName = meshname;
681
682 // Insert Mesh
683 LoadedMeshes.push_back(tempMesh);
684 }
685
686 file.close();
687
688 // Set Materials for each Mesh
689 for (int i = 0; i < MeshMatNames.size(); i++)
690 {
691 std::string matname = MeshMatNames[i];
692
693 // Find corresponding material name in loaded materials
694 // when found copy material variables into mesh material
695 for (int j = 0; j < LoadedMaterials.size(); j++)
696 {
697 if (LoadedMaterials[j].name == matname)
698 {
699 LoadedMeshes[i].MeshMaterial = LoadedMaterials[j];
700 break;
701 }
702 }
703 }
704
705 if (LoadedMeshes.empty() && LoadedVertices.empty() && LoadedIndices.empty())
706 {
707 return false;
708 }
709 else
710 {
711 return true;
712 }
713 }
714
715 // Loaded Mesh Objects
716 std::vector<Mesh> LoadedMeshes;
717 // Loaded Vertex Objects
718 std::vector<Vertex> LoadedVertices;
719 // Loaded Index Positions
720 std::vector<unsigned int> LoadedIndices;
721 // Loaded Material Objects
722 std::vector<Material> LoadedMaterials;
723
724 private:
725 // Generate vertices from a list of positions,
726 // tcoords, normals and a face line
727 void GenVerticesFromRawOBJ(std::vector<Vertex>& oVerts,
728 const std::vector<Vector3>& iPositions,
729 const std::vector<Vector2>& iTCoords,
730 const std::vector<Vector3>& iNormals,
731 std::string icurline)
732 {
733 std::vector<std::string> sface, svert;
734 Vertex vVert;
735 algorithm::split(algorithm::tail(icurline), sface, " ");
736
737 bool noNormal = false;
738
739 // For every given vertex do this
740 for (int i = 0; i < int(sface.size()); i++)
741 {
742 // See What type the vertex is.
743 int vtype;
744
745 algorithm::split(sface[i], svert, "/");
746
747 // Check for just position - v1
748 if (svert.size() == 1)
749 {
750 // Only position
751 vtype = 1;
752 }
753
754 // Check for position & texture - v1/vt1
755 if (svert.size() == 2)
756 {
757 // Position & Texture
758 vtype = 2;
759 }
760
761 // Check for Position, Texture and Normal - v1/vt1/vn1
762 // or if Position and Normal - v1//vn1
763 if (svert.size() == 3)
764 {
765 if (svert[1] != "")
766 {
767 // Position, Texture, and Normal
768 vtype = 4;
769 }
770 else
771 {
772 // Position & Normal
773 vtype = 3;
774 }
775 }
776
777 // Calculate and store the vertex
778 switch (vtype)
779 {
780 case 1: // P
781 {
782 vVert.Position = algorithm::getElement(iPositions, svert[0]);
783 vVert.TextureCoordinate = Vector2(0, 0);
784 noNormal = true;
785 oVerts.push_back(vVert);
786 break;
787 }
788 case 2: // P/T
789 {
790 vVert.Position = algorithm::getElement(iPositions, svert[0]);
791 vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]);
792 noNormal = true;
793 oVerts.push_back(vVert);
794 break;
795 }
796 case 3: // P//N
797 {
798 vVert.Position = algorithm::getElement(iPositions, svert[0]);
799 vVert.TextureCoordinate = Vector2(0, 0);
800 vVert.Normal = algorithm::getElement(iNormals, svert[2]);
801 oVerts.push_back(vVert);
802 break;
803 }
804 case 4: // P/T/N
805 {
806 vVert.Position = algorithm::getElement(iPositions, svert[0]);
807 vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]);
808 vVert.Normal = algorithm::getElement(iNormals, svert[2]);
809 oVerts.push_back(vVert);
810 break;
811 }
812 default:
813 {
814 break;
815 }
816 }
817 }
818
819 // take care of missing normals
820 // these may not be truly acurate but it is the
821 // best they get for not compiling a mesh with normals
822 if (noNormal)
823 {
824 Vector3 A = oVerts[0].Position - oVerts[1].Position;
825 Vector3 B = oVerts[2].Position - oVerts[1].Position;
826
827 Vector3 normal = math::CrossV3(A, B);
828
829 for (int i = 0; i < int(oVerts.size()); i++)
830 {
831 oVerts[i].Normal = normal;
832 }
833 }
834 }
835
836 // Triangulate a list of vertices into a face by printing
837 // inducies corresponding with triangles within it
838 void VertexTriangluation(std::vector<unsigned int>& oIndices,
839 const std::vector<Vertex>& iVerts)
840 {
841 // If there are 2 or less verts,
842 // no triangle can be created,
843 // so exit
844 if (iVerts.size() < 3)
845 {
846 return;
847 }
848 // If it is a triangle no need to calculate it
849 if (iVerts.size() == 3)
850 {
851 oIndices.push_back(0);
852 oIndices.push_back(1);
853 oIndices.push_back(2);
854 return;
855 }
856
857 // Create a list of vertices
858 std::vector<Vertex> tVerts = iVerts;
859
860 while (true)
861 {
862 // For every vertex
863 for (int i = 0; i < int(tVerts.size()); i++)
864 {
865 // pPrev = the previous vertex in the list
866 Vertex pPrev;
867 if (i == 0)
868 {
869 pPrev = tVerts[tVerts.size() - 1];
870 }
871 else
872 {
873 pPrev = tVerts[i - 1];
874 }
875
876 // pCur = the current vertex;
877 Vertex pCur = tVerts[i];
878
879 // pNext = the next vertex in the list
880 Vertex pNext;
881 if (i == tVerts.size() - 1)
882 {
883 pNext = tVerts[0];
884 }
885 else
886 {
887 pNext = tVerts[i + 1];
888 }
889
890 // Check to see if there are only 3 verts left
891 // if so this is the last triangle
892 if (tVerts.size() == 3)
893 {
894 // Create a triangle from pCur, pPrev, pNext
895 for (int j = 0; j < int(tVerts.size()); j++)
896 {
897 if (iVerts[j].Position == pCur.Position)
898 oIndices.push_back(j);
899 if (iVerts[j].Position == pPrev.Position)
900 oIndices.push_back(j);
901 if (iVerts[j].Position == pNext.Position)
902 oIndices.push_back(j);
903 }
904
905 tVerts.clear();
906 break;
907 }
908 if (tVerts.size() == 4)
909 {
910 // Create a triangle from pCur, pPrev, pNext
911 for (int j = 0; j < int(iVerts.size()); j++)
912 {
913 if (iVerts[j].Position == pCur.Position)
914 oIndices.push_back(j);
915 if (iVerts[j].Position == pPrev.Position)
916 oIndices.push_back(j);
917 if (iVerts[j].Position == pNext.Position)
918 oIndices.push_back(j);
919 }
920
921 Vector3 tempVec;
922 for (int j = 0; j < int(tVerts.size()); j++)
923 {
924 if (tVerts[j].Position != pCur.Position
925 && tVerts[j].Position != pPrev.Position
926 && tVerts[j].Position != pNext.Position)
927 {
928 tempVec = tVerts[j].Position;
929 break;
930 }
931 }
932
933 // Create a triangle from pCur, pPrev, pNext
934 for (int j = 0; j < int(iVerts.size()); j++)
935 {
936 if (iVerts[j].Position == pPrev.Position)
937 oIndices.push_back(j);
938 if (iVerts[j].Position == pNext.Position)
939 oIndices.push_back(j);
940 if (iVerts[j].Position == tempVec)
941 oIndices.push_back(j);
942 }
943
944 tVerts.clear();
945 break;
946 }
947
948 // If Vertex is not an interior vertex
949 float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359);
950 if (angle <= 0 && angle >= 180)
951 continue;
952
953 // If any vertices are within this triangle
954 bool inTri = false;
955 for (int j = 0; j < int(iVerts.size()); j++)
956 {
957 if (algorithm::inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position, pNext.Position)
958 && iVerts[j].Position != pPrev.Position
959 && iVerts[j].Position != pCur.Position
960 && iVerts[j].Position != pNext.Position)
961 {
962 inTri = true;
963 break;
964 }
965 }
966 if (inTri)
967 continue;
968
969 // Create a triangle from pCur, pPrev, pNext
970 for (int j = 0; j < int(iVerts.size()); j++)
971 {
972 if (iVerts[j].Position == pCur.Position)
973 oIndices.push_back(j);
974 if (iVerts[j].Position == pPrev.Position)
975 oIndices.push_back(j);
976 if (iVerts[j].Position == pNext.Position)
977 oIndices.push_back(j);
978 }
979
980 // Delete pCur from the list
981 for (int j = 0; j < int(tVerts.size()); j++)
982 {
983 if (tVerts[j].Position == pCur.Position)
984 {
985 tVerts.erase(tVerts.begin() + j);
986 break;
987 }
988 }
989
990 // reset i to the start
991 // -1 since loop will add 1 to it
992 i = -1;
993 }
994
995 // if no triangles were created
996 if (oIndices.size() == 0)
997 break;
998
999 // if no more vertices
1000 if (tVerts.size() == 0)
1001 break;
1002 }
1003 }
1004
1005 // Load Materials from .mtl file
1006 bool LoadMaterials(std::string path)
1007 {
1008 // If the file is not a material file return false
1009 if (path.substr(path.size() - 4, path.size()) != ".mtl")
1010 return false;
1011
1012 std::ifstream file(path);
1013
1014 // If the file is not found return false
1015 if (!file.is_open())
1016 return false;
1017
1018 Material tempMaterial;
1019
1020 bool listening = false;
1021
1022 // Go through each line looking for material variables
1023 std::string curline;
1024 while (std::getline(file, curline))
1025 {
1026 // new material and material name
1027 if (algorithm::firstToken(curline) == "newmtl")
1028 {
1029 if (!listening)
1030 {
1031 listening = true;
1032
1033 if (curline.size() > 7)
1034 {
1035 tempMaterial.name = algorithm::tail(curline);
1036 }
1037 else
1038 {
1039 tempMaterial.name = "none";
1040 }
1041 }
1042 else
1043 {
1044 // Generate the material
1045
1046 // Push Back loaded Material
1047 LoadedMaterials.push_back(tempMaterial);
1048
1049 // Clear Loaded Material
1050 tempMaterial = Material();
1051
1052 if (curline.size() > 7)
1053 {
1054 tempMaterial.name = algorithm::tail(curline);
1055 }
1056 else
1057 {
1058 tempMaterial.name = "none";
1059 }
1060 }
1061 }
1062 // Ambient Color
1063 if (algorithm::firstToken(curline) == "Ka")
1064 {
1065 std::vector<std::string> temp;
1066 algorithm::split(algorithm::tail(curline), temp, " ");
1067
1068 if (temp.size() != 3)
1069 continue;
1070
1071 tempMaterial.Ka.X = std::stof(temp[0]);
1072 tempMaterial.Ka.Y = std::stof(temp[1]);
1073 tempMaterial.Ka.Z = std::stof(temp[2]);
1074 }
1075 // Diffuse Color
1076 if (algorithm::firstToken(curline) == "Kd")
1077 {
1078 std::vector<std::string> temp;
1079 algorithm::split(algorithm::tail(curline), temp, " ");
1080
1081 if (temp.size() != 3)
1082 continue;
1083
1084 tempMaterial.Kd.X = std::stof(temp[0]);
1085 tempMaterial.Kd.Y = std::stof(temp[1]);
1086 tempMaterial.Kd.Z = std::stof(temp[2]);
1087 }
1088 // Specular Color
1089 if (algorithm::firstToken(curline) == "Ks")
1090 {
1091 std::vector<std::string> temp;
1092 algorithm::split(algorithm::tail(curline), temp, " ");
1093
1094 if (temp.size() != 3)
1095 continue;
1096
1097 tempMaterial.Ks.X = std::stof(temp[0]);
1098 tempMaterial.Ks.Y = std::stof(temp[1]);
1099 tempMaterial.Ks.Z = std::stof(temp[2]);
1100 }
1101 // Specular Exponent
1102 if (algorithm::firstToken(curline) == "Ns")
1103 {
1104 tempMaterial.Ns = std::stof(algorithm::tail(curline));
1105 }
1106 // Optical Density
1107 if (algorithm::firstToken(curline) == "Ni")
1108 {
1109 tempMaterial.Ni = std::stof(algorithm::tail(curline));
1110 }
1111 // Dissolve
1112 if (algorithm::firstToken(curline) == "d")
1113 {
1114 tempMaterial.d = std::stof(algorithm::tail(curline));
1115 }
1116 // Illumination
1117 if (algorithm::firstToken(curline) == "illum")
1118 {
1119 tempMaterial.illum = std::stoi(algorithm::tail(curline));
1120 }
1121 // Ambient Texture Map
1122 if (algorithm::firstToken(curline) == "map_Ka")
1123 {
1124 tempMaterial.map_Ka = algorithm::tail(curline);
1125 }
1126 // Diffuse Texture Map
1127 if (algorithm::firstToken(curline) == "map_Kd")
1128 {
1129 tempMaterial.map_Kd = algorithm::tail(curline);
1130 }
1131 // Specular Texture Map
1132 if (algorithm::firstToken(curline) == "map_Ks")
1133 {
1134 tempMaterial.map_Ks = algorithm::tail(curline);
1135 }
1136 // Specular Hightlight Map
1137 if (algorithm::firstToken(curline) == "map_Ns")
1138 {
1139 tempMaterial.map_Ns = algorithm::tail(curline);
1140 }
1141 // Alpha Texture Map
1142 if (algorithm::firstToken(curline) == "map_d")
1143 {
1144 tempMaterial.map_d = algorithm::tail(curline);
1145 }
1146 // Bump Map
1147 if (algorithm::firstToken(curline) == "map_Bump" || algorithm::firstToken(curline) == "map_bump" || algorithm::firstToken(curline) == "bump")
1148 {
1149 tempMaterial.map_bump = algorithm::tail(curline);
1150 }
1151 }
1152
1153 // Deal with last material
1154
1155 // Push Back loaded Material
1156 LoadedMaterials.push_back(tempMaterial);
1157
1158 // Test to see if anything was loaded
1159 // If not return false
1160 if (LoadedMaterials.empty())
1161 return false;
1162 // If so return true
1163 else
1164 return true;
1165 }
1166 };
1167}
Definition: OBJ_Loader.h:413
Definition: OBJ_Loader.h:150
Definition: OBJ_Loader.h:195
Definition: OBJ_Loader.h:33
Definition: OBJ_Loader.h:81
Definition: OBJ_Loader.h:138