- Ifc: rework geometry generation for openings to use 2D profiles for approximate boolean differentiation whenever possible. Also fix issues in 2D projection, which caused very spurious triangle artifacts.

pull/17/head
Alexander Gessler 2013-01-27 21:21:30 +01:00
parent 2e6ad884bb
commit 250ca6837f
3 changed files with 146 additions and 54 deletions

View File

@ -71,7 +71,8 @@ namespace Assimp {
const std::vector<IfcVector3>& 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<IfcVector3>& 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<TempOpening>& 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<IfcVector2>& out_contour, const TempMesh& in_mesh,
IfcFloat& out_base_d, bool &ok)
bool &ok, IfcVector3& nor_out)
{
const std::vector<IfcVector3>& 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<IfcVector2>& 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<IfcVector2>()(vmin, vmax);
IfcVector3 vmin, vmax;
MinMaxChooser<IfcVector3>()(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<IfcVector2>& 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<IfcVector2>& 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<IfcVector2>& 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<TempOpening>& openings,
const std::vector<IfcVector3>& nors,
TempMesh& curmesh,
bool check_intersection,
bool generate_connection_geometry)
bool generate_connection_geometry,
const IfcVector3& wall_extrusion_axis)
{
std::vector<IfcVector3>& out = curmesh.verts;
OpeningRefVector contours_to_openings;
@ -1940,16 +1946,13 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
bool ok = true;
std::vector<IfcVector2> 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<TempOpening>& openings,
std::vector<IfcVector2> temp_contour;
std::vector<IfcVector2> 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<IfcVector3> profile_verts = opening.profileMesh->verts;
std::vector<unsigned int> 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<IfcVector3> profile_verts = profile_data->verts;
std::vector<unsigned int> profile_vertcnts = profile_data->vertcnt;
if(profile_verts.size() <= 2) {
continue;
}
@ -1993,17 +2032,21 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
MinMaxChooser<IfcVector2>()(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<TempOpening>& 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<TempOpening>& 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<TempOpening>& 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<TempOpening>& 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<IfcVector3>()(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<IfcVector3> 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<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh());
profile->Swap(result);
boost::shared_ptr<TempMesh> profile2D = boost::shared_ptr<TempMesh>(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<IfcExtrudedAreaSolid>()) {
ProcessExtrudedAreaSolid(*solid,meshout,conv);
ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings);
}
else if(const IfcRevolvedAreaSolid* const rev = swept.ToPtr<IfcRevolvedAreaSolid>()) {
ProcessRevolvedAreaSolid(*rev,meshout,conv);
@ -2485,9 +2550,9 @@ void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, T
// buildings.
boost::shared_ptr<TempMesh> meshtmp(new TempMesh());
ProcessExtrudedAreaSolid(*as,*meshtmp,conv);
ProcessExtrudedAreaSolid(*as,*meshtmp,conv,false);
std::vector<TempOpening> openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp));
std::vector<TempOpening> openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp,boost::shared_ptr<TempMesh>(NULL)));
result = first_operand;
@ -2512,7 +2577,7 @@ void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, T
continue;
}
GenerateOpenings(openings, std::vector<IfcVector3>(1,IfcVector3(1,0,0)), temp);
GenerateOpenings(openings, std::vector<IfcVector3>(1,IfcVector3(1,0,0)), temp, false, true);
result.Append(temp);
vit += pcount;
@ -2621,18 +2686,28 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned
return false;
}
meshtmp->RemoveAdjacentDuplicates();
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<IfcSolidModel>(),IfcVector3(0,0,0),meshtmp));
conv.collect_openings->push_back(TempOpening(geo.ToPtr<IfcSolidModel>(),
IfcVector3(0,0,0),
meshtmp,
boost::shared_ptr<TempMesh>(NULL)));
return true;
}
meshtmp->RemoveAdjacentDuplicates();
meshtmp->RemoveDegenerates();
if(fix_orientation) {
meshtmp->FixupFaceOrientation();
}

View File

@ -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)
{

View File

@ -78,7 +78,9 @@ struct TempOpening
{
const IFC::IfcSolidModel* solid;
IfcVector3 extrusionDir;
boost::shared_ptr<TempMesh> profileMesh;
boost::shared_ptr<TempMesh> 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<TempMesh> profileMesh)
TempOpening(const IFC::IfcSolidModel* solid,IfcVector3 extrusionDir,
boost::shared_ptr<TempMesh> profileMesh,
boost::shared_ptr<TempMesh> profileMesh2D)
: solid(solid)
, extrusionDir(extrusionDir)
, profileMesh(profileMesh)
, profileMesh2D(profileMesh2D)
{
}
@ -196,6 +201,8 @@ struct TempMesh
void ComputePolygonNormals(std::vector<IfcVector3>& normals,
bool normalize = true,
size_t ofs = 0) const;
void Swap(TempMesh& other);
};