From bae89e65a16b4d59a17f41cf05511f01b8b89220 Mon Sep 17 00:00:00 2001
From: "Grayson Riffe (Laptop)" <graysonriffe@yahoo.com>
Date: Wed, 3 Nov 2021 14:03:49 -0500
Subject: [PATCH] Optimized model loader

---
 NothinFancy/src/Assets.cpp         |   4 +-
 NothinFancy/src/Renderer/Model.cpp | 173 ++++++++++++++---------------
 NothinFancy/src/include/Assets.h   |   1 -
 NothinFancy/src/include/Model.h    |  26 ++++-
 4 files changed, 109 insertions(+), 95 deletions(-)

diff --git a/NothinFancy/src/Assets.cpp b/NothinFancy/src/Assets.cpp
index bddc155..8b19971 100644
--- a/NothinFancy/src/Assets.cpp
+++ b/NothinFancy/src/Assets.cpp
@@ -234,9 +234,9 @@ namespace nf {
 					}
 				}
 				assetContents = assetContents.substr(assetContents.find("\n") + 1);
-				model->data = new char[assetSize + 1];
+				model->data = new char[assetSize];
 				std::memcpy(model->data, &assetContents[0], assetSize);
-				model->data[assetSize] = '\0';
+				model->size = assetSize;
 				if (packName == "base.nfpack")
 					model->isBaseAsset = true;
 				m_assets[assetName] = model;
diff --git a/NothinFancy/src/Renderer/Model.cpp b/NothinFancy/src/Renderer/Model.cpp
index 9feb091..86667ca 100644
--- a/NothinFancy/src/Renderer/Model.cpp
+++ b/NothinFancy/src/Renderer/Model.cpp
@@ -2,6 +2,7 @@
 
 #include <map>
 #include <algorithm>
+#include <sstream>
 #include "glm/glm.hpp"
 #include "glm/gtc/matrix_transform.hpp"
 #include "GL/glew.h"
