This repository has been archived on 2020-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Indoor/floorplan/3D/objects/OBJReader.h
2018-10-25 11:50:12 +02:00

229 lines
5.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* © Copyright 2014 Urheberrechtshinweis
* Alle Rechte vorbehalten / All Rights Reserved
*
* Programmcode ist urheberrechtlich geschuetzt.
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
* Keine Verwendung ohne explizite Genehmigung.
* (vgl. § 106 ff UrhG / § 97 UrhG)
*/
#ifndef OBJREADER_H
#define OBJREADER_H
#include <vector>
#include <string>
#include <fstream>
#include "../../../geo/Point2.h"
#include "../../../geo/Point3.h"
/**
* prase .obj files
*/
class OBJReader {
public:
/** group vertex+normal+texture */
struct VNT {
int idxVertex;
int idxNormal;
int idxTexture;
Point3 vertex;
Point3 normal;
Point2 texture;
};
/** one triangle */
struct Face {
VNT vnt[3];
Face(VNT v1, VNT v2, VNT v3) : vnt{v1,v2,v3} {;}
};
/** one object within the file */
struct Object {
std::string material;
std::string name;
std::vector<Face> faces;
};
/** internal data */
struct Data {
std::vector<Point3> vertices;
std::vector<Point2> texCoords;
std::vector<Point3> normals;
std::vector<std::string> materialFiles;
std::vector<Object> objects;
Object& curObj() {
if (objects.empty()) {objects.push_back(Object());}
return objects.back();
}
} data;
public:
/** ctor. use readXYZ() */
OBJReader() {
;
}
/** read .obj from the given file */
void readFile(const std::string& file) {
std::ifstream is(file);
std::string line;
while(getline(is, line)) {parseLine(line);}
is.close();
}
/** read obj from the given data string (.obj file contents) */
void readData(const char* data) {
readData(std::string(data));
}
/** read obj from the given data string (.obj file contents) */
void readData(const std::string& data) {
std::stringstream is(data);
std::string line;
while(getline(is, line)) {parseLine(line);}
}
/** get the parsed data */
const Data& getData() const {return data;}
private:
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
size_t end_pos = start_pos + from.length();
str.replace(start_pos, end_pos, to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
/** remove empty strings from the vector */
std::vector<std::string> nonEmpty(const std::vector<std::string>& src) {
std::vector<std::string> res;
for (const std::string& s : src) {
if (!s.empty()) {res.push_back(s);}
}
return res;
}
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
/** parse one line of the .obj file */
void parseLine(std::string line) {
if (line.length() < 2) {return;}
// remove other linebreaks
replaceAll(line, "\r", "");
const std::vector<std::string> tokens = nonEmpty(split(line, ' '));
const std::string token = tokens.front();
if ("mtllib" == token) {data.materialFiles.push_back(tokens[1]);}
if ("usemtl" == token) {data.curObj().material = tokens[1];}
if ("v" == token) {parseVertex(tokens);}
if ("vt" == token) {parseTexCoord(tokens);}
if ("vn" == token) {parseNormal(tokens);}
if ("f" == token) {parseFace(tokens);}
if ("g" == token) {newObject(tokens[1]);}
if ("o" == token) {newObject(tokens[1]);}
}
/** allocate a new object */
void newObject(const std::string& name) {
Object o;
o.name = name;
data.objects.push_back(o);
}
/** parse one vertex from the tokenizer */
void parseVertex(const std::vector<std::string>& t) {
const float x = std::stof(t[1]);
const float y = std::stof(t[2]);
const float z = std::stof(t[3]);
data.vertices.push_back(Point3(x,y,z));
}
/** parse one texture-coordinate from the tokenizer */
void parseTexCoord(const std::vector<std::string>& t) {
const float u = std::stof(t[1]);
const float v = std::stof(t[2]);
data.texCoords.push_back(Point2(u, -v));
}
/** parse one normal from the tokenizer */
void parseNormal(const std::vector<std::string>& t) {
const float x = std::stof(t[1]);
const float y = std::stof(t[2]);
const float z = std::stof(t[3]);
data.normals.push_back(Point3(x,y,z));
}
/** parse one face from the tokenizer */
void parseFace(const std::vector<std::string>& t) {
std::vector<VNT> indices;
int numVertices = 0;
for (size_t i = 1; i < t.size(); ++i) {
// one V/T/N
const std::string entry = t[i];
const std::vector<std::string> vtn = split(entry, '/');
++numVertices;
const std::string v = vtn[0];
const std::string vt = (vtn.size() > 1) ? (vtn[1]) : ("");
const std::string vn = (vtn.size() > 2) ? (vtn[2]) : ("");
//const std::string vt = t2.getToken('/', false);
//const std::string vn = t2.getToken('/', false);
// create a new vertex/normal/texture combination
VNT vnt;
vnt.idxVertex = (std::stoi(v) - 1);
vnt.idxNormal = (vn.empty()) ? (-1) : (std::stoi(vn) - 1);
vnt.idxTexture = (vt.empty()) ? (-1) : (std::stoi(vt) - 1);
if (vnt.idxVertex >= 0) {vnt.vertex = data.vertices[vnt.idxVertex];}
if (vnt.idxNormal >= 0) {vnt.normal = data.normals[vnt.idxNormal];}
if (vnt.idxTexture >= 0) {vnt.texture = data.texCoords[vnt.idxTexture];}
indices.push_back(vnt);
}
// this will both, create normal triangles and triangulate polygons
// see: http://www.mathopenref.com/polygontriangles.html
for (int i = 1; i < (int) indices.size()-1; ++i) {
Face face(indices[0], indices[1], indices[i+1]);
data.curObj().faces.push_back(face);
}
// sanity check
// if (numVertices != 3) {throw "this face is not a triangle!";}
}
};
#endif // OBJREADER_H