From d2b167f2d1ca15e42a177b65cdf34f35592452f7 Mon Sep 17 00:00:00 2001
From: Dan Goodliffe <dan@randomdan.homeip.net>
Date: Sat, 13 Feb 2021 14:52:03 +0000
Subject: New .obj parser, packer, mesher

---
 gfx/models/mesh.cpp       |  70 ++++----
 gfx/models/mesh.h         |  17 +-
 gfx/models/obj.h          |  36 ++++
 gfx/models/obj.ll         | 121 +++++++++++++
 gfx/models/obj_loader.cpp | 429 ----------------------------------------------
 gfx/models/obj_loader.h   |  53 ------
 6 files changed, 207 insertions(+), 519 deletions(-)
 create mode 100644 gfx/models/obj.h
 create mode 100644 gfx/models/obj.ll
 delete mode 100644 gfx/models/obj_loader.cpp
 delete mode 100644 gfx/models/obj_loader.h

(limited to 'gfx/models')

diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp
index 4a3d9d4..25a4b58 100644
--- a/gfx/models/mesh.cpp
+++ b/gfx/models/mesh.cpp
@@ -1,41 +1,19 @@
 #include "mesh.h"
-#include "obj_loader.h"
+#include "obj.h"
 #include "vertex.hpp"
-#include <memory>
+#include <algorithm>
+#include <glm/glm.hpp>
+#include <iterator>
+#include <resource.h>
 #include <vector>
 
-Mesh::Mesh(const std::string & fileName) : Mesh(OBJModel(fileName).ToIndexedModel()) { }
+Mesh::Mesh(const std::filesystem::path & fileName) : Mesh(ObjParser {Resource::mapPath(fileName)}) { }
 
-Mesh::Mesh(const IndexedModel & model) :
-	m_vertexArrayObject {}, m_vertexArrayBuffers {}, m_numIndices {model.indices.size()}, mode {GL_TRIANGLES}
-{
-	glGenVertexArrays(1, &m_vertexArrayObject);
-	glBindVertexArray(m_vertexArrayObject);
-
-	glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers.data());
-
-	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
-	glBufferData(
-			GL_ARRAY_BUFFER, sizeof(model.positions[0]) * model.positions.size(), &model.positions[0], GL_STATIC_DRAW);
-	glEnableVertexAttribArray(0);
-	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
-
-	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TEXCOORD_VB]);
-	glBufferData(
-			GL_ARRAY_BUFFER, sizeof(model.texCoords[0]) * model.texCoords.size(), &model.texCoords[0], GL_STATIC_DRAW);
-	glEnableVertexAttribArray(1);
-	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+Mesh::Mesh(const ObjParser & obj) : Mesh(packObjParser(obj), GL_TRIANGLES) { }
 
-	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[NORMAL_VB]);
-	glBufferData(GL_ARRAY_BUFFER, sizeof(model.normals[0]) * model.normals.size(), &model.normals[0], GL_STATIC_DRAW);
-	glEnableVertexAttribArray(2);
-	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
-
-	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vertexArrayBuffers[INDEX_VB]);
-	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(model.indices[0]) * model.indices.size(), &model.indices[0],
-			GL_STATIC_DRAW);
-
-	glBindVertexArray(0);
+Mesh::Mesh(std::pair<std::vector<Vertex>, std::vector<unsigned int>> && vandi, GLenum m) :
+	Mesh(vandi.first, vandi.second, m)
+{
 }
 
 Mesh::Mesh(std::span<Vertex> vertices, std::span<unsigned int> indices, GLenum m) :
@@ -64,6 +42,34 @@ Mesh::Mesh(std::span<Vertex> vertices, std::span<unsigned int> indices, GLenum m
 	glBindVertexArray(0);
 }
 
