#ifndef MODELFACTORY_H #define MODELFACTORY_H #include "../../../floorplan/v2/Floorplan.h" #include "../../../geo/Triangle3.h" #include "ModelFactoryHelper.h" #include "Obstacle3.h" #include "Cube.h" /** * convert an indoor map into a 3D model based on triangles */ class ModelFactory { private: bool exportCeilings = true; bool exportObstacles = true; bool exportWallTops = false; const Floorplan::IndoorMap* map; public: /** ctor */ ModelFactory(const Floorplan::IndoorMap* map) : map(map) { } /** get all triangles grouped by obstacle */ std::vector triangulize() { std::vector res; // process each floor for (const Floorplan::Floor* f : map->floors) { // triangulize the floor itself (floor/ceiling) if (exportCeilings) {res.push_back(getTriangles(f));} // process each obstacle within the floor for (const Floorplan::FloorObstacle* fo : f->obstacles) { // handle line obstacles const Floorplan::FloorObstacleLine* fol = dynamic_cast(fo); if (fol) { if (fol->type == Floorplan::ObstacleType::HANDRAIL) {continue;} if (exportObstacles) {res.push_back(getTriangles(f, fol));} } } // TODO: remove //break; } return res; } /** DEBUG: convert to .obj file code for exporting */ std::string toOBJ() { const std::vector obs = triangulize(); int nVerts = 1; int nObjs = 0; std::string res; // write each obstacle for (const Obstacle3D& o : obs) { // write the vertices for (const Triangle3& t : o.triangles) { res += "v " + std::to_string(t.p1.x) + " " + std::to_string(t.p1.y) + " " + std::to_string(t.p1.z) + "\n"; res += "v " + std::to_string(t.p2.x) + " " + std::to_string(t.p2.y) + " " + std::to_string(t.p2.z) + "\n"; res += "v " + std::to_string(t.p3.x) + " " + std::to_string(t.p3.y) + " " + std::to_string(t.p3.z) + "\n"; } // create a new group res += "g elem_" + std::to_string(++nObjs) + "\n"; // write the group's faces for (size_t i = 0; i < o.triangles.size(); ++i) { res += "f " + std::to_string(nVerts+0) + " " + std::to_string(nVerts+1) + " " + std::to_string(nVerts+2) + "\n"; nVerts += 3; } } // done return res; } private: /** convert a floor (floor/ceiling) into triangles */ Obstacle3D getTriangles(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) // TODO: variable type? Obstacle3D res(Floorplan::Material::CONCRETE); Polygon poly; // append all "add" and "remove" areas for (Floorplan::FloorOutlinePolygon* fop : f->outline) { switch (fop->method) { case Floorplan::OutlineMethod::ADD: poly.add(fop->poly); break; case Floorplan::OutlineMethod::REMOVE: poly.remove(fop->poly); break; default: throw 1; } } // convert them into polygons std::vector> polys = poly.get(f->getStartingZ()); // convert polygons (GL_TRIANGLE_STRIP) to triangles for (const std::vector& 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 res.triangles.push_back(tria1); res.triangles.push_back(tria2); } } 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(fol->material); res.triangles = cube.getTriangles(); return res; } }; #endif // MODELFACTORY_H