vrshoot

view libs/assimp/IFCGeometry.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2010, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file IFCGeometry.cpp
42 * @brief Geometry conversion and synthesis for IFC
43 */
45 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
48 #include "IFCUtil.h"
49 #include "PolyTools.h"
50 #include "ProcessHelper.h"
52 #include "../contrib/poly2tri/poly2tri/poly2tri.h"
53 #include "../contrib/clipper/clipper.hpp"
55 #include <iterator>
57 namespace Assimp {
58 namespace IFC {
60 // ------------------------------------------------------------------------------------------------
61 bool ProcessPolyloop(const IfcPolyLoop& loop, TempMesh& meshout, ConversionData& /*conv*/)
62 {
63 size_t cnt = 0;
64 BOOST_FOREACH(const IfcCartesianPoint& c, loop.Polygon) {
65 IfcVector3 tmp;
66 ConvertCartesianPoint(tmp,c);
68 meshout.verts.push_back(tmp);
69 ++cnt;
70 }
72 meshout.vertcnt.push_back(cnt);
74 // zero- or one- vertex polyloops simply ignored
75 if (meshout.vertcnt.back() > 1) {
76 return true;
77 }
79 if (meshout.vertcnt.back()==1) {
80 meshout.vertcnt.pop_back();
81 meshout.verts.pop_back();
82 }
83 return false;
84 }
86 // ------------------------------------------------------------------------------------------------
87 void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t master_bounds = (size_t)-1)
88 {
89 // handle all trivial cases
90 if(inmesh.vertcnt.empty()) {
91 return;
92 }
93 if(inmesh.vertcnt.size() == 1) {
94 result.Append(inmesh);
95 return;
96 }
98 ai_assert(std::count(inmesh.vertcnt.begin(), inmesh.vertcnt.end(), 0) == 0);
100 typedef std::vector<unsigned int>::const_iterator face_iter;
102 face_iter begin = inmesh.vertcnt.begin(), end = inmesh.vertcnt.end(), iit;
103 std::vector<unsigned int>::const_iterator outer_polygon_it = end;
105 // major task here: given a list of nested polygon boundaries (one of which
106 // is the outer contour), reduce the triangulation task arising here to
107 // one that can be solved using the "quadrulation" algorithm which we use
108 // for pouring windows out of walls. The algorithm does not handle all
109 // cases but at least it is numerically stable and gives "nice" triangles.
111 // first compute normals for all polygons using Newell's algorithm
112 // do not normalize 'normals', we need the original length for computing the polygon area
113 std::vector<IfcVector3> normals;
114 inmesh.ComputePolygonNormals(normals,false);
116 // One of the polygons might be a IfcFaceOuterBound (in which case `master_bounds`
117 // is its index). Sadly we can't rely on it, the docs say 'At most one of the bounds
118 // shall be of the type IfcFaceOuterBound'
119 IfcFloat area_outer_polygon = 1e-10f;
120 if (master_bounds != (size_t)-1) {
121 ai_assert(master_bounds < inmesh.vertcnt.size());
122 outer_polygon_it = begin + master_bounds;
123 }
124 else {
125 for(iit = begin; iit != end; iit++) {
126 // find the polygon with the largest area and take it as the outer bound.
127 IfcVector3& n = normals[std::distance(begin,iit)];
128 const IfcFloat area = n.SquareLength();
129 if (area > area_outer_polygon) {
130 area_outer_polygon = area;
131 outer_polygon_it = iit;
132 }
133 }
134 }
136 ai_assert(outer_polygon_it != end);
138 const size_t outer_polygon_size = *outer_polygon_it;
139 const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)];
141 // Generate fake openings to meet the interface for the quadrulate
142 // algorithm. It boils down to generating small boxes given the
143 // inner polygon and the surface normal of the outer contour.
144 // It is important that we use the outer contour's normal because
145 // this is the plane onto which the quadrulate algorithm will
146 // project the entire mesh.
147 std::vector<TempOpening> fake_openings;
148 fake_openings.reserve(inmesh.vertcnt.size()-1);
150 std::vector<IfcVector3>::const_iterator vit = inmesh.verts.begin(), outer_vit;
152 for(iit = begin; iit != end; vit += *iit++) {
153 if (iit == outer_polygon_it) {
154 outer_vit = vit;
155 continue;
156 }
158 // Filter degenerate polygons to keep them from causing trouble later on
159 IfcVector3& n = normals[std::distance(begin,iit)];
160 const IfcFloat area = n.SquareLength();
161 if (area < 1e-5f) {
162 IFCImporter::LogWarn("skipping degenerate polygon (ProcessPolygonBoundaries)");
163 continue;
164 }
166 fake_openings.push_back(TempOpening());
167 TempOpening& opening = fake_openings.back();
169 opening.extrusionDir = master_normal;
170 opening.solid = NULL;
172 opening.profileMesh = boost::make_shared<TempMesh>();
173 opening.profileMesh->verts.reserve(*iit);
174 opening.profileMesh->vertcnt.push_back(*iit);
176 std::copy(vit, vit + *iit, std::back_inserter(opening.profileMesh->verts));
177 }
179 // fill a mesh with ONLY the main polygon
180 TempMesh temp;
181 temp.verts.reserve(outer_polygon_size);
182 temp.vertcnt.push_back(outer_polygon_size);
183 std::copy(outer_vit, outer_vit+outer_polygon_size,
184 std::back_inserter(temp.verts));
186 GenerateOpenings(fake_openings, normals, temp, false, false);
187 result.Append(temp);
188 }
190 // ------------------------------------------------------------------------------------------------
191 void ProcessConnectedFaceSet(const IfcConnectedFaceSet& fset, TempMesh& result, ConversionData& conv)
192 {
193 BOOST_FOREACH(const IfcFace& face, fset.CfsFaces) {
194 // size_t ob = -1, cnt = 0;
195 TempMesh meshout;
196 BOOST_FOREACH(const IfcFaceBound& bound, face.Bounds) {
198 if(const IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IfcPolyLoop>()) {
199 if(ProcessPolyloop(*polyloop, meshout,conv)) {
201 // The outer boundary is better determined by checking which
202 // polygon covers the largest area.
204 //if(bound.ToPtr<IfcFaceOuterBound>()) {
205 // ob = cnt;
206 //}
207 //++cnt;
209 }
210 }
211 else {
212 IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName());
213 continue;
214 }
216 // And this, even though it is sometimes TRUE and sometimes FALSE,
217 // does not really improve results.
219 /*if(!IsTrue(bound.Orientation)) {
220 size_t c = 0;
221 BOOST_FOREACH(unsigned int& c, meshout.vertcnt) {
222 std::reverse(result.verts.begin() + cnt,result.verts.begin() + cnt + c);
223 cnt += c;
224 }
225 }*/
226 }
227 ProcessPolygonBoundaries(result, meshout);
228 }
229 }
231 // ------------------------------------------------------------------------------------------------
232 void ProcessRevolvedAreaSolid(const IfcRevolvedAreaSolid& solid, TempMesh& result, ConversionData& conv)
233 {
234 TempMesh meshout;
236 // first read the profile description
237 if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) {
238 return;
239 }
241 IfcVector3 axis, pos;
242 ConvertAxisPlacement(axis,pos,solid.Axis);
244 IfcMatrix4 tb0,tb1;
245 IfcMatrix4::Translation(pos,tb0);
246 IfcMatrix4::Translation(-pos,tb1);
248 const std::vector<IfcVector3>& in = meshout.verts;
249 const size_t size=in.size();
251 bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2;
252 const IfcFloat max_angle = solid.Angle*conv.angle_scale;
253 if(fabs(max_angle) < 1e-3) {
254 if(has_area) {
255 result = meshout;
256 }
257 return;
258 }
260 const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(16 * fabs(max_angle)/AI_MATH_HALF_PI_F));
261 const IfcFloat delta = max_angle/cnt_segments;
263 has_area = has_area && fabs(max_angle) < AI_MATH_TWO_PI_F*0.99;
265 result.verts.reserve(size*((cnt_segments+1)*4+(has_area?2:0)));
266 result.vertcnt.reserve(size*cnt_segments+2);
268 IfcMatrix4 rot;
269 rot = tb0 * IfcMatrix4::Rotation(delta,axis,rot) * tb1;
271 size_t base = 0;
272 std::vector<IfcVector3>& out = result.verts;
274 // dummy data to simplify later processing
275 for(size_t i = 0; i < size; ++i) {
276 out.insert(out.end(),4,in[i]);
277 }
279 for(unsigned int seg = 0; seg < cnt_segments; ++seg) {
280 for(size_t i = 0; i < size; ++i) {
281 const size_t next = (i+1)%size;
283 result.vertcnt.push_back(4);
284 const IfcVector3& base_0 = out[base+i*4+3],base_1 = out[base+next*4+3];
286 out.push_back(base_0);
287 out.push_back(base_1);
288 out.push_back(rot*base_1);
289 out.push_back(rot*base_0);
290 }
291 base += size*4;
292 }
294 out.erase(out.begin(),out.begin()+size*4);
296 if(has_area) {
297 // leave the triangulation of the profile area to the ear cutting
298 // implementation in aiProcess_Triangulate - for now we just
299 // feed in two huge polygons.
300 base -= size*8;
301 for(size_t i = size; i--; ) {
302 out.push_back(out[base+i*4+3]);
303 }
304 for(size_t i = 0; i < size; ++i ) {
305 out.push_back(out[i*4]);
306 }
307 result.vertcnt.push_back(size);
308 result.vertcnt.push_back(size);
309 }
311 IfcMatrix4 trafo;
312 ConvertAxisPlacement(trafo, solid.Position);
314 result.Transform(trafo);
315 IFCImporter::LogDebug("generate mesh procedurally by radial extrusion (IfcRevolvedAreaSolid)");
316 }
320 // ------------------------------------------------------------------------------------------------
321 void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, ConversionData& conv)
322 {
323 const Curve* const curve = Curve::Convert(*solid.Directrix, conv);
324 if(!curve) {
325 IFCImporter::LogError("failed to convert Directrix curve (IfcSweptDiskSolid)");
326 return;
327 }
329 const std::vector<IfcVector3>& in = result.verts;
331 const unsigned int cnt_segments = 16;
332 const IfcFloat deltaAngle = AI_MATH_TWO_PI/cnt_segments;
334 const size_t samples = curve->EstimateSampleCount(solid.StartParam,solid.EndParam);
336 result.verts.reserve(cnt_segments * samples * 4);
337 result.vertcnt.reserve((cnt_segments - 1) * samples);
339 std::vector<IfcVector3> points;
340 points.reserve(cnt_segments * samples);
342 TempMesh temp;
343 curve->SampleDiscrete(temp,solid.StartParam,solid.EndParam);
344 const std::vector<IfcVector3>& curve_points = temp.verts;
346 if(curve_points.empty()) {
347 IFCImporter::LogWarn("curve evaluation yielded no points (IfcSweptDiskSolid)");
348 return;
349 }
351 IfcVector3 current = curve_points[0];
352 IfcVector3 previous = current;
353 IfcVector3 next;
355 IfcVector3 startvec;
356 startvec.x = 1.0f;
357 startvec.y = 1.0f;
358 startvec.z = 1.0f;
360 unsigned int last_dir = 0;
362 // generate circles at the sweep positions
363 for(size_t i = 0; i < samples; ++i) {
365 if(i != samples - 1) {
366 next = curve_points[i + 1];
367 }
369 // get a direction vector reflecting the approximate curvature (i.e. tangent)
370 IfcVector3 d = (current-previous) + (next-previous);
372 d.Normalize();
374 // figure out an arbitrary point q so that (p-q) * d = 0,
375 // try to maximize ||(p-q)|| * ||(p_last-q_last)||
376 IfcVector3 q;
377 bool take_any = false;
379 for (unsigned int i = 0; i < 2; ++i, take_any = true) {
380 if ((last_dir == 0 || take_any) && abs(d.x) > 1e-6) {
381 q.y = startvec.y;
382 q.z = startvec.z;
383 q.x = -(d.y * q.y + d.z * q.z) / d.x;
384 last_dir = 0;
385 break;
386 }
387 else if ((last_dir == 1 || take_any) && abs(d.y) > 1e-6) {
388 q.x = startvec.x;
389 q.z = startvec.z;
390 q.y = -(d.x * q.x + d.z * q.z) / d.y;
391 last_dir = 1;
392 break;
393 }
394 else if ((last_dir == 2 && abs(d.z) > 1e-6) || take_any) {
395 q.y = startvec.y;
396 q.x = startvec.x;
397 q.z = -(d.y * q.y + d.x * q.x) / d.z;
398 last_dir = 2;
399 break;
400 }
401 }
403 q *= solid.Radius / q.Length();
404 startvec = q;
406 // generate a rotation matrix to rotate q around d
407 IfcMatrix4 rot;
408 IfcMatrix4::Rotation(deltaAngle,d,rot);
410 for (unsigned int seg = 0; seg < cnt_segments; ++seg, q *= rot ) {
411 points.push_back(q + current);
412 }
414 previous = current;
415 current = next;
416 }
418 // make quads
419 for(size_t i = 0; i < samples - 1; ++i) {
421 const aiVector3D& this_start = points[ i * cnt_segments ];
423 // locate corresponding point on next sample ring
424 unsigned int best_pair_offset = 0;
425 float best_distance_squared = 1e10f;
426 for (unsigned int seg = 0; seg < cnt_segments; ++seg) {
427 const aiVector3D& p = points[ (i+1) * cnt_segments + seg];
428 const float l = (p-this_start).SquareLength();
430 if(l < best_distance_squared) {
431 best_pair_offset = seg;
432 best_distance_squared = l;
433 }
434 }
436 for (unsigned int seg = 0; seg < cnt_segments; ++seg) {
438 result.verts.push_back(points[ i * cnt_segments + (seg % cnt_segments)]);
439 result.verts.push_back(points[ i * cnt_segments + (seg + 1) % cnt_segments]);
440 result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + 1 + best_pair_offset) % cnt_segments)]);
441 result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + best_pair_offset) % cnt_segments)]);
443 IfcVector3& v1 = *(result.verts.end()-1);
444 IfcVector3& v2 = *(result.verts.end()-2);
445 IfcVector3& v3 = *(result.verts.end()-3);
446 IfcVector3& v4 = *(result.verts.end()-4);
448 if (((v4-v3) ^ (v4-v1)) * (v4 - curve_points[i]) < 0.0f) {
449 std::swap(v4, v1);
450 std::swap(v3, v2);
451 }
453 result.vertcnt.push_back(4);
454 }
455 }
457 IFCImporter::LogDebug("generate mesh procedurally by sweeping a disk along a curve (IfcSweptDiskSolid)");
458 }
460 // ------------------------------------------------------------------------------------------------
461 IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut)
462 {
463 const std::vector<IfcVector3>& out = curmesh.verts;
464 IfcMatrix3 m;
466 ok = true;
468 // The input "mesh" must be a single polygon
469 const size_t s = out.size();
470 assert(curmesh.vertcnt.size() == 1 && curmesh.vertcnt.back() == s);
472 const IfcVector3 any_point = out[s-1];
473 IfcVector3 nor;
475 // The input polygon is arbitrarily shaped, therefore we might need some tries
476 // until we find a suitable normal. Note that Newell's algorithm would give
477 // a more robust result, but this variant also gives us a suitable first
478 // axis for the 2D coordinate space on the polygon plane, exploiting the
479 // fact that the input polygon is nearly always a quad.
480 bool done = false;
481 size_t i, j;
482 for (i = 0; !done && i < s-2; done || ++i) {
483 for (j = i+1; j < s-1; ++j) {
484 nor = -((out[i]-any_point)^(out[j]-any_point));
485 if(fabs(nor.Length()) > 1e-8f) {
486 done = true;
487 break;
488 }
489 }
490 }
492 if(!done) {
493 ok = false;
494 return m;
495 }
497 nor.Normalize();
498 norOut = nor;
500 IfcVector3 r = (out[i]-any_point);
501 r.Normalize();
503 //if(d) {
504 // *d = -any_point * nor;
505 //}
507 // Reconstruct orthonormal basis
508 // XXX use Gram Schmidt for increased robustness
509 IfcVector3 u = r ^ nor;
510 u.Normalize();
512 m.a1 = r.x;
513 m.a2 = r.y;
514 m.a3 = r.z;
516 m.b1 = u.x;
517 m.b2 = u.y;
518 m.b3 = u.z;
520 m.c1 = -nor.x;
521 m.c2 = -nor.y;
522 m.c3 = -nor.z;
524 return m;
525 }
528 // ------------------------------------------------------------------------------------------------
529 void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& result,
530 ConversionData& conv, bool collect_openings)
531 {
532 TempMesh meshout;
534 // First read the profile description
535 if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) {
536 return;
537 }
539 IfcVector3 dir;
540 ConvertDirection(dir,solid.ExtrudedDirection);
542 dir *= solid.Depth; /*
543 if(conv.collect_openings && !conv.apply_openings) {
544 dir *= 1000.0;
545 } */
547 // Outline: assuming that `meshout.verts` is now a list of vertex points forming
548 // the underlying profile, extrude along the given axis, forming new
549 // triangles.
551 std::vector<IfcVector3>& in = meshout.verts;
552 const size_t size=in.size();
554 const bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2;
555 if(solid.Depth < 1e-6) {
556 if(has_area) {
557 result = meshout;
558 }
559 return;
560 }
562 result.verts.reserve(size*(has_area?4:2));
563 result.vertcnt.reserve(meshout.vertcnt.size()+2);
565 // First step: transform all vertices into the target coordinate space
566 IfcMatrix4 trafo;
567 ConvertAxisPlacement(trafo, solid.Position);
569 IfcVector3 vmin, vmax;
570 MinMaxChooser<IfcVector3>()(vmin, vmax);
571 BOOST_FOREACH(IfcVector3& v,in) {
572 v *= trafo;
574 vmin = std::min(vmin, v);
575 vmax = std::max(vmax, v);
576 }
578 vmax -= vmin;
579 const IfcFloat diag = vmax.Length();
581 IfcVector3 min = in[0];
582 dir *= IfcMatrix3(trafo);
584 std::vector<IfcVector3> nors;
585 const bool openings = !!conv.apply_openings && conv.apply_openings->size();
587 // Compute the normal vectors for all opening polygons as a prerequisite
588 // to TryAddOpenings_Poly2Tri()
589 // XXX this belongs into the aforementioned function
590 if (openings) {
592 if (!conv.settings.useCustomTriangulation) {
593 // it is essential to apply the openings in the correct spatial order. The direction
594 // doesn't matter, but we would screw up if we started with e.g. a door in between
595 // two windows.
596 std::sort(conv.apply_openings->begin(),conv.apply_openings->end(),
597 TempOpening::DistanceSorter(min));
598 }
600 nors.reserve(conv.apply_openings->size());
601 BOOST_FOREACH(TempOpening& t,*conv.apply_openings) {
602 TempMesh& bounds = *t.profileMesh.get();
604 if (bounds.verts.size() <= 2) {
605 nors.push_back(IfcVector3());
606 continue;
607 }
608 nors.push_back(((bounds.verts[2]-bounds.verts[0])^(bounds.verts[1]-bounds.verts[0]) ).Normalize());
609 }
610 }
613 TempMesh temp;
614 TempMesh& curmesh = openings ? temp : result;
615 std::vector<IfcVector3>& out = curmesh.verts;
617 size_t sides_with_openings = 0;
618 for(size_t i = 0; i < size; ++i) {
619 const size_t next = (i+1)%size;
621 curmesh.vertcnt.push_back(4);
623 out.push_back(in[i]);
624 out.push_back(in[i]+dir);
625 out.push_back(in[next]+dir);
626 out.push_back(in[next]);
628 if(openings) {
629 if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) {
630 ++sides_with_openings;
631 }
633 result.Append(temp);
634 temp.Clear();
635 }
636 }
638 if(openings) {
639 BOOST_FOREACH(TempOpening& opening, *conv.apply_openings) {
640 if (!opening.wallPoints.empty()) {
641 IFCImporter::LogError("failed to generate all window caps");
642 }
643 opening.wallPoints.clear();
644 }
645 }
647 size_t sides_with_v_openings = 0;
648 if(has_area) {
650 for(size_t n = 0; n < 2; ++n) {
651 for(size_t i = size; i--; ) {
652 out.push_back(in[i]+(n?dir:IfcVector3()));
653 }
655 curmesh.vertcnt.push_back(size);
656 if(openings && size > 2) {
657 if(GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) {
658 ++sides_with_v_openings;
659 }
661 result.Append(temp);
662 temp.Clear();
663 }
664 }
665 }
667 if(openings && ((sides_with_openings == 1 && sides_with_openings) || (sides_with_v_openings == 2 && sides_with_v_openings))) {
668 IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
669 }
671 IFCImporter::LogDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)");
673 // If this is an opening element, store both the extruded mesh and the 2D profile mesh
674 // it was created from. Return an empty mesh to the caller.
675 if(collect_openings && !result.IsEmpty()) {
676 ai_assert(conv.collect_openings);
677 boost::shared_ptr<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh());
678 profile->Swap(result);
680 boost::shared_ptr<TempMesh> profile2D = boost::shared_ptr<TempMesh>(new TempMesh());
681 profile2D->Swap(meshout);
682 conv.collect_openings->push_back(TempOpening(&solid,dir,profile, profile2D));
684 ai_assert(result.IsEmpty());
685 }
686 }
688 // ------------------------------------------------------------------------------------------------
689 void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
690 ConversionData& conv)
691 {
692 if(const IfcExtrudedAreaSolid* const solid = swept.ToPtr<IfcExtrudedAreaSolid>()) {
693 ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings);
694 }
695 else if(const IfcRevolvedAreaSolid* const rev = swept.ToPtr<IfcRevolvedAreaSolid>()) {
696 ProcessRevolvedAreaSolid(*rev,meshout,conv);
697 }
698 else {
699 IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is " + swept.GetClassName());
700 }
701 }
703 // ------------------------------------------------------------------------------------------------
704 bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned int>& mesh_indices,
705 ConversionData& conv)
706 {
707 bool fix_orientation = true;
708 boost::shared_ptr< TempMesh > meshtmp = boost::make_shared<TempMesh>();
709 if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<IfcShellBasedSurfaceModel>()) {
710 BOOST_FOREACH(boost::shared_ptr<const IfcShell> shell,shellmod->SbsmBoundary) {
711 try {
712 const EXPRESS::ENTITY& e = shell->To<ENTITY>();
713 const IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<IfcConnectedFaceSet>();
715 ProcessConnectedFaceSet(fs,*meshtmp.get(),conv);
716 }
717 catch(std::bad_cast&) {
718 IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
719 }
720 }
721 }
722 else if(const IfcConnectedFaceSet* fset = geo.ToPtr<IfcConnectedFaceSet>()) {
723 ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv);
724 }
725 else if(const IfcSweptAreaSolid* swept = geo.ToPtr<IfcSweptAreaSolid>()) {
726 ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv);
727 }
728 else if(const IfcSweptDiskSolid* disk = geo.ToPtr<IfcSweptDiskSolid>()) {
729 ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv);
730 fix_orientation = false;
731 }
732 else if(const IfcManifoldSolidBrep* brep = geo.ToPtr<IfcManifoldSolidBrep>()) {
733 ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv);
734 }
735 else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr<IfcFaceBasedSurfaceModel>()) {
736 BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) {
737 ProcessConnectedFaceSet(fc,*meshtmp.get(),conv);
738 }
739 }
740 else if(const IfcBooleanResult* boolean = geo.ToPtr<IfcBooleanResult>()) {
741 ProcessBoolean(*boolean,*meshtmp.get(),conv);
742 }
743 else if(geo.ToPtr<IfcBoundingBox>()) {
744 // silently skip over bounding boxes
745 return false;
746 }
747 else {
748 IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is " + geo.GetClassName());
749 return false;
750 }
752 // Do we just collect openings for a parent element (i.e. a wall)?
753 // In such a case, we generate the polygonal mesh as usual,
754 // but attach it to a TempOpening instance which will later be applied
755 // to the wall it pertains to.
757 // Note: swep area solids are added in ProcessExtrudedAreaSolid(),
758 // which returns an empty mesh.
759 if(conv.collect_openings) {
760 if (!meshtmp->IsEmpty()) {
761 conv.collect_openings->push_back(TempOpening(geo.ToPtr<IfcSolidModel>(),
762 IfcVector3(0,0,0),
763 meshtmp,
764 boost::shared_ptr<TempMesh>()));
765 }
766 return true;
767 }
769 if (meshtmp->IsEmpty()) {
770 return false;
771 }
773 meshtmp->RemoveAdjacentDuplicates();
774 meshtmp->RemoveDegenerates();
776 if(fix_orientation) {
777 meshtmp->FixupFaceOrientation();
778 }
780 aiMesh* const mesh = meshtmp->ToMesh();
781 if(mesh) {
782 mesh->mMaterialIndex = ProcessMaterials(geo,conv);
783 mesh_indices.push_back(conv.meshes.size());
784 conv.meshes.push_back(mesh);
785 return true;
786 }
787 return false;
788 }
790 // ------------------------------------------------------------------------------------------------
791 void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,
792 ConversionData& /*conv*/)
793 {
794 if (!mesh_indices.empty()) {
796 // make unique
797 std::sort(mesh_indices.begin(),mesh_indices.end());
798 std::vector<unsigned int>::iterator it_end = std::unique(mesh_indices.begin(),mesh_indices.end());
800 const size_t size = std::distance(mesh_indices.begin(),it_end);
802 nd->mNumMeshes = size;
803 nd->mMeshes = new unsigned int[nd->mNumMeshes];
804 for(unsigned int i = 0; i < nd->mNumMeshes; ++i) {
805 nd->mMeshes[i] = mesh_indices[i];
806 }
807 }
808 }
810 // ------------------------------------------------------------------------------------------------
811 bool TryQueryMeshCache(const IfcRepresentationItem& item,
812 std::vector<unsigned int>& mesh_indices,
813 ConversionData& conv)
814 {
815 ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(&item);
816 if (it != conv.cached_meshes.end()) {
817 std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices));
818 return true;
819 }
820 return false;
821 }
823 // ------------------------------------------------------------------------------------------------
824 void PopulateMeshCache(const IfcRepresentationItem& item,
825 const std::vector<unsigned int>& mesh_indices,
826 ConversionData& conv)
827 {
828 conv.cached_meshes[&item] = mesh_indices;
829 }
831 // ------------------------------------------------------------------------------------------------
832 bool ProcessRepresentationItem(const IfcRepresentationItem& item,
833 std::vector<unsigned int>& mesh_indices,
834 ConversionData& conv)
835 {
836 if (!TryQueryMeshCache(item,mesh_indices,conv)) {
837 if(ProcessGeometricItem(item,mesh_indices,conv)) {
838 if(mesh_indices.size()) {
839 PopulateMeshCache(item,mesh_indices,conv);
840 }
841 }
842 else return false;
843 }
844 return true;
845 }
848 } // ! IFC
849 } // ! Assimp
851 #endif