@@ -13,122 +14,68 @@
 
 namespace nf {
 	Model::Model(AModel* model, bool physicsExport) :
-		m_base(model->isBaseAsset)
+		m_base(model->isBaseAsset),
+		m_newMtl("newmtl"),
+		m_newLine("\n")
 	{
 		if (model->neededTextures.size() > 32)
 			Error("Model exceedes 32 texture limit!");
-		std::string obj = model->data;
-		size_t startMtlPos = obj.find("newmtl");
-		std::string mtl = obj.substr(startMtlPos);
-		struct TempMaterial {
-			std::vector<float> outVB;
-			std::vector<float> unindexedVB;
-			std::vector<unsigned int> vbIndices;
-			std::vector<float> outTC;
-			std::vector<float> unindexedTC;
-			std::vector<unsigned int> tcIndices;
-			std::vector<float> outVN;
-			std::vector<float> unindexedVN;
-			std::vector<unsigned int> vnIndices;
-			std::vector<float> unindexedTan;
-			std::vector<float> outTan;
-			std::vector<unsigned int> outIB;
-			unsigned int ibCount = 0;
+		std::vector<char> file(model->data, model->data + model->size);
+		size_t mtlPos = std::search(file.begin(), file.end(), m_newMtl, m_newMtl + std::strlen(m_newMtl)) - file.begin();
+		std::vector<char> mtl(&file[mtlPos], &file[0] + file.size());
 
-			std::string diffuseTextureName;
-			Vec3 diffuseColor;
-			std::string specularTextureName;
-			std::string normalTextureName;
-			float shininess = 1.0f;
-		};
 		std::unordered_map<std::string, TempMaterial*> mats;
-		std::string currMat;
-		while (true) {
-			char line[500];
-			int result = sscanf_s(mtl.c_str(), "\n%s", line, (unsigned)_countof(line));
-			if (result == EOF)
-				break;
-			if (std::strcmp(line, "newmtl") == 0) {
-				char matName[100];
-				sscanf_s(mtl.c_str(), "\nnewmtl %s\n", matName, (unsigned)_countof(matName));
-				currMat = matName;
-				mats[currMat] = new TempMaterial;
-			}
-			else if (std::strcmp(line, "Kd") == 0) {
-				float r = 0.0, g = 0.0, b = 0.0;
-				sscanf_s(mtl.c_str(), "\nKd %f %f %f\n", &r, &g, &b);
-				mats[currMat]->diffuseColor = Vec3(r, g, b);
-			}
-			else if (std::strcmp(line, "map_Kd") == 0) {
-				char texName[100];
-				sscanf_s(mtl.c_str(), "\nmap_Kd %s\n", texName, (unsigned)_countof(texName));
-				mats[currMat]->diffuseTextureName = (std::string)texName;
-			}
-			else if (std::strcmp(line, "map_Ks") == 0) {
-				char texName[100];
-				sscanf_s(mtl.c_str(), "\nmap_Ks %s\n", texName, (unsigned)_countof(texName));
-				mats[currMat]->specularTextureName = (std::string)texName;
-			}
-			else if (std::strcmp(line, "map_Bump") == 0) {
-				char texName[100];
-				sscanf_s(mtl.c_str(), "\nmap_Bump %s\n", texName, (unsigned)_countof(texName));
-				mats[currMat]->normalTextureName = (std::string)texName;
-			}
-			else if (std::strcmp(line, "Ns") == 0) {
-				float s = 0.0;
-				sscanf_s(mtl.c_str(), "\nNs %f\n", &s);
-				mats[currMat]->shininess = s;
-			}
-			size_t pos = mtl.find(line) + strlen(line);
-			mtl = mtl.substr(pos);
-		}
+		parseMaterials(mats, mtl);
 
-		std::string file = obj.substr(0, startMtlPos);
+		std::vector<char> obj(&file[0], &file[mtlPos]);
 		std::vector<float> vbRaw, tcRaw, vnRaw;
 		std::string usingMat;
 
 		bool tcPresent = false, vnPresent = false;
+		size_t position = 0;
 		while (true) {
-			char line[500];
-			int remove = 0;
-			int result = sscanf_s(file.c_str(), "\n%s", line, (unsigned)_countof(line));
-			if (result == EOF)
+			size_t nextLine = std::search(obj.begin() + position, obj.end(), m_newLine, m_newLine + std::strlen(m_newLine)) - (obj.begin() + position);
+			if (!nextLine)
 				break;
-			if (std::strcmp(line, "v") == 0) {
-				float x = 0.0, y = 0.0, z = 0.0;
-				sscanf_s(file.c_str(), "\nv %f %f %f\n", &x, &y, &z);
-				remove = 28;
+			std::vector<char> line(&obj[position], &obj[position + nextLine]);
+			position += nextLine + 1;
+			std::stringstream ss(std::string(&line[0], line.size()));
+			std::string firstWord;
+			ss >> firstWord;
+
+			if (std::strcmp(&firstWord[0], "v") == 0) {
+				float x = 0.0f, y = 0.0f, z = 0.0f;
+				ss >> x >> y >> z;
 				vbRaw.push_back(x);
 				vbRaw.push_back(y);
 				vbRaw.push_back(z);
 			}
-			else if (std::strcmp(line, "vt") == 0) {
+			else if (std::strcmp(&firstWord[0], "vt") == 0) {
 				tcPresent = true;
-				float u = 0.0, v = 0.0;
-				sscanf_s(file.c_str(), "\nvt %f %f\n", &u, &v);
-				remove = 18;
+				float u = 0.0f, v = 0.0f;
+				ss >> u >> v;
 				tcRaw.push_back(u);
 				tcRaw.push_back(v);
 			}
-			else if (std::strcmp(line, "vn") == 0) {
+			else if (std::strcmp(&firstWord[0], "vn") == 0) {
 				vnPresent = true;
-				float x = 0.0, y = 0.0, z = 0.0;
-				sscanf_s(file.c_str(), "\nvn %f %f %f\n", &x, &y, &z);
-				remove = 20;
+				float x = 0.0f, y = 0.0f, z = 0.0f;
+				ss >> x >> y >> z;
 				vnRaw.push_back(x);
 				vnRaw.push_back(y);
 				vnRaw.push_back(z);
 			}
-			else if (std::strcmp(line, "usemtl") == 0) {
-				char matName[100];
-				sscanf_s(file.c_str(), "\nusemtl %s\n", matName, (unsigned)_countof(matName));
+			else if (std::strcmp(&firstWord[0], "usemtl") == 0) {
+				std::string matName;
+				ss >> matName;
 				usingMat = matName;
-				remove = 5;
 			}
-			else if (std::strcmp(line, "f") == 0) {
+			else if (std::strcmp(&firstWord[0], "f") == 0) {
 				unsigned int vertexIndex[3], uvIndex[3], vnIndex[3];
-				sscanf_s(file.c_str(), "\nf %d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &vnIndex[0], &vertexIndex[1], &uvIndex[1], &vnIndex[1], &vertexIndex[2], &uvIndex[2], &vnIndex[2]);
-				remove = 15;
+				char temp;
+				ss >> vertexIndex[0] >> temp >> uvIndex[0] >> temp >> vnIndex[0] >> vertexIndex[1] >> temp >> uvIndex[1] >> temp >> vnIndex[1] >> vertexIndex[2] >> temp >> uvIndex[2] >> temp >> vnIndex[2];
+				if (ss.rdbuf()->in_avail() > 1)
+					Error("Model has non-triangle faces!");
 				mats[usingMat]->vbIndices.push_back(vertexIndex[0]);
 				mats[usingMat]->vbIndices.push_back(vertexIndex[1]);
 				mats[usingMat]->vbIndices.push_back(vertexIndex[2]);
@@ -139,9 +86,6 @@ namespace nf {
 				mats[usingMat]->vnIndices.push_back(vnIndex[1]);
 				mats[usingMat]->vnIndices.push_back(vnIndex[2]);
 			}
-
-			size_t pos = file.find(line) + strlen(line) + remove;
-			file.erase(0, pos);
 		}
 
 		if (!tcPresent)
@@ -311,6 +255,53 @@ namespace nf {
 			Application::getApp()->getPhysicsEngine()->addMesh(this, vboPositions);
 	}
 
+	void Model::parseMaterials(std::unordered_map<std::string, TempMaterial*>& mats, std::vector<char>& mtl) {
+		std::string currMat;
+		size_t position = 0;
+		while (true) {
+			size_t nextLine = std::search(mtl.begin() + position, mtl.end(), m_newLine, m_newLine + std::strlen(m_newLine)) - (mtl.begin() + position);
+			if (position + nextLine >= mtl.size())
+				break;
+			std::vector<char> line(&mtl[position], &mtl[position + nextLine]);
+			position += nextLine + 1;
+			std::stringstream ss(std::string(&line[0], line.size()));
+			std::string firstWord;
+			ss >> firstWord;
+
+			if (std::strcmp(&firstWord[0], "newmtl") == 0) {
+				std::string matName;
+				ss >> matName;
+				currMat = matName;
+				mats[currMat] = new TempMaterial;
+			}
+			else if (std::strcmp(&firstWord[0], "Kd") == 0) {
+				float r = 0.0f, g = 0.0f, b = 0.0f;
+				ss >> r >> g >> b;
+				mats[currMat]->diffuseColor = Vec3(r, g, b);
+			}
+			else if (std::strcmp(&firstWord[0], "map_Kd") == 0) {
+				std::string texName;
+				ss >> texName;
+				mats[currMat]->diffuseTextureName = texName;
+			}
+			else if (std::strcmp(&firstWord[0], "map_Ks") == 0) {
+				std::string texName;
+				ss >> texName;
+				mats[currMat]->specularTextureName = texName;
+			}
+			else if (std::strcmp(&firstWord[0], "map_Bump") == 0) {
+				std::string texName;
+				ss >> texName;
+				mats[currMat]->normalTextureName = texName;
+			}
+			else if (std::strcmp(&firstWord[0], "Ns") == 0) {
+				float s = 0.0f;
+				ss >> s;
+				mats[currMat]->shininess = s;
+			}
+		}
+	}
+
 	void Model::render(Shader* shader, bool onlyDepth, unsigned int count) {
 		m_vao->bind();
 		m_ib->bind();
diff --git a/NothinFancy/src/include/Assets.h b/NothinFancy/src/include/Assets.h
index 6dce338..d4c76c6 100644
--- a/NothinFancy/src/include/Assets.h
+++ b/NothinFancy/src/include/Assets.h
@@ -23,7 +23,6 @@ namespace nf {
 
 	struct AModel : Asset {
 		Model* loadedModel = nullptr;
-
 		std::unordered_map<std::string, ATexture*> neededTextures;
 
 		~AModel() override;
diff --git a/NothinFancy/src/include/Model.h b/NothinFancy/src/include/Model.h
index 3b134b6..ca8394f 100644
--- a/NothinFancy/src/include/Model.h
+++ b/NothinFancy/src/include/Model.h
@@ -19,10 +19,34 @@ namespace nf {
 
 		~Model();
 	private:
-		void parseMaterials(std::unordered_map<std::string, TempMaterial*>& mats, std::string& mtl);
+		struct TempMaterial {
+			std::vector<float> outVB;
+			std::vector<float> unindexedVB;
+			std::vector<unsigned int> vbIndices;
+			std::vector<float> outTC;
+			std::vector<float> unindexedTC;
+			std::vector<unsigned int> tcIndices;
+			std::vector<float> outVN;
+			std::vector<float> unindexedVN;
+			std::vector<unsigned int> vnIndices;
+			std::vector<float> unindexedTan;
+			std::vector<float> outTan;
+			std::vector<unsigned int> outIB;
+			unsigned int ibCount = 0;
+
+			std::string diffuseTextureName;
+			Vec3 diffuseColor;
+			std::string specularTextureName;
+			std::string normalTextureName;
+			float shininess = 1.0f;
+		};
+
+		void parseMaterials(std::unordered_map<std::string, TempMaterial*>& mats, std::vector<char>& mtl);
 
 		bool m_base;
 		std::vector<std::tuple<Texture*, Texture*, Texture*, float, float, float, float>> m_materials;
+		const char* m_newMtl;
+		const char* m_newLine;
 		const std::string m_hasDiffString = "hasDiffuseTex[";
 		const std::string m_diffString = "diffuseTexture[";
 		const std::string m_diffColorString = "diffuseColor[";