diff --git a/Escape-the-Dawn/Escape-the-Dawn.vcxproj b/Escape-the-Dawn/Escape-the-Dawn.vcxproj
index 5ba9649..ad8b4ce 100644
--- a/Escape-the-Dawn/Escape-the-Dawn.vcxproj
+++ b/Escape-the-Dawn/Escape-the-Dawn.vcxproj
@@ -104,6 +104,7 @@
+
@@ -145,6 +146,7 @@
+
diff --git a/Escape-the-Dawn/Escape-the-Dawn.vcxproj.filters b/Escape-the-Dawn/Escape-the-Dawn.vcxproj.filters
index 80dae46..70e92ec 100644
--- a/Escape-the-Dawn/Escape-the-Dawn.vcxproj.filters
+++ b/Escape-the-Dawn/Escape-the-Dawn.vcxproj.filters
@@ -44,6 +44,7 @@
GUI
+
@@ -142,6 +143,7 @@
+
diff --git a/Escape-the-Dawn/OBJ.cpp b/Escape-the-Dawn/OBJ.cpp
new file mode 100644
index 0000000..fb1ae79
--- /dev/null
+++ b/Escape-the-Dawn/OBJ.cpp
@@ -0,0 +1,217 @@
+#include "OBJ.h"
+
+OBJ::OBJ()
+{
+ m_CurrentMaterial = nullptr;
+}
+
+void OBJ::LoadFromFile(std::string filename)
+{
+ m_Path = boost::filesystem::path(filename);
+ LOG_INFO("Parsing .obj \"%s\"", m_Path.c_str());
+
+ // http://paulbourke.net/dataformats/obj/
+ std::ifstream file(m_Path.string());
+
+ std::string line;
+ while (std::getline(file, line)) {
+ if (line.length() == 0)
+ continue;
+
+ std::stringstream ss(line);
+
+ std::string prefix;
+ ss >> prefix;
+
+ // Ignore comments
+ if (prefix == "#")
+ continue;
+
+ // Material files
+ if (prefix == "mtllib") {
+ std::string materialFilename;
+ ss >> materialFilename;
+ m_MaterialPath = m_Path.branch_path() / materialFilename;
+ ParseMaterial();
+ continue;
+ }
+
+ // Material statement
+ if (prefix == "usemtl") {
+ std::string material;
+ ss >> material;
+ m_CurrentMaterial = &Materials[material];
+ continue;
+ }
+
+ // Vertices
+ if (prefix == "v") {
+ float x, y, z;
+ ss >> x >> y >> z;
+ Vertices.push_back(std::make_tuple(x, y, z));
+ continue;
+ }
+
+ // Normals
+ if (prefix == "vn") {
+ float x, y, z;
+ ss >> x >> y >> z;
+ Normals.push_back(std::make_tuple(x, y, z));
+ }
+
+ // Texture coordinates
+ if (prefix == "vt") {
+ float u, v, w;
+ ss >> u >> v >> w;
+ TextureCoords.push_back(std::make_tuple(u, v, w));
+ }
+
+ // Face definitions
+ if (prefix == "f") {
+ Face face;
+ face.Material = m_CurrentMaterial;
+
+ std::string faceDefString;
+ while (ss >> faceDefString) {
+ std::stringstream ss2(faceDefString);
+ FaceDefinition faceDef = { -1, -1, -1 };
+
+ ss2 >> faceDef.VertexIndex;
+ ss2.ignore(); // Ignore first delimiter
+ if (!ss2)
+ continue;
+
+ if (ss2.peek() == '/') {
+ ss2.ignore();
+ ss2 >> faceDef.NormalIndex;
+ } else {
+ ss2 >> faceDef.TextureCoordIndex;
+ ss2.ignore();
+ ss2 >> faceDef.NormalIndex;
+ }
+ face.Definitions.push_back(faceDef);
+ }
+ Faces.push_back(face);
+ }
+ }
+}
+
+void OBJ::ParseMaterial()
+{
+ // http://paulbourke.net/dataformats/mtl/
+ std::ifstream file(m_MaterialPath.string());
+
+ std::string currentMaterialName;
+ MaterialInfo* currentMaterial = nullptr;
+
+ std::string line;
+ while (std::getline(file, line)) {
+ if (line.length() == 0)
+ continue;
+
+ std::stringstream ss(line);
+
+ std::string prefix;
+ ss >> prefix;
+
+ // Create a new material definition
+ if (prefix == "newmtl") {
+ MaterialInfo mat =
+ {
+ "",
+ std::make_tuple(0.2f, 0.2f, 0.2f),
+ std::make_tuple(0.8f, 0.8f, 0.8f),
+ std::make_tuple(1.0f, 1.0f, 1.0f),
+ std::make_tuple(1.0f, 1.0f, 1.0f),
+ 1.0f,
+ 1.0f,
+ 0.0f,
+ 0,
+ };
+
+ ss >> currentMaterialName;
+ LOG_INFO("Parsing material %s", currentMaterialName);
+ Materials[currentMaterialName] = mat;
+ currentMaterial = &Materials[currentMaterialName];
+ continue;
+ }
+
+ if (!currentMaterial)
+ continue;
+
+ // Ambient color
+ if (prefix == "Ka") {
+ float r, g, b;
+ ss >> r >> g >> b;
+ currentMaterial->AmbientColor = std::make_tuple(r, g, b);
+ continue;
+ }
+ // Diffuse color
+ if (prefix == "Kd") {
+ float r, g, b;
+ ss >> r >> g >> b;
+ currentMaterial->DiffuseColor = std::make_tuple(r, g, b);
+ continue;
+ }
+ // Specular color
+ if (prefix == "Ks") {
+ float r, g, b;
+ ss >> r >> g >> b;
+ currentMaterial->SpecularColor = std::make_tuple(r, g, b);
+ continue;
+ }
+ // Transmission filter
+ if (prefix == "Tf") {
+ std::stringstream ss2;
+ ss2 << ss.str();
+
+ std::string command;
+ ss2 >> command;
+ if (command == "xyz") {
+ // TODO: "The "Ks xyz" statement specifies the specular reflectivity using CIEXYZ values."
+ }
+ else if (command == "spectral") {
+ // TODO: "The "Tf spectral" statement specifies the transmission filter using a spectral curve."
+ } else {
+ float r, g, b;
+ ss >> r;
+ // G and B are optional
+ if (!(ss >> g >> b))
+ {
+ g = r;
+ b = r;
+ }
+ currentMaterial->TransmissionFilter = std::make_tuple(r, g, b);
+ }
+ continue;
+ }
+ // Optical density
+ if (prefix == "Ni") {
+ ss >> currentMaterial->OpticalDensity;
+ continue;
+ }
+ // Alpha
+ if (prefix == "d" || prefix == "Tr") {
+ ss >> currentMaterial->Alpha;
+ continue;
+ }
+ // Shininess
+ if (prefix == "Ns") {
+ ss >> currentMaterial->Shininess;
+ continue;
+ }
+ // Illumination model
+ if (prefix == "illum") {
+ int illum = 0;
+ ss >> illum;
+ currentMaterial->IlluminationModel = illum;
+ continue;
+ }
+ // Texture file
+ // TODO:
+ if (prefix == "map_Ka" || prefix == "map_Kd") {
+ ss >> currentMaterial->TextureFile;
+ continue;
+ }
+ }
+}
diff --git a/Escape-the-Dawn/OBJ.h b/Escape-the-Dawn/OBJ.h
new file mode 100644
index 0000000..a8f241f
--- /dev/null
+++ b/Escape-the-Dawn/OBJ.h
@@ -0,0 +1,64 @@
+#ifndef OBJ_h__
+#define OBJ_h__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include