moved from ray3 to floorplan/3D

worked on new wall models
refactoring
This commit is contained in:
2018-07-24 08:13:16 +02:00
parent 083a1c2cf2
commit 8dd1ba0be6
25 changed files with 703 additions and 92 deletions

View File

@@ -0,0 +1,130 @@
#ifndef MTLREADER_H
#define MTLREADER_H
#include <vector>
#include <unordered_map>
#include <string>
#include <fstream>
#include "../../../geo/Point2.h"
#include "../../../geo/Point3.h"
/**
* prase .mtl files
*/
class MTLReader {
public:
struct Material {
std::string textureFile = "";
Point3 diffuse = Point3(1,1,1);
float alpha = 1.0;
};
Material* cur = nullptr;
std::unordered_map<std::string, Material> map;
/** ctor. use readXYZ() */
MTLReader() {
;
}
/** 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 std::string& data) {
std::stringstream is(data);
std::string line;
while(getline(is, line)) {parseLine(line);}
}
/** get the given material */
const Material& getMaterial(const std::string& mat) const {
const auto& it = map.find(mat);
if (it == map.end()) {throw Exception("material not available");}
return it->second;
}
private:
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;
}
}
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;
}
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 leading "#"
while (line[0] == ' ' || line[0] == '\t') {
line.erase(line.begin());
}
// remove other linebreaks
replaceAll(line, "\r", "");
const std::vector<std::string> tokens = nonEmpty(split(line, ' '));
const std::string token = tokens.front();
if ("newmtl" == token) {
const std::string id = tokens[1];
map[id] = Material();
cur = &map[id];
} else if ("map_Ka" == token) {
const std::string texFile = tokens[1];
cur->textureFile = texFile;
} else if ("map_Kd" == token) {
const std::string texFile = tokens[1];
cur->textureFile = texFile;
} else if ("Kd" == token) {
cur->diffuse.x = std::stof(tokens[1]);
cur->diffuse.y = std::stof(tokens[2]);
cur->diffuse.z = std::stof(tokens[3]);
} else if ("d" == token) {
cur->alpha = std::stof(tokens[1]);
}
}
};
#endif // MTLREADER_H

View File

@@ -0,0 +1,135 @@
#ifndef FLOORPLAN_3D_OBJPOOL_H
#define FLOORPLAN_3D_OBJPOOL_H
#include <vector>
#include "../../../geo/Triangle3.h"
#include <unordered_map>
#include "OBJReader.h"
#include "../Obstacle3.h"
// LINUX ONLY
//#include <dirent.h>
//#include <stdio.h>
#include "../../../data/File.h"
#include "../../../misc/Debug.h"
namespace Floorplan3D {
/**
* load several named 3D models for quick re-use
*/
class OBJPool {
private:
static constexpr const char* name = "OBJ-Pool";
/** singleton */
OBJPool() {;}
bool initDone = false;
std::unordered_map<std::string, Obstacle3D> cache;
public:
/** singleton access */
static OBJPool& get() {
static OBJPool instance;
return instance;
}
/** init with *.obj data folder */
void init(const std::string& folder) {
scanFolder(folder);
initDone = true;
}
/** init with multiple *.obj data folder */
void init(const std::initializer_list<std::string>& folders) {
for (const std::string& folder : folders) {
try {
scanFolder(folder);
} catch (...) {;}
}
initDone = true;
}
/** get all triangles for the given object (if known) */
const Obstacle3D& getObject(const std::string& name) {
// ensure the cache is initialized
if (!initDone) {
throw Exception("OBJPool: not initialized. call init(folder) first");
}
static Obstacle3D empty;
// find the entry
const auto& it = cache.find(name);
if (it == cache.end()) {return empty;}
return it->second;
}
private:
/** scan the given folder for all *.obj files */
void scanFolder(const std::string& folder) {
FS::File d(folder);
if (!d.exists()) {
throw Exception("OBJPool: folder not found: " + folder);
}
for (const FS::File& f : d.listFiles()) {
std::string name = f.getFilename();
if (endsWith(name, ".obj")) {
//std::string name = entry.path().filename().string();
name = name.substr(0, name.length() - 4); // without extension
load(f.getPath(), name);
}
}
}
inline bool endsWith(std::string const & value, std::string const & ending) {
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
/** load the given .obj file into the cache */
void load(const std::string& absName, const std::string& name) {
OBJReader reader;
reader.readFile(absName);
//reader.readFile("/mnt/vm/paper/diss/code/IndoorMap/res/mdl/" + file + ".obj"); // todo
// create triangles
Obstacle3D obs;
for (const OBJReader::Object& obj : reader.getData().objects) {
for (const OBJReader::Face& face : obj.faces) {
const Triangle3 tria(face.vnt[0].vertex, face.vnt[1].vertex, face.vnt[2].vertex);
obs.triangles.push_back(tria);
}
}
Log::add(this->name, "loaded: " + absName + " [" + std::to_string(obs.triangles.size()) + " triangles]", true);
// store
cache[name] = obs;
}
};
}
#endif // FLOORPLAN_3D_OBJPOOL_H

View File

@@ -0,0 +1,213 @@
#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 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