Program Listing for File Object3D.cpp¶
↰ Return to documentation for file (gui/Demeter/Renderer/Object3D.cpp)
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include "logging/Logger.hpp"
#include "Object3D.hpp"
#include "Utils.hpp"
bool Object3D::HandleSubObject(std::istringstream &ss)
{
if (!meshIndices->empty()) {
std::unique_ptr<Mesh> m = std::make_unique<Mesh>();
if (
!m->Init(currentName, std::move(meshVertices), std::move(meshIndices))) {
Log::failed << "Failed to initialize mesh for object: " << currentName;
return false;
}
_meshArr.push_back(std::move(m));
// Reset for next mesh
meshVertices = std::make_unique<std::vector<Vertex>>();
meshIndices = std::make_unique<std::vector<unsigned int>>();
uniqueVertexMap.clear();
index = 0;
}
ss >> currentName;
return true;
}
void Object3D::HandleVertex(std::istringstream &ss)
{
glm::vec3 pos;
ss >> pos.x >> pos.y >> pos.z;
positions.push_back(pos);
}
void Object3D::HandleVertexTexture(std::istringstream &ss)
{
glm::vec2 uv;
ss >> uv.x >> uv.y;
uv.y = 1.0 - uv.y; // Flip Y for OpenGL
texCoords.push_back(uv);
}
void Object3D::HandleVertexNormal(std::istringstream &ss)
{
glm::vec3 norm;
ss >> norm.x >> norm.y >> norm.z;
normals.push_back(norm);
}
void Object3D::HandleFace(std::istringstream &ss)
{
std::string vertexStr;
std::vector<unsigned int> faceIndices;
while (ss >> vertexStr) {
if (!uniqueVertexMap.contains(vertexStr)) {
std::istringstream vss(vertexStr);
std::string v;
std::string t;
std::string n;
std::getline(vss, v, '/');
std::getline(vss, t, '/');
std::getline(vss, n, '/');
Vertex vert = {};
vert.position = positions[std::stoi(v) - 1];
if (!t.empty())
vert.texCoord = texCoords[std::stoi(t) - 1];
if (!n.empty())
vert.normal = normals[std::stoi(n) - 1];
meshVertices->push_back(vert);
uniqueVertexMap[vertexStr] = index++;
}
faceIndices.push_back(uniqueVertexMap[vertexStr]);
}
// Triangulate polygon (fan method)
for (size_t i = 1; i + 1 < faceIndices.size(); i++) {
meshIndices->push_back(faceIndices[0]);
meshIndices->push_back(faceIndices[i]);
meshIndices->push_back(faceIndices[i + 1]);
}
}
bool Object3D::ParseLine(const std::string &prefix, std::istringstream &ss)
{
switch (Hash(prefix.c_str())) {
case Hash("o"):
case Hash("g"):
return HandleSubObject(ss);
case Hash("v"):
HandleVertex(ss);
break;
case Hash("vt"):
HandleVertexTexture(ss);
break;
case Hash("vn"):
HandleVertexNormal(ss);
break;
case Hash("f"):
HandleFace(ss);
break;
default:
break; // Ignore other lines
}
return true;
}
bool Object3D::Init(const std::string &path)
{
std::ifstream file(path);
if (!file.is_open()) {
Log::failed << "Could not open OBJ file: " << path;
return false;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream ss(line);
std::string prefix;
ss >> prefix;
if (!ParseLine(prefix, ss)) {
Log::failed << "Failed to parse line in OBJ file: " << line;
return false;
}
}
// Final mesh (in case file doesn't end with new object)
if (!meshIndices->empty()) {
std::unique_ptr<Mesh> m = std::make_unique<Mesh>();
if (
!m->Init(currentName, std::move(meshVertices), std::move(meshIndices))) {
Log::failed << "Failed to initialize mesh for object: " << currentName;
return false;
}
_meshArr.push_back(std::move(m));
}
return true;
}
void Object3D::Draw(ShaderProgram &shader, Camera &camera) const
{
for (const std::unique_ptr<Mesh> &mesh: _meshArr)
mesh->Draw(shader, modelMatrix, camera);
}
void Object3D::SetTexture(size_t meshID, std::shared_ptr<Texture> t)
{
if (meshID < _meshArr.size())
_meshArr[meshID]->SetTexture(std::move(t));
}