/* Title : Duct Taped GL: Model Author: Edward R. Gonzalez Description: Allows the management of loading and buffer/rendering models from wavefront obj file sources. */ #pragma once // DGL #include "DGL_Buffers.hpp" #include "DGL_Types.hpp" #include "DGL_Space.hpp" // Non-Standard C++ #include "Cpp_Alias.hpp" namespace DGL { struct VertexGenerator { using ComponentList = std::vector< gFloat>; public: void AddComponent(gFloat _float) { comp.push_back(_float); } // TODO: De-hard-code this generator to do any vector size. Vector2 GetVector2() { return Vector2(comp.at(0), comp.at(1)); } Vector3 GetVector3() { return Vector3(comp.at(0), comp.at(1), comp.at(2)); } void Normalize() { using std::pow ; using std::sqrt; using Element = ComponentList::iterator; gFloat magnitude = 0.0f; for (Element element = comp.begin(); element != comp.end(); element++) { magnitude += pow(*element, 2.0f); } magnitude = sqrt(magnitude); for (Element element = comp.begin(); element != comp.end(); element++) { *element /= magnitude; } } private: ComponentList comp; }; struct Face { Vec3Int Vertexes; }; using FaceList = std::vector; struct FaceGenerator { using ComponentList = std::vector< gUInt>; ComponentList vertIndexes, uvIndexes, normals; void AddVertexIndex(gUInt _index) { vertIndexes.push_back(_index); } void AddUVIndex(gUInt _index) { uvIndexes.push_back(_index); } void AddNormalIndex(gUInt _index) { normals.push_back(_index); } Face GetFace() { Face generated;// = { {0,0,0}, {0,0,0}, {0,0,0} }; for (int index = 0; index < 3; index++) { generated.Vertexes.vec[index] = vertIndexes[index]; if (index < 2) { if (uvIndexes.size() > 0) { //generated.UVs[index] = uvIndexes[index]; } } if (normals.size() > 0) { //generated.Normals.vec[index] = normals[index]; // Not using built in normals for now... } } if (uvIndexes.size() == 0) { //generated.UVs = { 0, 0 }; } return generated; } }; // TODO: Add support for textures... class Model { private: // Not the most efficient method to do normals, but works. void generateNormals() { normals.resize(verticies.size(), Vector3(0.0f)); for (int faceIndex = 0; faceIndex < faces.size(); faceIndex++) { int vertexIndex1 = faces[faceIndex].Vertexes.vec[0], vertexIndex2 = faces[faceIndex].Vertexes.vec[1], vertexIndex3 = faces[faceIndex].Vertexes.vec[2] ; Vector3 vert1 = verticies[vertexIndex1], vert2 = verticies[vertexIndex2], vert3 = verticies[vertexIndex3] ; Vector3 edge1 = vert2 - vert1, edge2 = vert3 - vert1, normal = GetCrossNormal(edge1, edge2); normals[vertexIndex1] += normal; normals[vertexIndex2] += normal; normals[vertexIndex3] += normal; } for (int index = 0; index < normals.size(); index++) { normals[index] = GetDirection(normals[index]); } } public: Model(const string& _filePath) : loaded (false ), vertexArrayID (0 ), vertexBufferID (0 ), normalBuffferID(0 ), textureBufferID(0 ), elementBufferID(0 ), filePath (_filePath ), verticies (VertexList()), normals (VertexList()), textureUVs (UVList ()), faces (FaceList ()) {} // Hardcoded to only do the verticies and normals for now... void Buffer() { // Generate buffers GenerateVertexBuffers(vertexArrayID , 1); GenerateBuffers (vertexBufferID , 1); GenerateBuffers (normalBuffferID, 1); GenerateBuffers (elementBufferID, 1); if (normals.size() == 0) { generateNormals(); } BindVertexArray(vertexArrayID); // Vertex Position Buffering BindBuffer(EBufferTarget::VertexAttributes, vertexBufferID); BufferData(verticies[0], gSize(verticies.size() * sizeof(Vector3)), EBufferTarget::VertexAttributes, EBufferUsage::StaticDraw); FormatVertexAttributes(0, EDataType::Float, ZeroOffset(), 3, false); EnableVertexAttributeArray(0); // Normal Buffering if (normals.size() != 0) { BindBuffer(EBufferTarget::VertexAttributes, normalBuffferID); BufferData(normals[0], gSize(normals.size() * sizeof(Vector3)), EBufferTarget::VertexAttributes, EBufferUsage::StaticDraw); FormatVertexAttributes(1, EDataType::Float, ZeroOffset(), 3, false); EnableVertexAttributeArray(1); } // Texture buffering // TODO: Fix this. if (textureUVs.size() != 0) { //glBindTexture(TBO, GL_TEXTURE_2D); //BufferData(Address(TextureMap[0]), TextureMap.size() * sizeof(Vector2), EBufferTarget::TextureData, EBufferUsage::StaticDraw); //FormatVertexAttributes(2, EDataType::Float, ZeroOffset(), 2, EBool::False); //EnableVertexAttributeArray(2); } BindBuffer(EBufferTarget::VertexIndices, elementBufferID); BufferData(faces[0], gSize(faces.size() * sizeof(Face)), EBufferTarget::VertexIndices, EBufferUsage::StaticDraw); UnbindVertexArray(); // Unbind vertex array. } void Load() { using std::get ; using std::getline ; using std::ifstream ; using std::ios ; using std::stringstream; using std::ws ; ifstream fileBuffer; fileBuffer.open(filePath); auto processVertex = [&](stringstream& _vertexStream) { VertexGenerator vertex; gFloat componentValue; while (not _vertexStream.eof()) { _vertexStream >> componentValue >> ws; vertex.AddComponent(componentValue); } verticies.push_back(vertex.GetVector3()); }; auto processNormals = [&](stringstream& _normalStream) { VertexGenerator normal; gFloat componentValue; while (not _normalStream.eof()) { _normalStream >> componentValue >> ws; normal.AddComponent(componentValue); } normal.Normalize(); normals.push_back(normal.GetVector3()); }; auto processTexture = [&](stringstream& _normalStream) { VertexGenerator texture; gFloat componentValue; while (not _normalStream.eof()) { _normalStream >> componentValue >> ws; texture.AddComponent(componentValue); } textureUVs.push_back(texture.GetVector2()); }; auto processFace = [&](stringstream& _faceStream) { FaceGenerator faceMade; gUInt vertexIndex, textureIndex, normalIndex; while (not _faceStream.eof()) { _faceStream >> vertexIndex >> ws; faceMade.AddVertexIndex(vertexIndex - 1); indicies.push_back(vertexIndex - 1); if (_faceStream.peek() == '/') { _faceStream.get(); if (_faceStream.peek() == '/') { _faceStream.get(); _faceStream >> normalIndex >> ws; faceMade.AddNormalIndex(normalIndex -1); indicies.push_back(normalIndex - 1); } else { _faceStream >> textureIndex >> ws; faceMade.AddUVIndex(textureIndex - 1); if (_faceStream.peek() == '/') { _faceStream.get(); _faceStream >> normalIndex >> ws; faceMade.AddNormalIndex(normalIndex - 1); indicies.push_back(normalIndex - 1); } } } } faces.push_back(faceMade.GetFace()); }; if (fileBuffer.is_open()) { stringstream stringBuffer; stringBuffer << fileBuffer.rdbuf(); string line; while (not stringBuffer.eof()) { getline(stringBuffer, line); stringstream lineStream(line); string lineSig; lineStream >> lineSig >> ws; if (lineSig == "v") { processVertex(lineStream); } else if (lineSig == "vn") { processNormals(lineStream); } else if (lineSig == "vt") { processTexture(lineStream); } else if (lineSig == "f") { processFace(lineStream); } } fileBuffer.close(); Buffer(); loaded = true; return; } else { throw std::runtime_error("Could not open file to load model."); } } bool Ready() { return loaded; } void Render() { BindVertexArray(vertexArrayID); gInt SizeRef; GetBufferParameterIV(EBufferTarget::VertexIndices, EBufferParam::Size, SizeRef); SizeRef /= sizeof(gUInt); DrawElements(EPrimitives::Triangles, SizeRef, EDataType::UnsignedInt, ZeroOffset()); UnbindVertexArray(); } Model& operator= (const Model& _model) { vertexArrayID = _model.vertexArrayID ; vertexBufferID = _model.vertexBufferID ; normalBuffferID = _model.normalBuffferID; textureBufferID = _model.textureBufferID; elementBufferID = _model.elementBufferID; filePath = _model.filePath; verticies = _model.verticies ; normals = _model.normals ; textureUVs = _model.textureUVs; faces = _model.faces ; indicies = _model.indicies ; return *this; } private: // Components bool loaded; ID vertexArrayID ; ID vertexBufferID ; ID normalBuffferID; ID textureBufferID; ID elementBufferID; string filePath; VertexList verticies ; VertexList normals ; UVList textureUVs; FaceList faces ; VIndexList indicies ; }; };