218 lines
5.3 KiB
C++
218 lines
5.3 KiB
C++
#ifndef MODELFACTORY_H
|
|
#define MODELFACTORY_H
|
|
|
|
#include "../../../floorplan/v2/Floorplan.h"
|
|
#include "../../../geo/Triangle3.h"
|
|
#include "ModelFactoryHelper.h"
|
|
#include "Obstacle3.h"
|
|
#include "Cube.h"
|
|
#include "FloorplanMesh.h"
|
|
|
|
/**
|
|
* convert an indoor map into a 3D model based on triangles
|
|
*/
|
|
class ModelFactory {
|
|
|
|
private:
|
|
|
|
bool exportCeilings = true;
|
|
bool exportObstacles = true;
|
|
bool exportWallTops = false;
|
|
std::vector<Floorplan::Floor*> exportFloors;
|
|
|
|
const Floorplan::IndoorMap* map;
|
|
|
|
public:
|
|
|
|
|
|
/** ctor */
|
|
ModelFactory(const Floorplan::IndoorMap* map) : map(map) {
|
|
|
|
}
|
|
|
|
void setExportCeilings(bool exp) {
|
|
this->exportCeilings = exp;
|
|
}
|
|
|
|
/** limit to-be-exported floors */
|
|
void setFloors(const std::vector<Floorplan::Floor*> floors) {
|
|
this->exportFloors = floors;
|
|
}
|
|
|
|
/** convert floorplan to mesh */
|
|
FloorplanMesh getMesh() {
|
|
|
|
FloorplanMesh mesh;
|
|
mesh.elements = triangulize();
|
|
return mesh;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
|
|
/** get all triangles grouped by obstacle */
|
|
std::vector<Obstacle3D> triangulize() {
|
|
|
|
std::vector<Obstacle3D> res;
|
|
|
|
// get the to-be-exported floors (either "all" or "user defined")
|
|
const std::vector<Floorplan::Floor*>& floors = (exportFloors.empty()) ? (map->floors) : (exportFloors);
|
|
|
|
// process each floor
|
|
for (const Floorplan::Floor* f : floors) {
|
|
|
|
// triangulize the floor itself (floor/ceiling)
|
|
if (exportCeilings) {
|
|
std::vector<Obstacle3D> tmp = getFloor(f);
|
|
res.insert(res.end(), tmp.begin(), tmp.end());
|
|
}
|
|
|
|
// process each obstacle within the floor
|
|
for (const Floorplan::FloorObstacle* fo : f->obstacles) {
|
|
|
|
// handle line obstacles
|
|
const Floorplan::FloorObstacleLine* fol = dynamic_cast<const Floorplan::FloorObstacleLine*>(fo);
|
|
if (fol) {
|
|
if (fol->type == Floorplan::ObstacleType::HANDRAIL) {continue;}
|
|
if (exportObstacles) {res.push_back(getTriangles(f, fol));}
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: remove
|
|
//break;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
/** convert a floor (floor/ceiling) into triangles */
|
|
std::vector<Obstacle3D> getFloor(const Floorplan::Floor* f) {
|
|
|
|
// floor uses an outline based on "add" and "remove" areas
|
|
// we need to create the apropriate triangles to model the polygon
|
|
// including all holes (remove-areas)
|
|
|
|
// process all "add" regions by type
|
|
// [this allows for overlaps of the same type]
|
|
std::unordered_map<std::string, Polygon> types;
|
|
for (Floorplan::FloorOutlinePolygon* fop : f->outline) {
|
|
if (fop->method == Floorplan::OutlineMethod::ADD) {
|
|
if (fop->outdoor) {
|
|
types["outdoor"].add(fop->poly);
|
|
} else {
|
|
types["indoor"].add(fop->poly);
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove the "remove" regions from EVERY "add" region added within the previous step
|
|
for (Floorplan::FloorOutlinePolygon* fop : f->outline) {
|
|
if (fop->method == Floorplan::OutlineMethod::REMOVE) {
|
|
for (auto& it : types) {
|
|
it.second.remove(fop->poly);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<Obstacle3D> res;
|
|
|
|
// create an obstacle for each type (indoor, outdoor)
|
|
for (auto& it : types) {
|
|
|
|
// TODO: variable type?
|
|
Obstacle3D::Type type = (it.first == "indoor") ? (Obstacle3D::Type::GROUND_INDOOR) : (Obstacle3D::Type::GROUND_OUTDOOR);
|
|
Obstacle3D obs(type, Floorplan::Material::CONCRETE);
|
|
|
|
// convert them into polygons
|
|
std::vector<std::vector<Point3>> polys = it.second.get(f->getStartingZ());
|
|
|
|
// convert polygons (GL_TRIANGLE_STRIP) to triangles
|
|
for (const std::vector<Point3>& pts : polys) {
|
|
for (int i = 0; i < (int)pts.size() - 2; ++i) {
|
|
|
|
// floor must be double-sided for reflection to work with the correct normals
|
|
Triangle3 tria1 (pts[i+0], pts[i+1], pts[i+2]);
|
|
Triangle3 tria2 (pts[i+2], pts[i+1], pts[i+0]);
|
|
|
|
// ensure the triangle with the normal pointing downwards (towards bulding's cellar)
|
|
// is below the triangle that points upwards (towards the sky)
|
|
if (tria1.getNormal().z < 0) {tria1 = tria1 - Point3(0,0,0.02);}
|
|
if (tria2.getNormal().z < 0) {tria2 = tria2 - Point3(0,0,0.02);}
|
|
|
|
// add both
|
|
obs.triangles.push_back(tria1);
|
|
obs.triangles.push_back(tria2);
|
|
|
|
}
|
|
}
|
|
|
|
res.push_back(obs);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
/** convert a line obstacle to 3D triangles */
|
|
Obstacle3D getTriangles(const Floorplan::Floor* f, const Floorplan::FloorObstacleLine* fol) {
|
|
|
|
/*
|
|
Obstacle3D res(fol->material);
|
|
|
|
Point3 p1(fol->from.x, fol->from.y, f->getStartingZ());
|
|
Point3 p2(fol->to.x, fol->to.y, f->getStartingZ());
|
|
Point3 p3(fol->to.x, fol->to.y, f->getEndingZ());
|
|
Point3 p4(fol->from.x, fol->from.y, f->getEndingZ());
|
|
|
|
Triangle3 t1(p1,p2,p3);
|
|
Triangle3 t2(p1,p3,p4);
|
|
|
|
res.triangles.push_back(t1);
|
|
res.triangles.push_back(t2);
|
|
|
|
*/
|
|
|
|
const float thickness_m = fol->thickness_m;
|
|
const Point2 from = fol->from;
|
|
const Point2 to = fol->to;
|
|
const Point2 cen2 = (from+to)/2;
|
|
|
|
const float rad = std::atan2(to.y - from.y, to.x - from.x);
|
|
const float deg = rad * 180 / M_PI;
|
|
|
|
// cube's destination center
|
|
const Point3 pos(cen2.x, cen2.y, f->atHeight + f->height/2);
|
|
|
|
// div by 2.01 to prevent overlapps and z-fi
|
|
const float sx = from.getDistance(to) / 2.01f;
|
|
const float sy = thickness_m / 2.01f;
|
|
const float sz = f->height / 2.01f; // prevent overlaps
|
|
const Point3 size(sx, sy, sz);
|
|
const Point3 rot(0,0,deg);
|
|
|
|
// build
|
|
Cube cube(pos, size, rot);
|
|
|
|
// done
|
|
Obstacle3D res(Obstacle3D::Type::WALL, fol->material);
|
|
res.triangles = cube.getTriangles();
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
#endif // MODELFACTORY_H
|