+Mesh::Data
+Mesh::packObjParser(const ObjParser & obj)
+{
+	std::vector<Vertex> vertices;
+	std::vector<ObjParser::FaceElement> vertexOrder;
+	std::vector<unsigned int> indices;
+	std::for_each(obj.faces.begin(), obj.faces.end(), [&](const ObjParser::Face & face) {
+		for (auto idx = 2U; idx < face.size(); idx += 1) {
+			auto f = [&](auto idx) {
+				const auto & fe {face[idx]};
+				if (const auto existing = std::find(vertexOrder.begin(), vertexOrder.end(), fe);
+						existing != vertexOrder.end()) {
+					indices.push_back(std::distance(vertexOrder.begin(), existing));
+				}
+				else {
+					indices.push_back(vertices.size());
+					vertices.emplace_back(obj.vertices[fe.x - 1], obj.texCoords[fe.y - 1], -obj.normals[fe.z - 1]);
+					vertexOrder.emplace_back(fe);
+				}
+			};
+			f(0);
+			f(idx);
+			f(idx - 1);
+		}
+	});
+	return std::make_pair(vertices, indices);
+}
+
 Mesh::~Mesh()
 {
 	glDeleteBuffers(NUM_BUFFERS, m_vertexArrayBuffers.data());
diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h
index ebd300b..4982145 100644
--- a/gfx/models/mesh.h
+++ b/gfx/models/mesh.h
@@ -4,19 +4,22 @@
 #include <GL/glew.h>
 #include <array>
 #include <cstddef>
+#include <filesystem>
+#include <gfx/models/vertex.hpp>
 #include <span>
 #include <special_members.hpp>
-#include <string>
+#include <utility>
+#include <vector>
 
-class IndexedModel;
-class Vertex;
+class ObjParser;
 
 enum MeshBufferPositions { POSITION_VB, TEXCOORD_VB, NORMAL_VB, INDEX_VB };
 
 class Mesh {
 public:
-	explicit Mesh(const std::string & fileName);
-	explicit Mesh(const IndexedModel & model);
+	using Data = std::pair<std::vector<Vertex>, std::vector<unsigned int>>;
+	explicit Mesh(const std::filesystem::path & fileName);
+	explicit Mesh(const ObjParser & obj);
 	Mesh(std::span<Vertex> vertices, std::span<unsigned int> indices, GLenum = GL_TRIANGLES);
 	virtual ~Mesh();
 
@@ -26,6 +29,10 @@ public:
 	void Draw() const;
 
 private:
+	explicit Mesh(Data && vandi, GLenum = GL_TRIANGLES);
+
+	static Data packObjParser(const ObjParser &);
+
 	static constexpr unsigned int NUM_BUFFERS {4};
 
 	GLuint m_vertexArrayObject;
diff --git a/gfx/models/obj.h b/gfx/models/obj.h
new file mode 100644
index 0000000..96c5e94
--- /dev/null
+++ b/gfx/models/obj.h
@@ -0,0 +1,36 @@
+#ifndef OBJ_H
+#define OBJ_H
+
+#ifndef yyFlexLexer
+#	define yyFlexLexer objbaseFlexLexer
+#	include <FlexLexer.h>
+#endif
+#include <filesystem>
+#include <fstream>
+#include <glm/glm.hpp>
+#include <memory>
+#include <vector>
+
+class ObjParser : yyFlexLexer {
+public:
+	explicit ObjParser(const std::filesystem::path & fileName) : ObjParser {std::make_unique<std::ifstream>(fileName)}
+	{
+	}
+
+	explicit ObjParser(std::unique_ptr<std::istream> in) : yyFlexLexer(in.get())
+	{
+		ObjParser::yylex();
+	}
+
+	int yylex() override;
+
+	std::vector<glm::vec4> vertices;
+	std::vector<glm::vec3> texCoords;
+	std::vector<glm::vec3> normals;
+	using FaceElement = glm::vec<3, int>;
+	using Face = std::vector<FaceElement>;
+	std::vector<Face> faces;
+	glm::length_t axis {0};
+};
+
+#endif
diff --git a/gfx/models/obj.ll b/gfx/models/obj.ll
new file mode 100644
index 0000000..9329a5a
--- /dev/null
+++ b/gfx/models/obj.ll
@@ -0,0 +1,121 @@
+%option batch
+%option c++
+%option noyywrap
+%option 8bit
+%option stack
+%option yyclass="ObjParser"
+%option prefix="objbase"
+
+%{
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#if __clang__
+#pragma GCC diagnostic ignored "-Wnull-conversion"
+#endif
+#include <gfx/models/obj.h>
+#include <glm/glm.hpp>
+#include <memory>
+#include <string>
+#include <vector>
+class objbaseFlexLexer;
+%}
+
+comment #.*
+float -?[0-9.]+
+index -?[0-9]+
+linestring [^\r\n]*
+truthy (1|on)
+falsey (0|off)
+
+%x FACE
+%x MTLLIB
+%x OBJECT
+%x SMOOTH
+%x USEMTL
+%x VERTEX
+%x NORMAL
+%x TEXCOORD
+
+%%
+
+<INITIAL>{comment} {
+  // fprintf(stderr, "COMMENT %s\n", YYText());
+}
+<INITIAL>"f " {
+  BEGIN(FACE);
+  faces.emplace_back();
+  faces.back().emplace_back();
+  axis = 0;
+}
+<INITIAL>"mtllib " {
+  BEGIN(MTLLIB);
+}
+<INITIAL>"o " {
+  BEGIN(OBJECT);
+}
+<INITIAL>"s " {
+  BEGIN(SMOOTH);
+}
+<INITIAL>"usemtl " {
+  BEGIN(USEMTL);
+}
+<INITIAL>"v " {
+  BEGIN(VERTEX);
+  vertices.emplace_back(0, 0, 0, 1);
+  axis = 0;
+}
+<INITIAL>"vn " {
+  BEGIN(NORMAL);
+  normals.emplace_back(0, 0, 0);
+  axis = 0;
+}
+<INITIAL>"vt " {
+  BEGIN(TEXCOORD);
+  texCoords.emplace_back(0, 1, 1);
+  axis = 0;
+}
+<USEMTL>{linestring} {
+  // fprintf(stderr, "USEMTL <%s>\n", YYText());
+}
+<MTLLIB>{linestring} {
+  // fprintf(stderr, "MTLLIB <%s>\n", YYText());
+}
+<OBJECT>{linestring} {
+  // fprintf(stderr, "OBJECT <%s>\n", YYText());
+}
+<SMOOTH>{truthy} {
+  // fprintf(stderr, "Set smooth\n");
+}
+<SMOOTH>{falsey} {
+  // fprintf(stderr, "Set flat\n");
+}
+<VERTEX>{float} {
+  vertices.back()[axis++] = std::stof(YYText());
+}
+<NORMAL>{float} {
+  normals.back()[axis++] = std::stof(YYText());
+}
+<TEXCOORD>{float} {
+  texCoords.back()[axis++] = std::stof(YYText());
+}
+<FACE>{index} {
+  faces.back().back()[axis] = std::stoi(YYText());
+}
+<FACE>\/ {
+  axis++;
+}
+<FACE>[ \t] {
+  faces.back().emplace_back();
+  axis = 0;
+}
+
+<*>[ \t] {
+}
+<*>[\r\n\f] {
+  BEGIN(INITIAL);
+}
+
+%%
+
+// Make iwyu think unistd.h is required
+[[maybe_unused]]static auto x=getpid;
diff --git a/gfx/models/obj_loader.cpp b/gfx/models/obj_loader.cpp
deleted file mode 100644
index 75ab251..0000000
--- a/gfx/models/obj_loader.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-#include "obj_loader.h"
-#include <algorithm>
-#include <fstream>
-#include <map>
-#include <memory>
-#include <resource.h>
-#include <stdexcept>
-#include <utility>
-
-static bool CompareOBJIndexPtr(const OBJIndex * a, const OBJIndex * b);
-static inline unsigned int FindNextChar(unsigned int start, const char * str, unsigned int length, char token);
-static inline unsigned int ParseOBJIndexValue(const std::string & token, unsigned int start, unsigned int end);
-static inline float ParseOBJFloatValue(const std::string & token, unsigned int start, unsigned int end);
-static inline std::vector<std::string> SplitString(const std::string & s, char delim);
-
-OBJModel::OBJModel(const std::filesystem::path & fileName)
-{
-	hasUVs = false;
-	hasNormals = false;
-	std::ifstream file;
-	file.open(Resource::mapPath(fileName).c_str());
-
-	std::string line;
-	if (file.is_open()) {
-		while (file.good()) {
-			getline(file, line);
-
-			unsigned int lineLength = line.length();
-
-			if (lineLength < 2) {
-				continue;
-			}
-
-			const char * lineCStr = line.c_str();
-
-			switch (lineCStr[0]) {
-				case 'v':
-					switch (lineCStr[1]) {
-						case 't':
-							this->uvs.push_back(ParseOBJVec2(line));
-							break;
-						case 'n':
-							this->normals.push_back(ParseOBJVec3(line));
-							break;
-						case ' ':
-						case '\t':
-							this->vertices.push_back(ParseOBJVec3(line));
-					}
-					break;
-				case 'f':
-					CreateOBJFace(line);
-					break;
-				default:
-					break;
-			};
-		}
-	}
-	else {
-		throw std::runtime_error {"Unable to load mesh: " + fileName.string()};
-	}
-}
-
-void
-IndexedModel::CalcNormals()
-{
-	for (unsigned int i = 0; i < indices.size(); i += 3) {
-		int i0 = indices[i];
-		int i1 = indices[i + 1];
-		int i2 = indices[i + 2];
-
-		glm::vec3 v1 = positions[i1] - positions[i0];
-		glm::vec3 v2 = positions[i2] - positions[i0];
-
-		glm::vec3 normal = glm::normalize(glm::cross(v1, v2));
-
-		normals[i0] += normal;
-		normals[i1] += normal;
-		normals[i2] += normal;
-	}
-
-	for (unsigned int i = 0; i < positions.size(); i++) {
-		normals[i] = glm::normalize(normals[i]);
-	}
-}
-
-IndexedModel
-OBJModel::ToIndexedModel()
-{
-	IndexedModel result;
-	IndexedModel normalModel;
-
-	unsigned int numIndices = OBJIndices.size();
-
-	std::vector<OBJIndex *> indexLookup;
-
-	for (unsigned int i = 0; i < numIndices; i++) {
-		indexLookup.push_back(&OBJIndices[i]);
-	}
-
-	std::sort(indexLookup.begin(), indexLookup.end(), CompareOBJIndexPtr);
-
-	std::map<OBJIndex, unsigned int> normalModelIndexMap;
-	std::map<unsigned int, unsigned int> indexMap;
-
-	for (unsigned int i = 0; i < numIndices; i++) {
-		OBJIndex * currentIndex = &OBJIndices[i];
-
-		glm::vec3 currentPosition = vertices[currentIndex->vertexIndex];
-		glm::vec2 currentTexCoord;
-		glm::vec3 currentNormal;
-
-		if (hasUVs) {
-			currentTexCoord = uvs[currentIndex->uvIndex];
-		}
-		else {
-			currentTexCoord = glm::vec2(0, 0);
-		}
-
-		if (hasNormals) {
-			currentNormal = normals[currentIndex->normalIndex];
-		}
-		else {
-			currentNormal = glm::vec3(0, 0, 0);
-		}
-
-		unsigned int normalModelIndex;
-		unsigned int resultModelIndex;
-
-		// Create model to properly generate normals on
-		const auto it = normalModelIndexMap.find(*currentIndex);
-		if (it == normalModelIndexMap.end()) {
-			normalModelIndex = normalModel.positions.size();
-
-			normalModelIndexMap.insert(std::pair<OBJIndex, unsigned int>(*currentIndex, normalModelIndex));
-			normalModel.positions.push_back(currentPosition);
-			normalModel.texCoords.push_back(currentTexCoord);
-			normalModel.normals.push_back(currentNormal);
-		}
-		else {
-			normalModelIndex = it->second;
-		}
-
-		// Create model which properly separates texture coordinates
-		unsigned int previousVertexLocation = FindLastVertexIndex(indexLookup, currentIndex, result);
-
-		if (previousVertexLocation == (unsigned int)-1) {
-			resultModelIndex = result.positions.size();
-
-			result.positions.push_back(currentPosition);
-			result.texCoords.push_back(currentTexCoord);
-			result.normals.push_back(currentNormal);
-		}
-		else {
-			resultModelIndex = previousVertexLocation;
-		}
-
-		normalModel.indices.push_back(normalModelIndex);
-		result.indices.push_back(resultModelIndex);
-		indexMap.insert(std::pair<unsigned int, unsigned int>(resultModelIndex, normalModelIndex));
-	}
-
-	if (!hasNormals) {
-		normalModel.CalcNormals();
-
-		for (unsigned int i = 0; i < result.positions.size(); i++) {
-			result.normals[i] = normalModel.normals[indexMap[i]];
-		}
-	}
-
-	return result;
-};
-
-unsigned int
-OBJModel::FindLastVertexIndex(
-		const std::vector<OBJIndex *> & indexLookup, const OBJIndex * currentIndex, const IndexedModel & result)
-{
-	unsigned int start = 0;
-	unsigned int end = indexLookup.size();
-	unsigned int current = (end - start) / 2 + start;
-	unsigned int previous = start;
-
-	while (current != previous) {
-		OBJIndex * testIndex = indexLookup[current];
-
-		if (testIndex->vertexIndex == currentIndex->vertexIndex) {
-			unsigned int countStart = current;
-
-			for (unsigned int i = 0; i < current; i++) {
-				OBJIndex * possibleIndex = indexLookup[current - i];
-
-				if (possibleIndex == currentIndex) {
-					continue;
-				}
-
-				if (possibleIndex->vertexIndex != currentIndex->vertexIndex) {
-					break;
-				}
-
-				countStart--;
-			}
-
-			for (unsigned int i = countStart; i < indexLookup.size() - countStart; i++) {
-				OBJIndex * possibleIndex = indexLookup[current + i];
-
-				if (possibleIndex == currentIndex) {
-					continue;
-				}
-
-				if (possibleIndex->vertexIndex != currentIndex->vertexIndex) {
-					break;
-				}
-				else if ((!hasUVs || possibleIndex->uvIndex == currentIndex->uvIndex)
-						&& (!hasNormals || possibleIndex->normalIndex == currentIndex->normalIndex)) {
-					glm::vec3 currentPosition = vertices[currentIndex->vertexIndex];
-					glm::vec2 currentTexCoord;
-					glm::vec3 currentNormal;
-
-					if (hasUVs) {
-						currentTexCoord = uvs[currentIndex->uvIndex];
-					}
-					else {
-						currentTexCoord = glm::vec2(0, 0);
-					}
-
-					if (hasNormals) {
-						currentNormal = normals[currentIndex->normalIndex];
-					}
-					else {
-						currentNormal = glm::vec3(0, 0, 0);
-					}
-
-					for (unsigned int j = 0; j < result.positions.size(); j++) {
-						if (currentPosition == result.positions[j]
-								&& ((!hasUVs || currentTexCoord == result.texCoords[j])
-										&& (!hasNormals || currentNormal == result.normals[j]))) {
-							return j;
-						}
-					}
-				}
-			}
-
-			return -1;
-		}
-		else {
-			if (testIndex->vertexIndex < currentIndex->vertexIndex) {
-				start = current;
-			}
-			else {
-				end = current;
-			}
-		}
-
-		previous = current;
-		current = (end - start) / 2 + start;
-	}
-
-	return -1;
-}
-
-void
-OBJModel::CreateOBJFace(const std::string & line)
-{
-	std::vector<std::string> tokens = SplitString(line, ' ');
-
-	this->OBJIndices.push_back(ParseOBJIndex(tokens[1], &this->hasUVs, &this->hasNormals));
-	this->OBJIndices.push_back(ParseOBJIndex(tokens[2], &this->hasUVs, &this->hasNormals));
-	this->OBJIndices.push_back(ParseOBJIndex(tokens[3], &this->hasUVs, &this->hasNormals));
-
-	if ((int)tokens.size() > 4) {
-		this->OBJIndices.push_back(ParseOBJIndex(tokens[1], &this->hasUVs, &this->hasNormals));
-		this->OBJIndices.push_back(ParseOBJIndex(tokens[3], &this->hasUVs, &this->hasNormals));
-		this->OBJIndices.push_back(ParseOBJIndex(tokens[4], &this->hasUVs, &this->hasNormals));
-	}
-}
-
-OBJIndex
-OBJModel::ParseOBJIndex(const std::string & token, bool * hasUVs, bool * hasNormals)
-{
-	unsigned int tokenLength = token.length();
-	const char * tokenString = token.c_str();
-
-	unsigned int vertIndexStart = 0;
-	unsigned int vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, '/');
-
-	OBJIndex result {
-			.vertexIndex = ParseOBJIndexValue(token, vertIndexStart, vertIndexEnd),
-			.uvIndex = 0,
-			.normalIndex = 0,
-	};
-
-	if (vertIndexEnd >= tokenLength) {
-		return result;
-	}
-
-	vertIndexStart = vertIndexEnd + 1;
-	vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, '/');
-
-	result.uvIndex = ParseOBJIndexValue(token, vertIndexStart, vertIndexEnd);
-	*hasUVs = true;
-
-	if (vertIndexEnd >= tokenLength) {
-		return result;
-	}
-
-	vertIndexStart = vertIndexEnd + 1;
-	vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, '/');
-
-	result.normalIndex = ParseOBJIndexValue(token, vertIndexStart, vertIndexEnd);
-	*hasNormals = true;
-
-	return result;
-}
-
-glm::vec3
-OBJModel::ParseOBJVec3(const std::string & line)
-{
-	unsigned int tokenLength = line.length();
-	const char * tokenString = line.c_str();
-
-	unsigned int vertIndexStart = 2;
-
-	while (vertIndexStart < tokenLength) {
-		if (tokenString[vertIndexStart] != ' ') {
-			break;
-		}
-		vertIndexStart++;
-	}
-
-	unsigned int vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, ' ');
-
-	float x = ParseOBJFloatValue(line, vertIndexStart, vertIndexEnd);
-
-	vertIndexStart = vertIndexEnd + 1;
-	vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, ' ');
-
-	float y = ParseOBJFloatValue(line, vertIndexStart, vertIndexEnd);
-
-	vertIndexStart = vertIndexEnd + 1;
-	vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, ' ');
-
-	float z = ParseOBJFloatValue(line, vertIndexStart, vertIndexEnd);
-
-	return glm::vec3(x, y, z);
-}
-
-glm::vec2
-OBJModel::ParseOBJVec2(const std::string & line)
-{
-	unsigned int tokenLength = line.length();
-	const char * tokenString = line.c_str();
-
-	unsigned int vertIndexStart = 3;
-
-	while (vertIndexStart < tokenLength) {
-		if (tokenString[vertIndexStart] != ' ') {
-			break;
-		}
-		vertIndexStart++;
-	}
-
-	unsigned int vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, ' ');
-
-	float x = ParseOBJFloatValue(line, vertIndexStart, vertIndexEnd);
-
-	vertIndexStart = vertIndexEnd + 1;
-	vertIndexEnd = FindNextChar(vertIndexStart, tokenString, tokenLength, ' ');
-
-	float y = ParseOBJFloatValue(line, vertIndexStart, vertIndexEnd);
-
-	return glm::vec2(x, y);
-}
-
-static bool
-CompareOBJIndexPtr(const OBJIndex * a, const OBJIndex * b)
-{
-	return a->vertexIndex < b->vertexIndex;
-}
-
-static inline unsigned int
-FindNextChar(unsigned int start, const char * str, unsigned int length, char token)
-{
-	unsigned int result = start;
-	while (result < length) {
-		result++;
-		if (str[result] == token) {
-			break;
-		}
-	}
-
-	return result;
-}
-
-static inline unsigned int
-ParseOBJIndexValue(const std::string & token, unsigned int start, unsigned int end)
-{
-	return std::stoul(token.substr(start, end - start)) - 1;
-}
-
-static inline float
-ParseOBJFloatValue(const std::string & token, unsigned int start, unsigned int end)
-{
-	return std::stof(token.substr(start, end - start));
-}
-
-static inline std::vector<std::string>
-SplitString(const std::string & s, char delim)
-{
-	std::vector<std::string> elems;
-
-	const char * cstr = s.c_str();
-	unsigned int strLength = s.length();
-	unsigned int start = 0;
-	unsigned int end = 0;
-
-	while (end <= strLength) {
-		while (end <= strLength) {
-			if (cstr[end] == delim) {
-				break;
-			}
-			end++;
-		}
-
-		elems.push_back(s.substr(start, end - start));
-		start = end + 1;
-		end = start;
-	}
-
-	return elems;
-}
diff --git a/gfx/models/obj_loader.h b/gfx/models/obj_loader.h
deleted file mode 100644
index 2fe3d35..0000000
--- a/gfx/models/obj_loader.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef OBJ_LOADER_H_INCLUDED
-#define OBJ_LOADER_H_INCLUDED
-
-#include <filesystem>
-#include <glm/glm.hpp>
-#include <string>
-#include <vector>
-
-struct OBJIndex {
-	unsigned int vertexIndex;
-	unsigned int uvIndex;
-	unsigned int normalIndex;
-
-	bool
-	operator<(const OBJIndex & r) const
-	{
-		return vertexIndex < r.vertexIndex;
-	}
-};
-
-class IndexedModel {
-public:
-	std::vector<glm::vec3> positions;
-	std::vector<glm::vec2> texCoords;
-	std::vector<glm::vec3> normals;
-	std::vector<unsigned int> indices;
-
-	void CalcNormals();
-};
-
-class OBJModel {
-public:
-	std::vector<OBJIndex> OBJIndices;
-	std::vector<glm::vec3> vertices;
-	std::vector<glm::vec2> uvs;
-	std::vector<glm::vec3> normals;
-	bool hasUVs;
-	bool hasNormals;
-
-	explicit OBJModel(const std::filesystem::path & fileName);
-
-	IndexedModel ToIndexedModel();
-
-private:
-	unsigned int FindLastVertexIndex(
-			const std::vector<OBJIndex *> & indexLookup, const OBJIndex * currentIndex, const IndexedModel & result);
-	void CreateOBJFace(const std::string & line);
-	static glm::vec2 ParseOBJVec2(const std::string & line);
-	static glm::vec3 ParseOBJVec3(const std::string & line);
-	static OBJIndex ParseOBJIndex(const std::string & token, bool * hasUVs, bool * hasNormals);
-};
-
-#endif // OBJ_LOADER_H_INCLUDED
-- 
cgit v1.2.3