diff --git a/code/IFCGeometry.cpp b/code/IFCGeometry.cpp index 767b79353..0f39c05fe 100644 --- a/code/IFCGeometry.cpp +++ b/code/IFCGeometry.cpp @@ -71,7 +71,8 @@ namespace Assimp { const std::vector& nors, TempMesh& curmesh, bool check_intersection = true, - bool generate_connection_geometry = true); + bool generate_connection_geometry = true, + const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0)); // ------------------------------------------------------------------------------------------------ @@ -478,13 +479,14 @@ void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, Conv } // ------------------------------------------------------------------------------------------------ -IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcFloat* d = NULL) +IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut) { const std::vector& out = curmesh.verts; IfcMatrix3 m; ok = true; + // The input "mesh" must be a single polygon const size_t s = out.size(); assert(curmesh.vertcnt.size() == 1 && curmesh.vertcnt.back() == s); @@ -497,9 +499,9 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcFloa // axis for the 2D coordinate space on the polygon plane, exploiting the // fact that the input polygon is nearly always a quad. bool done = false; - size_t base = s-curmesh.vertcnt.back(), i, j; - for (i = base; !done && i < s-1; !done && ++i) { - for (j = i+1; j < s; ++j) { + size_t base = 0, i, j; + for (i = 0; !done && i < s-2; done || ++i) { + for (j = i+1; j < s-1; ++j) { nor = -((out[i]-any_point)^(out[j]-any_point)); if(fabs(nor.Length()) > 1e-8f) { done = true; @@ -514,13 +516,14 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcFloa } nor.Normalize(); + norOut = nor; IfcVector3 r = (out[i]-any_point); r.Normalize(); - if(d) { - *d = -any_point * nor; - } + //if(d) { + // *d = -any_point * nor; + //} // Reconstruct orthonormal basis // XXX use Gram Schmidt for increased robustness @@ -554,13 +557,14 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: // Try to derive a solid base plane within the current surface for use as // working coordinate system. bool ok; - const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh, ok); + IfcVector3 nor; + const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh, ok, nor); if (!ok) { return false; } const IfcMatrix3 minv = IfcMatrix3(m).Inverse(); - const IfcVector3& nor = IfcVector3(m.c1, m.c2, m.c3); + IfcFloat coord = -1; @@ -1779,7 +1783,7 @@ size_t CloseWindows(ContourVector& contours, } BOOST_FOREACH(TempOpening* opening, refs) { - opening->wallPoints.clear(); + //opening->wallPoints.clear(); } } @@ -1842,12 +1846,12 @@ void Quadrify(const ContourVector& contours, TempMesh& curmesh) // ------------------------------------------------------------------------------------------------ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh& in_mesh, - IfcFloat& out_base_d, bool &ok) + bool &ok, IfcVector3& nor_out) { const std::vector& in_verts = in_mesh.verts; ok = true; - IfcMatrix4 m = IfcMatrix4(DerivePlaneCoordinateSpace(in_mesh, ok, &out_base_d)); + IfcMatrix4 m = IfcMatrix4(DerivePlaneCoordinateSpace(in_mesh, ok, nor_out)); if(!ok) { return IfcMatrix4(); } @@ -1856,11 +1860,12 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh ai_assert(fabs(det-1) < 1e-5); #endif - IfcFloat coord = 0; + IfcFloat zcoord = 0; out_contour.reserve(in_verts.size()); - IfcVector2 vmin, vmax; - MinMaxChooser()(vmin, vmax); + + IfcVector3 vmin, vmax; + MinMaxChooser()(vmin, vmax); // Project all points into the new coordinate system, collect min/max verts on the way BOOST_FOREACH(const IfcVector3& x, in_verts) { @@ -1874,14 +1879,14 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh // if(coord != -1.0f) { // assert(fabs(coord - vv.z) < 1e-3f); // } - coord += vv.z; - vmin = std::min(IfcVector2(vv.x, vv.y), vmin); - vmax = std::max(IfcVector2(vv.x, vv.y), vmax); + zcoord += vv.z; + vmin = std::min(vv, vmin); + vmax = std::max(vv, vmax); out_contour.push_back(IfcVector2(vv.x,vv.y)); } - coord /= in_verts.size(); + zcoord /= in_verts.size(); // Further improve the projection by mapping the entire working set into // [0,1] range. This gives us a consistent data range so all epsilons @@ -1902,7 +1907,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh mult.a4 = -vmin.x * mult.a1; mult.b4 = -vmin.y * mult.b2; - mult.c4 = -coord; + mult.c4 = -zcoord; m = mult * m; // debug code to verify correctness @@ -1912,7 +1917,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh const IfcVector3& vv = m * x; out_contour2.push_back(IfcVector2(vv.x,vv.y)); - ai_assert(fabs(vv.z) < 1e-5); + ai_assert(fabs(vv.z) < vmax.z + 1e-8); } for(size_t i = 0; i < out_contour.size(); ++i) { @@ -1928,7 +1933,8 @@ bool GenerateOpenings(std::vector& openings, const std::vector& nors, TempMesh& curmesh, bool check_intersection, - bool generate_connection_geometry) + bool generate_connection_geometry, + const IfcVector3& wall_extrusion_axis) { std::vector& out = curmesh.verts; OpeningRefVector contours_to_openings; @@ -1940,16 +1946,13 @@ bool GenerateOpenings(std::vector& openings, bool ok = true; std::vector contour_flat; - IfcFloat base_d; - const IfcMatrix4& m = ProjectOntoPlane(contour_flat, curmesh, base_d, ok); + IfcVector3 nor; + const IfcMatrix4& m = ProjectOntoPlane(contour_flat, curmesh, ok, nor); if(!ok) { return false; } - IfcVector3 nor = IfcVector3(m.c1, m.c2, m.c3); - nor.Normalize(); - // Obtain inverse transform for getting back to world space later on const IfcMatrix4 minv = IfcMatrix4(m).Inverse(); @@ -1959,10 +1962,46 @@ bool GenerateOpenings(std::vector& openings, std::vector temp_contour; std::vector temp_contour2; + IfcVector3 wall_extrusion_axis_norm = wall_extrusion_axis; + wall_extrusion_axis_norm.Normalize(); + size_t c = 0; BOOST_FOREACH(TempOpening& opening,openings) { - std::vector profile_verts = opening.profileMesh->verts; - std::vector profile_vertcnts = opening.profileMesh->vertcnt; + + // extrusionDir may be 0,0,0 on case where the opening mesh is not an + // IfcExtrudedAreaSolid but something else (i.e. a brep) + IfcVector3 norm_extrusion_dir = opening.extrusionDir; + if (norm_extrusion_dir.SquareLength() > 1e-10) { + norm_extrusion_dir.Normalize(); + } + else { + norm_extrusion_dir = IfcVector3(); + } + + TempMesh* profile_data = opening.profileMesh; + bool is_2d_source = false; + if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) { + + if(fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) { + // horizontal extrusion + if (fabs(norm_extrusion_dir * nor) > 0.9) { + profile_data = opening.profileMesh2D; + is_2d_source = true; + } + else { + //continue; + } + } + else { + // vertical extrusion + if (fabs(norm_extrusion_dir * nor) > 0.9) { + continue; + } + continue; + } + } + std::vector profile_verts = profile_data->verts; + std::vector profile_vertcnts = profile_data->vertcnt; if(profile_verts.size() <= 2) { continue; } @@ -1993,17 +2032,21 @@ bool GenerateOpenings(std::vector& openings, MinMaxChooser()(vpmin2,vpmax2); for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) { - const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ - (profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize(); - const IfcFloat abs_dot_face_nor = abs(nor * face_nor); - if (abs_dot_face_nor < 0.9) { - vi_total += profile_vertcnts[f]; - continue; + bool side_flag = true; + if (!is_2d_source) { + const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ + (profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize(); + + const IfcFloat abs_dot_face_nor = abs(nor * face_nor); + if (abs_dot_face_nor < 0.9) { + vi_total += profile_vertcnts[f]; + continue; + } + + side_flag = nor * face_nor > 0; } - const bool side_flag = nor * face_nor > 0; - for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) { const IfcVector3& x = profile_verts[vi_total]; @@ -2037,6 +2080,7 @@ bool GenerateOpenings(std::vector& openings, } if (temp_contour2.size() > 2) { + ai_assert(!is_2d_source); const IfcVector2 area = vpmax-vpmin; const IfcVector2 area2 = vpmax2-vpmin2; if (temp_contour.size() <= 2 || fabs(area2.x * area2.y) > fabs(area.x * area.y)) { @@ -2052,7 +2096,7 @@ bool GenerateOpenings(std::vector& openings, // TODO: This epsilon may be too large const IfcFloat epsilon = fabs(dmax-dmin) * 0.0001; - if (check_intersection && (0 < dmin-epsilon || 0 > dmax+epsilon)) { + if (!is_2d_source && check_intersection && (0 < dmin-epsilon || 0 > dmax+epsilon)) { continue; } @@ -2184,7 +2228,6 @@ bool GenerateOpenings(std::vector& openings, // Generate window caps to connect the symmetric openings on both sides // of the wall. if (generate_connection_geometry) { - CloseWindows(contours, minv, contours_to_openings, curmesh); } return true; @@ -2193,7 +2236,7 @@ bool GenerateOpenings(std::vector& openings, // ------------------------------------------------------------------------------------------------ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& result, - ConversionData& conv) + ConversionData& conv, bool collect_openings) { TempMesh meshout; @@ -2220,7 +2263,7 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul const bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2; if(solid.Depth < 1e-6) { if(has_area) { - meshout = result; + result = meshout; } return; } @@ -2231,14 +2274,22 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul // First step: transform all vertices into the target coordinate space IfcMatrix4 trafo; ConvertAxisPlacement(trafo, solid.Position); + + IfcVector3 vmin, vmax; + MinMaxChooser()(vmin, vmax); BOOST_FOREACH(IfcVector3& v,in) { v *= trafo; + + vmin = std::min(vmin, v); + vmax = std::max(vmax, v); } + + vmax -= vmin; + const IfcFloat diag = vmax.Length(); IfcVector3 min = in[0]; dir *= IfcMatrix3(trafo); - std::vector nors; const bool openings = !!conv.apply_openings && conv.apply_openings->size(); @@ -2284,7 +2335,7 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul out.push_back(in[next]); if(openings) { - if(GenerateOpenings(*conv.apply_openings,nors,temp,false, true)) { + if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) { ++sides_with_openings; } @@ -2312,7 +2363,7 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul curmesh.vertcnt.push_back(size); if(openings && size > 2) { - if(GenerateOpenings(*conv.apply_openings,nors,temp,true, true)) { + if(GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) { ++sides_with_v_openings; } @@ -2327,6 +2378,20 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul } IFCImporter::LogDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)"); + + // If this is an opening element, store both the extruded mesh and the 2D profile mesh + // it was created from. Return an empty mesh to the caller. + if(collect_openings && !result.IsEmpty()) { + ai_assert(conv.collect_openings); + boost::shared_ptr profile = boost::shared_ptr(new TempMesh()); + profile->Swap(result); + + boost::shared_ptr profile2D = boost::shared_ptr(new TempMesh()); + profile2D->Swap(meshout); + conv.collect_openings->push_back(TempOpening(&solid,dir,profile, profile2D)); + + ai_assert(result.IsEmpty()); + } } // ------------------------------------------------------------------------------------------------ @@ -2334,7 +2399,7 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, ConversionData& conv) { if(const IfcExtrudedAreaSolid* const solid = swept.ToPtr()) { - ProcessExtrudedAreaSolid(*solid,meshout,conv); + ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings); } else if(const IfcRevolvedAreaSolid* const rev = swept.ToPtr()) { ProcessRevolvedAreaSolid(*rev,meshout,conv); @@ -2485,9 +2550,9 @@ void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, T // buildings. boost::shared_ptr meshtmp(new TempMesh()); - ProcessExtrudedAreaSolid(*as,*meshtmp,conv); + ProcessExtrudedAreaSolid(*as,*meshtmp,conv,false); - std::vector openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp)); + std::vector openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp,boost::shared_ptr(NULL))); result = first_operand; @@ -2512,7 +2577,7 @@ void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, T continue; } - GenerateOpenings(openings, std::vector(1,IfcVector3(1,0,0)), temp); + GenerateOpenings(openings, std::vector(1,IfcVector3(1,0,0)), temp, false, true); result.Append(temp); vit += pcount; @@ -2621,18 +2686,28 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vectorRemoveAdjacentDuplicates(); - meshtmp->RemoveDegenerates(); + if (meshtmp->IsEmpty()) { + return false; + } // Do we just collect openings for a parent element (i.e. a wall)? - // In such a case, we generate the polygonal extrusion mesh as usual, + // In such a case, we generate the polygonal mesh as usual, // but attach it to a TempOpening instance which will later be applied // to the wall it pertains to. + + // Note: swep area solids are added in ProcessExtrudedAreaSolid(), + // which returns an empty mesh. if(conv.collect_openings) { - conv.collect_openings->push_back(TempOpening(geo.ToPtr(),IfcVector3(0,0,0),meshtmp)); + conv.collect_openings->push_back(TempOpening(geo.ToPtr(), + IfcVector3(0,0,0), + meshtmp, + boost::shared_ptr(NULL))); return true; } + meshtmp->RemoveAdjacentDuplicates(); + meshtmp->RemoveDegenerates(); + if(fix_orientation) { meshtmp->FixupFaceOrientation(); } diff --git a/code/IFCUtil.cpp b/code/IFCUtil.cpp index 17149906f..e7a9e1b9c 100644 --- a/code/IFCUtil.cpp +++ b/code/IFCUtil.cpp @@ -59,6 +59,9 @@ void TempOpening::Transform(const IfcMatrix4& mat) if(profileMesh) { profileMesh->Transform(mat); } + if(profileMesh2D) { + profileMesh2D->Transform(mat); + } extrusionDir *= IfcMatrix3(mat); } @@ -311,6 +314,13 @@ void TempMesh::RemoveAdjacentDuplicates() } } +// ------------------------------------------------------------------------------------------------ +void TempMesh::Swap(TempMesh& other) +{ + vertcnt.swap(other.vertcnt); + verts.swap(other.verts); +} + // ------------------------------------------------------------------------------------------------ bool IsTrue(const EXPRESS::BOOLEAN& in) { diff --git a/code/IFCUtil.h b/code/IFCUtil.h index aa835f270..84e10872d 100644 --- a/code/IFCUtil.h +++ b/code/IFCUtil.h @@ -78,7 +78,9 @@ struct TempOpening { const IFC::IfcSolidModel* solid; IfcVector3 extrusionDir; + boost::shared_ptr profileMesh; + boost::shared_ptr profileMesh2D; // list of points generated for this opening. This is used to // create connections between two opposing holes created @@ -96,10 +98,13 @@ struct TempOpening } // ------------------------------------------------------------------------------ - TempOpening(const IFC::IfcSolidModel* solid,IfcVector3 extrusionDir,boost::shared_ptr profileMesh) + TempOpening(const IFC::IfcSolidModel* solid,IfcVector3 extrusionDir, + boost::shared_ptr profileMesh, + boost::shared_ptr profileMesh2D) : solid(solid) , extrusionDir(extrusionDir) , profileMesh(profileMesh) + , profileMesh2D(profileMesh2D) { } @@ -196,6 +201,8 @@ struct TempMesh void ComputePolygonNormals(std::vector& normals, bool normalize = true, size_t ofs = 0) const; + + void Swap(TempMesh& other); };