- Ifc: first attempt at also supporting extruded area solids for boolean differentiation.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1315 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/6/merge
aramis_acg 2012-10-21 16:58:22 +00:00
parent 71fb04849c
commit d660ec35ad
1 changed files with 170 additions and 111 deletions

View File

@ -1462,7 +1462,7 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
IfcVector2 vpmin,vpmax;
MinMaxChooser<IfcVector2>()(vpmin,vpmax);
// the opening meshes are real 3D meshes so skip over all faces
// The opening meshes are real 3D meshes so skip over all faces
// clearly facing into the wrong direction.
std::vector<IfcVector2> contour;
for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) {
@ -1500,7 +1500,7 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
BoundingBox bb = BoundingBox(vpmin,vpmax);
std::vector<TempOpening*> joined_openings(1, &opening);
// see if this BB intersects any other, in which case we could not use the Quadrify()
// See if this BB intersects any other, in which case we could not use the Quadrify()
// algorithm and would revert to Poly2Tri only.
for (std::vector<BoundingBox>::iterator it = bbs.begin(); it != bbs.end();) {
const BoundingBox& ibb = *it;
@ -1508,7 +1508,7 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
if (ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x &&
ibb.first.y <= bb.second.y && ibb.second.y >= bb.second.x) {
// take these two contours and try to merge them. If they overlap (which
// Take these two contours and try to merge them. If they overlap (which
// should not happen, but in fact happens-in-the-real-world [tm] ),
// resume using a single contour and a single bounding box.
const std::vector<IfcVector2>& other = contours[std::distance(bbs.begin(),it)];
@ -1584,11 +1584,12 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
CleanupWindowContours(contours);
InsertWindowContours(bbs,contours,openings, minv,curmesh);
// this should connect the window openings on both sides of the wall,
// This should connect the window openings on both sides of the wall,
// but it produces lots of artifacts which are not resolved yet.
// Most of all, it makes all cases in which adjacent openings are
// not correctly merged together glaringly obvious.
//CloseWindows(contours, minv, contours_to_openings, curmesh);
// CloseWindows(contours, minv, contours_to_openings, curmesh);
return true;
}
@ -1719,30 +1720,20 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
IFCImporter::LogDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)");
}
// ------------------------------------------------------------------------------------------------
void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
ConversionData& conv)
{
if(const IfcExtrudedAreaSolid* const solid = swept.ToPtr<IfcExtrudedAreaSolid>()) {
// Do we just collect openings for a parent element (i.e. a wall)?
// In this case we don't extrude the surface yet, just keep the profile and transform it correctly
// In such a case, we generate the polygonal extrusion mesh as usual,
// but attach it to a TempOpening instance which will later be applied
// to the wall it pertains to.
if(conv.collect_openings) {
boost::shared_ptr<TempMesh> meshtmp(new TempMesh());
ProcessExtrudedAreaSolid(*solid,*meshtmp,conv);
/*
ProcessProfile(swept.SweptArea,*meshtmp,conv);
IfcMatrix4 m;
ConvertAxisPlacement(m,solid->Position);
meshtmp->Transform(m);
IfcVector3 dir;
ConvertDirection(dir,solid->ExtrudedDirection); */
conv.collect_openings->push_back(TempOpening(solid,IfcVector3(0,0,0)
/* IfcMatrix3(m) * (dir*static_cast<IfcFloat>(solid->Depth)) */,meshtmp));
conv.collect_openings->push_back(TempOpening(solid,IfcVector3(0,0,0),meshtmp));
return;
}
@ -1756,7 +1747,6 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
}
}
// ------------------------------------------------------------------------------------------------
enum Intersect {
Intersect_No,
@ -1785,127 +1775,196 @@ Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const I
return Intersect_Yes;
}
// ------------------------------------------------------------------------------------------------
void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
const TempMesh& first_operand,
ConversionData& conv)
{
ai_assert(hs != NULL);
const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
if(!plane) {
IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
return;
}
// extract plane base position vector and normal vector
IfcVector3 p,n(0.f,0.f,1.f);
if (plane->Position->Axis) {
ConvertDirection(n,plane->Position->Axis.Get());
}
ConvertCartesianPoint(p,plane->Position->Location);
if(!IsTrue(hs->AgreementFlag)) {
n *= -1.f;
}
// clip the current contents of `meshout` against the plane we obtained from the second operand
const std::vector<IfcVector3>& in = first_operand.verts;
std::vector<IfcVector3>& outvert = result.verts;
std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(),
end = first_operand.vertcnt.end(), iit;
outvert.reserve(in.size());
result.vertcnt.reserve(first_operand.vertcnt.size());
unsigned int vidx = 0;
for(iit = begin; iit != end; vidx += *iit++) {
unsigned int newcount = 0;
for(unsigned int i = 0; i < *iit; ++i) {
const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
// does the next segment intersect the plane?
IfcVector3 isectpos;
const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
if ( (e0-p).Normalize()*n > 0 ) {
outvert.push_back(e0);
++newcount;
}
}
else if (isect == Intersect_Yes) {
if ( (e0-p).Normalize()*n > 0 ) {
// e0 is on the right side, so keep it
outvert.push_back(e0);
outvert.push_back(isectpos);
newcount += 2;
}
else {
// e0 is on the wrong side, so drop it and keep e1 instead
outvert.push_back(isectpos);
++newcount;
}
}
}
if (!newcount) {
continue;
}
IfcVector3 vmin,vmax;
ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
// filter our IfcFloat points - those may happen if a point lies
// directly on the intersection line. However, due to IfcFloat
// precision a bitwise comparison is not feasible to detect
// this case.
const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
FuzzyVectorCompare fz(epsilon);
std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
if (e != outvert.end()) {
newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
outvert.erase(e,outvert.end());
}
if (fz(*( outvert.end()-newcount),outvert.back())) {
outvert.pop_back();
--newcount;
}
if(newcount > 2) {
result.vertcnt.push_back(newcount);
}
else while(newcount-->0) {
result.verts.pop_back();
}
}
IFCImporter::LogDebug("generating CSG geometry by plane clipping (IfcBooleanClippingResult)");
}
// ------------------------------------------------------------------------------------------------
void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, TempMesh& result,
const TempMesh& first_operand,
ConversionData& conv)
{
ai_assert(as != NULL);
// This case is handled by reduction to an instance of the quadrify() algorithm.
// Obviously, this won't work for arbitrarily complex cases. In fact, the first
// operand should be near-planar. Luckily, this is usually the case in Ifc
// buildings.
boost::shared_ptr<TempMesh> meshtmp(new TempMesh());
ProcessExtrudedAreaSolid(*as,*meshtmp,conv);
std::vector<TempOpening> openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp));
result = first_operand;
TempMesh temp;
std::vector<IfcVector3>::const_iterator vit = first_operand.verts.begin();
BOOST_FOREACH(unsigned int pcount, first_operand.vertcnt) {
temp.Clear();
temp.verts.insert(temp.verts.end(), vit, vit + pcount);
temp.vertcnt.push_back(pcount);
TryAddOpenings_Quadrulate(openings, std::vector<IfcVector3>(1,IfcVector3(1,0,0)), temp);
result.Append(temp);
vit += pcount;
}
IFCImporter::LogDebug("generating CSG geometry by geometric difference to a solid (IfcExtrudedAreaSolid)");
}
// ------------------------------------------------------------------------------------------------
void ProcessBoolean(const IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv)
{
// supported CSG operations:
// DIFFERENCE
if(const IfcBooleanResult* const clip = boolean.ToPtr<IfcBooleanResult>()) {
if(clip->Operator != "DIFFERENCE") {
IFCImporter::LogWarn("encountered unsupported boolean operator: " + (std::string)clip->Operator);
return;
}
TempMesh meshout;
// supported cases (1st operand):
// IfcBooleanResult -- call ProcessBoolean recursively
// IfcSweptAreaSolid -- obtain polygonal geometry first
// supported cases (2nd operand):
// IfcHalfSpaceSolid -- easy, clip against plane
// IfcExtrudedAreaSolid -- reduce to an instance of the quadrify() algorithm
const IfcHalfSpaceSolid* const hs = clip->SecondOperand->ResolveSelectPtr<IfcHalfSpaceSolid>(conv.db);
if(!hs) {
IFCImporter::LogError("expected IfcHalfSpaceSolid as second clipping operand");
return;
}
const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
if(!plane) {
IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
const IfcExtrudedAreaSolid* const as = clip->SecondOperand->ResolveSelectPtr<IfcExtrudedAreaSolid>(conv.db);
if(!hs && !as) {
IFCImporter::LogError("expected IfcHalfSpaceSolid or IfcExtrudedAreaSolid as second clipping operand");
return;
}
TempMesh first_operand;
if(const IfcBooleanResult* const op0 = clip->FirstOperand->ResolveSelectPtr<IfcBooleanResult>(conv.db)) {
ProcessBoolean(*op0,meshout,conv);
ProcessBoolean(*op0,first_operand,conv);
}
else if (const IfcSweptAreaSolid* const swept = clip->FirstOperand->ResolveSelectPtr<IfcSweptAreaSolid>(conv.db)) {
ProcessSweptAreaSolid(*swept,meshout,conv);
ProcessSweptAreaSolid(*swept,first_operand,conv);
}
else {
IFCImporter::LogError("expected IfcSweptAreaSolid or IfcBooleanResult as first clipping operand");
return;
}
// extract plane base position vector and normal vector
IfcVector3 p,n(0.f,0.f,1.f);
if (plane->Position->Axis) {
ConvertDirection(n,plane->Position->Axis.Get());
if(hs) {
ProcessBooleanHalfSpaceDifference(hs, result, first_operand, conv);
}
ConvertCartesianPoint(p,plane->Position->Location);
if(!IsTrue(hs->AgreementFlag)) {
n *= -1.f;
else {
ProcessBooleanExtrudedAreaSolidDifference(as, result, first_operand, conv);
}
// clip the current contents of `meshout` against the plane we obtained from the second operand
const std::vector<IfcVector3>& in = meshout.verts;
std::vector<IfcVector3>& outvert = result.verts;
std::vector<unsigned int>::const_iterator begin=meshout.vertcnt.begin(), end=meshout.vertcnt.end(), iit;
outvert.reserve(in.size());
result.vertcnt.reserve(meshout.vertcnt.size());
unsigned int vidx = 0;
for(iit = begin; iit != end; vidx += *iit++) {
unsigned int newcount = 0;
for(unsigned int i = 0; i < *iit; ++i) {
const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
// does the next segment intersect the plane?
IfcVector3 isectpos;
const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
if ( (e0-p).Normalize()*n > 0 ) {
outvert.push_back(e0);
++newcount;
}
}
else if (isect == Intersect_Yes) {
if ( (e0-p).Normalize()*n > 0 ) {
// e0 is on the right side, so keep it
outvert.push_back(e0);
outvert.push_back(isectpos);
newcount += 2;
}
else {
// e0 is on the wrong side, so drop it and keep e1 instead
outvert.push_back(isectpos);
++newcount;
}
}
}
if (!newcount) {
continue;
}
IfcVector3 vmin,vmax;
ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
// filter our IfcFloat points - those may happen if a point lies
// directly on the intersection line. However, due to IfcFloat
// precision a bitwise comparison is not feasible to detect
// this case.
const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
FuzzyVectorCompare fz(epsilon);
std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
if (e != outvert.end()) {
newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
outvert.erase(e,outvert.end());
}
if (fz(*( outvert.end()-newcount),outvert.back())) {
outvert.pop_back();
--newcount;
}
if(newcount > 2) {
result.vertcnt.push_back(newcount);
}
else while(newcount-->0)result.verts.pop_back();
}
IFCImporter::LogDebug("generating CSG geometry by plane clipping (IfcBooleanClippingResult)");
}
else {
IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is " + boolean.GetClassName());
}
}
// ------------------------------------------------------------------------------------------------
bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned int>& mesh_indices,
ConversionData& conv)