vrshoot
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/IFCGeometry.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,851 @@ 1.4 +/* 1.5 +Open Asset Import Library (assimp) 1.6 +---------------------------------------------------------------------- 1.7 + 1.8 +Copyright (c) 2006-2010, assimp team 1.9 +All rights reserved. 1.10 + 1.11 +Redistribution and use of this software in source and binary forms, 1.12 +with or without modification, are permitted provided that the 1.13 +following conditions are met: 1.14 + 1.15 +* Redistributions of source code must retain the above 1.16 + copyright notice, this list of conditions and the 1.17 + following disclaimer. 1.18 + 1.19 +* Redistributions in binary form must reproduce the above 1.20 + copyright notice, this list of conditions and the 1.21 + following disclaimer in the documentation and/or other 1.22 + materials provided with the distribution. 1.23 + 1.24 +* Neither the name of the assimp team, nor the names of its 1.25 + contributors may be used to endorse or promote products 1.26 + derived from this software without specific prior 1.27 + written permission of the assimp team. 1.28 + 1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.40 + 1.41 +---------------------------------------------------------------------- 1.42 +*/ 1.43 + 1.44 +/** @file IFCGeometry.cpp 1.45 + * @brief Geometry conversion and synthesis for IFC 1.46 + */ 1.47 + 1.48 +#include "AssimpPCH.h" 1.49 + 1.50 +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER 1.51 +#include "IFCUtil.h" 1.52 +#include "PolyTools.h" 1.53 +#include "ProcessHelper.h" 1.54 + 1.55 +#include "../contrib/poly2tri/poly2tri/poly2tri.h" 1.56 +#include "../contrib/clipper/clipper.hpp" 1.57 + 1.58 +#include <iterator> 1.59 + 1.60 +namespace Assimp { 1.61 + namespace IFC { 1.62 + 1.63 +// ------------------------------------------------------------------------------------------------ 1.64 +bool ProcessPolyloop(const IfcPolyLoop& loop, TempMesh& meshout, ConversionData& /*conv*/) 1.65 +{ 1.66 + size_t cnt = 0; 1.67 + BOOST_FOREACH(const IfcCartesianPoint& c, loop.Polygon) { 1.68 + IfcVector3 tmp; 1.69 + ConvertCartesianPoint(tmp,c); 1.70 + 1.71 + meshout.verts.push_back(tmp); 1.72 + ++cnt; 1.73 + } 1.74 + 1.75 + meshout.vertcnt.push_back(cnt); 1.76 + 1.77 + // zero- or one- vertex polyloops simply ignored 1.78 + if (meshout.vertcnt.back() > 1) { 1.79 + return true; 1.80 + } 1.81 + 1.82 + if (meshout.vertcnt.back()==1) { 1.83 + meshout.vertcnt.pop_back(); 1.84 + meshout.verts.pop_back(); 1.85 + } 1.86 + return false; 1.87 +} 1.88 + 1.89 +// ------------------------------------------------------------------------------------------------ 1.90 +void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t master_bounds = (size_t)-1) 1.91 +{ 1.92 + // handle all trivial cases 1.93 + if(inmesh.vertcnt.empty()) { 1.94 + return; 1.95 + } 1.96 + if(inmesh.vertcnt.size() == 1) { 1.97 + result.Append(inmesh); 1.98 + return; 1.99 + } 1.100 + 1.101 + ai_assert(std::count(inmesh.vertcnt.begin(), inmesh.vertcnt.end(), 0) == 0); 1.102 + 1.103 + typedef std::vector<unsigned int>::const_iterator face_iter; 1.104 + 1.105 + face_iter begin = inmesh.vertcnt.begin(), end = inmesh.vertcnt.end(), iit; 1.106 + std::vector<unsigned int>::const_iterator outer_polygon_it = end; 1.107 + 1.108 + // major task here: given a list of nested polygon boundaries (one of which 1.109 + // is the outer contour), reduce the triangulation task arising here to 1.110 + // one that can be solved using the "quadrulation" algorithm which we use 1.111 + // for pouring windows out of walls. The algorithm does not handle all 1.112 + // cases but at least it is numerically stable and gives "nice" triangles. 1.113 + 1.114 + // first compute normals for all polygons using Newell's algorithm 1.115 + // do not normalize 'normals', we need the original length for computing the polygon area 1.116 + std::vector<IfcVector3> normals; 1.117 + inmesh.ComputePolygonNormals(normals,false); 1.118 + 1.119 + // One of the polygons might be a IfcFaceOuterBound (in which case `master_bounds` 1.120 + // is its index). Sadly we can't rely on it, the docs say 'At most one of the bounds 1.121 + // shall be of the type IfcFaceOuterBound' 1.122 + IfcFloat area_outer_polygon = 1e-10f; 1.123 + if (master_bounds != (size_t)-1) { 1.124 + ai_assert(master_bounds < inmesh.vertcnt.size()); 1.125 + outer_polygon_it = begin + master_bounds; 1.126 + } 1.127 + else { 1.128 + for(iit = begin; iit != end; iit++) { 1.129 + // find the polygon with the largest area and take it as the outer bound. 1.130 + IfcVector3& n = normals[std::distance(begin,iit)]; 1.131 + const IfcFloat area = n.SquareLength(); 1.132 + if (area > area_outer_polygon) { 1.133 + area_outer_polygon = area; 1.134 + outer_polygon_it = iit; 1.135 + } 1.136 + } 1.137 + } 1.138 + 1.139 + ai_assert(outer_polygon_it != end); 1.140 + 1.141 + const size_t outer_polygon_size = *outer_polygon_it; 1.142 + const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)]; 1.143 + 1.144 + // Generate fake openings to meet the interface for the quadrulate 1.145 + // algorithm. It boils down to generating small boxes given the 1.146 + // inner polygon and the surface normal of the outer contour. 1.147 + // It is important that we use the outer contour's normal because 1.148 + // this is the plane onto which the quadrulate algorithm will 1.149 + // project the entire mesh. 1.150 + std::vector<TempOpening> fake_openings; 1.151 + fake_openings.reserve(inmesh.vertcnt.size()-1); 1.152 + 1.153 + std::vector<IfcVector3>::const_iterator vit = inmesh.verts.begin(), outer_vit; 1.154 + 1.155 + for(iit = begin; iit != end; vit += *iit++) { 1.156 + if (iit == outer_polygon_it) { 1.157 + outer_vit = vit; 1.158 + continue; 1.159 + } 1.160 + 1.161 + // Filter degenerate polygons to keep them from causing trouble later on 1.162 + IfcVector3& n = normals[std::distance(begin,iit)]; 1.163 + const IfcFloat area = n.SquareLength(); 1.164 + if (area < 1e-5f) { 1.165 + IFCImporter::LogWarn("skipping degenerate polygon (ProcessPolygonBoundaries)"); 1.166 + continue; 1.167 + } 1.168 + 1.169 + fake_openings.push_back(TempOpening()); 1.170 + TempOpening& opening = fake_openings.back(); 1.171 + 1.172 + opening.extrusionDir = master_normal; 1.173 + opening.solid = NULL; 1.174 + 1.175 + opening.profileMesh = boost::make_shared<TempMesh>(); 1.176 + opening.profileMesh->verts.reserve(*iit); 1.177 + opening.profileMesh->vertcnt.push_back(*iit); 1.178 + 1.179 + std::copy(vit, vit + *iit, std::back_inserter(opening.profileMesh->verts)); 1.180 + } 1.181 + 1.182 + // fill a mesh with ONLY the main polygon 1.183 + TempMesh temp; 1.184 + temp.verts.reserve(outer_polygon_size); 1.185 + temp.vertcnt.push_back(outer_polygon_size); 1.186 + std::copy(outer_vit, outer_vit+outer_polygon_size, 1.187 + std::back_inserter(temp.verts)); 1.188 + 1.189 + GenerateOpenings(fake_openings, normals, temp, false, false); 1.190 + result.Append(temp); 1.191 +} 1.192 + 1.193 +// ------------------------------------------------------------------------------------------------ 1.194 +void ProcessConnectedFaceSet(const IfcConnectedFaceSet& fset, TempMesh& result, ConversionData& conv) 1.195 +{ 1.196 + BOOST_FOREACH(const IfcFace& face, fset.CfsFaces) { 1.197 + // size_t ob = -1, cnt = 0; 1.198 + TempMesh meshout; 1.199 + BOOST_FOREACH(const IfcFaceBound& bound, face.Bounds) { 1.200 + 1.201 + if(const IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IfcPolyLoop>()) { 1.202 + if(ProcessPolyloop(*polyloop, meshout,conv)) { 1.203 + 1.204 + // The outer boundary is better determined by checking which 1.205 + // polygon covers the largest area. 1.206 + 1.207 + //if(bound.ToPtr<IfcFaceOuterBound>()) { 1.208 + // ob = cnt; 1.209 + //} 1.210 + //++cnt; 1.211 + 1.212 + } 1.213 + } 1.214 + else { 1.215 + IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName()); 1.216 + continue; 1.217 + } 1.218 + 1.219 + // And this, even though it is sometimes TRUE and sometimes FALSE, 1.220 + // does not really improve results. 1.221 + 1.222 + /*if(!IsTrue(bound.Orientation)) { 1.223 + size_t c = 0; 1.224 + BOOST_FOREACH(unsigned int& c, meshout.vertcnt) { 1.225 + std::reverse(result.verts.begin() + cnt,result.verts.begin() + cnt + c); 1.226 + cnt += c; 1.227 + } 1.228 + }*/ 1.229 + } 1.230 + ProcessPolygonBoundaries(result, meshout); 1.231 + } 1.232 +} 1.233 + 1.234 +// ------------------------------------------------------------------------------------------------ 1.235 +void ProcessRevolvedAreaSolid(const IfcRevolvedAreaSolid& solid, TempMesh& result, ConversionData& conv) 1.236 +{ 1.237 + TempMesh meshout; 1.238 + 1.239 + // first read the profile description 1.240 + if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) { 1.241 + return; 1.242 + } 1.243 + 1.244 + IfcVector3 axis, pos; 1.245 + ConvertAxisPlacement(axis,pos,solid.Axis); 1.246 + 1.247 + IfcMatrix4 tb0,tb1; 1.248 + IfcMatrix4::Translation(pos,tb0); 1.249 + IfcMatrix4::Translation(-pos,tb1); 1.250 + 1.251 + const std::vector<IfcVector3>& in = meshout.verts; 1.252 + const size_t size=in.size(); 1.253 + 1.254 + bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2; 1.255 + const IfcFloat max_angle = solid.Angle*conv.angle_scale; 1.256 + if(fabs(max_angle) < 1e-3) { 1.257 + if(has_area) { 1.258 + result = meshout; 1.259 + } 1.260 + return; 1.261 + } 1.262 + 1.263 + const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(16 * fabs(max_angle)/AI_MATH_HALF_PI_F)); 1.264 + const IfcFloat delta = max_angle/cnt_segments; 1.265 + 1.266 + has_area = has_area && fabs(max_angle) < AI_MATH_TWO_PI_F*0.99; 1.267 + 1.268 + result.verts.reserve(size*((cnt_segments+1)*4+(has_area?2:0))); 1.269 + result.vertcnt.reserve(size*cnt_segments+2); 1.270 + 1.271 + IfcMatrix4 rot; 1.272 + rot = tb0 * IfcMatrix4::Rotation(delta,axis,rot) * tb1; 1.273 + 1.274 + size_t base = 0; 1.275 + std::vector<IfcVector3>& out = result.verts; 1.276 + 1.277 + // dummy data to simplify later processing 1.278 + for(size_t i = 0; i < size; ++i) { 1.279 + out.insert(out.end(),4,in[i]); 1.280 + } 1.281 + 1.282 + for(unsigned int seg = 0; seg < cnt_segments; ++seg) { 1.283 + for(size_t i = 0; i < size; ++i) { 1.284 + const size_t next = (i+1)%size; 1.285 + 1.286 + result.vertcnt.push_back(4); 1.287 + const IfcVector3& base_0 = out[base+i*4+3],base_1 = out[base+next*4+3]; 1.288 + 1.289 + out.push_back(base_0); 1.290 + out.push_back(base_1); 1.291 + out.push_back(rot*base_1); 1.292 + out.push_back(rot*base_0); 1.293 + } 1.294 + base += size*4; 1.295 + } 1.296 + 1.297 + out.erase(out.begin(),out.begin()+size*4); 1.298 + 1.299 + if(has_area) { 1.300 + // leave the triangulation of the profile area to the ear cutting 1.301 + // implementation in aiProcess_Triangulate - for now we just 1.302 + // feed in two huge polygons. 1.303 + base -= size*8; 1.304 + for(size_t i = size; i--; ) { 1.305 + out.push_back(out[base+i*4+3]); 1.306 + } 1.307 + for(size_t i = 0; i < size; ++i ) { 1.308 + out.push_back(out[i*4]); 1.309 + } 1.310 + result.vertcnt.push_back(size); 1.311 + result.vertcnt.push_back(size); 1.312 + } 1.313 + 1.314 + IfcMatrix4 trafo; 1.315 + ConvertAxisPlacement(trafo, solid.Position); 1.316 + 1.317 + result.Transform(trafo); 1.318 + IFCImporter::LogDebug("generate mesh procedurally by radial extrusion (IfcRevolvedAreaSolid)"); 1.319 +} 1.320 + 1.321 + 1.322 + 1.323 +// ------------------------------------------------------------------------------------------------ 1.324 +void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, ConversionData& conv) 1.325 +{ 1.326 + const Curve* const curve = Curve::Convert(*solid.Directrix, conv); 1.327 + if(!curve) { 1.328 + IFCImporter::LogError("failed to convert Directrix curve (IfcSweptDiskSolid)"); 1.329 + return; 1.330 + } 1.331 + 1.332 + const std::vector<IfcVector3>& in = result.verts; 1.333 + 1.334 + const unsigned int cnt_segments = 16; 1.335 + const IfcFloat deltaAngle = AI_MATH_TWO_PI/cnt_segments; 1.336 + 1.337 + const size_t samples = curve->EstimateSampleCount(solid.StartParam,solid.EndParam); 1.338 + 1.339 + result.verts.reserve(cnt_segments * samples * 4); 1.340 + result.vertcnt.reserve((cnt_segments - 1) * samples); 1.341 + 1.342 + std::vector<IfcVector3> points; 1.343 + points.reserve(cnt_segments * samples); 1.344 + 1.345 + TempMesh temp; 1.346 + curve->SampleDiscrete(temp,solid.StartParam,solid.EndParam); 1.347 + const std::vector<IfcVector3>& curve_points = temp.verts; 1.348 + 1.349 + if(curve_points.empty()) { 1.350 + IFCImporter::LogWarn("curve evaluation yielded no points (IfcSweptDiskSolid)"); 1.351 + return; 1.352 + } 1.353 + 1.354 + IfcVector3 current = curve_points[0]; 1.355 + IfcVector3 previous = current; 1.356 + IfcVector3 next; 1.357 + 1.358 + IfcVector3 startvec; 1.359 + startvec.x = 1.0f; 1.360 + startvec.y = 1.0f; 1.361 + startvec.z = 1.0f; 1.362 + 1.363 + unsigned int last_dir = 0; 1.364 + 1.365 + // generate circles at the sweep positions 1.366 + for(size_t i = 0; i < samples; ++i) { 1.367 + 1.368 + if(i != samples - 1) { 1.369 + next = curve_points[i + 1]; 1.370 + } 1.371 + 1.372 + // get a direction vector reflecting the approximate curvature (i.e. tangent) 1.373 + IfcVector3 d = (current-previous) + (next-previous); 1.374 + 1.375 + d.Normalize(); 1.376 + 1.377 + // figure out an arbitrary point q so that (p-q) * d = 0, 1.378 + // try to maximize ||(p-q)|| * ||(p_last-q_last)|| 1.379 + IfcVector3 q; 1.380 + bool take_any = false; 1.381 + 1.382 + for (unsigned int i = 0; i < 2; ++i, take_any = true) { 1.383 + if ((last_dir == 0 || take_any) && abs(d.x) > 1e-6) { 1.384 + q.y = startvec.y; 1.385 + q.z = startvec.z; 1.386 + q.x = -(d.y * q.y + d.z * q.z) / d.x; 1.387 + last_dir = 0; 1.388 + break; 1.389 + } 1.390 + else if ((last_dir == 1 || take_any) && abs(d.y) > 1e-6) { 1.391 + q.x = startvec.x; 1.392 + q.z = startvec.z; 1.393 + q.y = -(d.x * q.x + d.z * q.z) / d.y; 1.394 + last_dir = 1; 1.395 + break; 1.396 + } 1.397 + else if ((last_dir == 2 && abs(d.z) > 1e-6) || take_any) { 1.398 + q.y = startvec.y; 1.399 + q.x = startvec.x; 1.400 + q.z = -(d.y * q.y + d.x * q.x) / d.z; 1.401 + last_dir = 2; 1.402 + break; 1.403 + } 1.404 + } 1.405 + 1.406 + q *= solid.Radius / q.Length(); 1.407 + startvec = q; 1.408 + 1.409 + // generate a rotation matrix to rotate q around d 1.410 + IfcMatrix4 rot; 1.411 + IfcMatrix4::Rotation(deltaAngle,d,rot); 1.412 + 1.413 + for (unsigned int seg = 0; seg < cnt_segments; ++seg, q *= rot ) { 1.414 + points.push_back(q + current); 1.415 + } 1.416 + 1.417 + previous = current; 1.418 + current = next; 1.419 + } 1.420 + 1.421 + // make quads 1.422 + for(size_t i = 0; i < samples - 1; ++i) { 1.423 + 1.424 + const aiVector3D& this_start = points[ i * cnt_segments ]; 1.425 + 1.426 + // locate corresponding point on next sample ring 1.427 + unsigned int best_pair_offset = 0; 1.428 + float best_distance_squared = 1e10f; 1.429 + for (unsigned int seg = 0; seg < cnt_segments; ++seg) { 1.430 + const aiVector3D& p = points[ (i+1) * cnt_segments + seg]; 1.431 + const float l = (p-this_start).SquareLength(); 1.432 + 1.433 + if(l < best_distance_squared) { 1.434 + best_pair_offset = seg; 1.435 + best_distance_squared = l; 1.436 + } 1.437 + } 1.438 + 1.439 + for (unsigned int seg = 0; seg < cnt_segments; ++seg) { 1.440 + 1.441 + result.verts.push_back(points[ i * cnt_segments + (seg % cnt_segments)]); 1.442 + result.verts.push_back(points[ i * cnt_segments + (seg + 1) % cnt_segments]); 1.443 + result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + 1 + best_pair_offset) % cnt_segments)]); 1.444 + result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + best_pair_offset) % cnt_segments)]); 1.445 + 1.446 + IfcVector3& v1 = *(result.verts.end()-1); 1.447 + IfcVector3& v2 = *(result.verts.end()-2); 1.448 + IfcVector3& v3 = *(result.verts.end()-3); 1.449 + IfcVector3& v4 = *(result.verts.end()-4); 1.450 + 1.451 + if (((v4-v3) ^ (v4-v1)) * (v4 - curve_points[i]) < 0.0f) { 1.452 + std::swap(v4, v1); 1.453 + std::swap(v3, v2); 1.454 + } 1.455 + 1.456 + result.vertcnt.push_back(4); 1.457 + } 1.458 + } 1.459 + 1.460 + IFCImporter::LogDebug("generate mesh procedurally by sweeping a disk along a curve (IfcSweptDiskSolid)"); 1.461 +} 1.462 + 1.463 +// ------------------------------------------------------------------------------------------------ 1.464 +IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut) 1.465 +{ 1.466 + const std::vector<IfcVector3>& out = curmesh.verts; 1.467 + IfcMatrix3 m; 1.468 + 1.469 + ok = true; 1.470 + 1.471 + // The input "mesh" must be a single polygon 1.472 + const size_t s = out.size(); 1.473 + assert(curmesh.vertcnt.size() == 1 && curmesh.vertcnt.back() == s); 1.474 + 1.475 + const IfcVector3 any_point = out[s-1]; 1.476 + IfcVector3 nor; 1.477 + 1.478 + // The input polygon is arbitrarily shaped, therefore we might need some tries 1.479 + // until we find a suitable normal. Note that Newell's algorithm would give 1.480 + // a more robust result, but this variant also gives us a suitable first 1.481 + // axis for the 2D coordinate space on the polygon plane, exploiting the 1.482 + // fact that the input polygon is nearly always a quad. 1.483 + bool done = false; 1.484 + size_t i, j; 1.485 + for (i = 0; !done && i < s-2; done || ++i) { 1.486 + for (j = i+1; j < s-1; ++j) { 1.487 + nor = -((out[i]-any_point)^(out[j]-any_point)); 1.488 + if(fabs(nor.Length()) > 1e-8f) { 1.489 + done = true; 1.490 + break; 1.491 + } 1.492 + } 1.493 + } 1.494 + 1.495 + if(!done) { 1.496 + ok = false; 1.497 + return m; 1.498 + } 1.499 + 1.500 + nor.Normalize(); 1.501 + norOut = nor; 1.502 + 1.503 + IfcVector3 r = (out[i]-any_point); 1.504 + r.Normalize(); 1.505 + 1.506 + //if(d) { 1.507 + // *d = -any_point * nor; 1.508 + //} 1.509 + 1.510 + // Reconstruct orthonormal basis 1.511 + // XXX use Gram Schmidt for increased robustness 1.512 + IfcVector3 u = r ^ nor; 1.513 + u.Normalize(); 1.514 + 1.515 + m.a1 = r.x; 1.516 + m.a2 = r.y; 1.517 + m.a3 = r.z; 1.518 + 1.519 + m.b1 = u.x; 1.520 + m.b2 = u.y; 1.521 + m.b3 = u.z; 1.522 + 1.523 + m.c1 = -nor.x; 1.524 + m.c2 = -nor.y; 1.525 + m.c3 = -nor.z; 1.526 + 1.527 + return m; 1.528 +} 1.529 + 1.530 + 1.531 +// ------------------------------------------------------------------------------------------------ 1.532 +void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& result, 1.533 + ConversionData& conv, bool collect_openings) 1.534 +{ 1.535 + TempMesh meshout; 1.536 + 1.537 + // First read the profile description 1.538 + if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) { 1.539 + return; 1.540 + } 1.541 + 1.542 + IfcVector3 dir; 1.543 + ConvertDirection(dir,solid.ExtrudedDirection); 1.544 + 1.545 + dir *= solid.Depth; /* 1.546 + if(conv.collect_openings && !conv.apply_openings) { 1.547 + dir *= 1000.0; 1.548 + } */ 1.549 + 1.550 + // Outline: assuming that `meshout.verts` is now a list of vertex points forming 1.551 + // the underlying profile, extrude along the given axis, forming new 1.552 + // triangles. 1.553 + 1.554 + std::vector<IfcVector3>& in = meshout.verts; 1.555 + const size_t size=in.size(); 1.556 + 1.557 + const bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2; 1.558 + if(solid.Depth < 1e-6) { 1.559 + if(has_area) { 1.560 + result = meshout; 1.561 + } 1.562 + return; 1.563 + } 1.564 + 1.565 + result.verts.reserve(size*(has_area?4:2)); 1.566 + result.vertcnt.reserve(meshout.vertcnt.size()+2); 1.567 + 1.568 + // First step: transform all vertices into the target coordinate space 1.569 + IfcMatrix4 trafo; 1.570 + ConvertAxisPlacement(trafo, solid.Position); 1.571 + 1.572 + IfcVector3 vmin, vmax; 1.573 + MinMaxChooser<IfcVector3>()(vmin, vmax); 1.574 + BOOST_FOREACH(IfcVector3& v,in) { 1.575 + v *= trafo; 1.576 + 1.577 + vmin = std::min(vmin, v); 1.578 + vmax = std::max(vmax, v); 1.579 + } 1.580 + 1.581 + vmax -= vmin; 1.582 + const IfcFloat diag = vmax.Length(); 1.583 + 1.584 + IfcVector3 min = in[0]; 1.585 + dir *= IfcMatrix3(trafo); 1.586 + 1.587 + std::vector<IfcVector3> nors; 1.588 + const bool openings = !!conv.apply_openings && conv.apply_openings->size(); 1.589 + 1.590 + // Compute the normal vectors for all opening polygons as a prerequisite 1.591 + // to TryAddOpenings_Poly2Tri() 1.592 + // XXX this belongs into the aforementioned function 1.593 + if (openings) { 1.594 + 1.595 + if (!conv.settings.useCustomTriangulation) { 1.596 + // it is essential to apply the openings in the correct spatial order. The direction 1.597 + // doesn't matter, but we would screw up if we started with e.g. a door in between 1.598 + // two windows. 1.599 + std::sort(conv.apply_openings->begin(),conv.apply_openings->end(), 1.600 + TempOpening::DistanceSorter(min)); 1.601 + } 1.602 + 1.603 + nors.reserve(conv.apply_openings->size()); 1.604 + BOOST_FOREACH(TempOpening& t,*conv.apply_openings) { 1.605 + TempMesh& bounds = *t.profileMesh.get(); 1.606 + 1.607 + if (bounds.verts.size() <= 2) { 1.608 + nors.push_back(IfcVector3()); 1.609 + continue; 1.610 + } 1.611 + nors.push_back(((bounds.verts[2]-bounds.verts[0])^(bounds.verts[1]-bounds.verts[0]) ).Normalize()); 1.612 + } 1.613 + } 1.614 + 1.615 + 1.616 + TempMesh temp; 1.617 + TempMesh& curmesh = openings ? temp : result; 1.618 + std::vector<IfcVector3>& out = curmesh.verts; 1.619 + 1.620 + size_t sides_with_openings = 0; 1.621 + for(size_t i = 0; i < size; ++i) { 1.622 + const size_t next = (i+1)%size; 1.623 + 1.624 + curmesh.vertcnt.push_back(4); 1.625 + 1.626 + out.push_back(in[i]); 1.627 + out.push_back(in[i]+dir); 1.628 + out.push_back(in[next]+dir); 1.629 + out.push_back(in[next]); 1.630 + 1.631 + if(openings) { 1.632 + if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) { 1.633 + ++sides_with_openings; 1.634 + } 1.635 + 1.636 + result.Append(temp); 1.637 + temp.Clear(); 1.638 + } 1.639 + } 1.640 + 1.641 + if(openings) { 1.642 + BOOST_FOREACH(TempOpening& opening, *conv.apply_openings) { 1.643 + if (!opening.wallPoints.empty()) { 1.644 + IFCImporter::LogError("failed to generate all window caps"); 1.645 + } 1.646 + opening.wallPoints.clear(); 1.647 + } 1.648 + } 1.649 + 1.650 + size_t sides_with_v_openings = 0; 1.651 + if(has_area) { 1.652 + 1.653 + for(size_t n = 0; n < 2; ++n) { 1.654 + for(size_t i = size; i--; ) { 1.655 + out.push_back(in[i]+(n?dir:IfcVector3())); 1.656 + } 1.657 + 1.658 + curmesh.vertcnt.push_back(size); 1.659 + if(openings && size > 2) { 1.660 + if(GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) { 1.661 + ++sides_with_v_openings; 1.662 + } 1.663 + 1.664 + result.Append(temp); 1.665 + temp.Clear(); 1.666 + } 1.667 + } 1.668 + } 1.669 + 1.670 + if(openings && ((sides_with_openings == 1 && sides_with_openings) || (sides_with_v_openings == 2 && sides_with_v_openings))) { 1.671 + IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp"); 1.672 + } 1.673 + 1.674 + IFCImporter::LogDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)"); 1.675 + 1.676 + // If this is an opening element, store both the extruded mesh and the 2D profile mesh 1.677 + // it was created from. Return an empty mesh to the caller. 1.678 + if(collect_openings && !result.IsEmpty()) { 1.679 + ai_assert(conv.collect_openings); 1.680 + boost::shared_ptr<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh()); 1.681 + profile->Swap(result); 1.682 + 1.683 + boost::shared_ptr<TempMesh> profile2D = boost::shared_ptr<TempMesh>(new TempMesh()); 1.684 + profile2D->Swap(meshout); 1.685 + conv.collect_openings->push_back(TempOpening(&solid,dir,profile, profile2D)); 1.686 + 1.687 + ai_assert(result.IsEmpty()); 1.688 + } 1.689 +} 1.690 + 1.691 +// ------------------------------------------------------------------------------------------------ 1.692 +void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, 1.693 + ConversionData& conv) 1.694 +{ 1.695 + if(const IfcExtrudedAreaSolid* const solid = swept.ToPtr<IfcExtrudedAreaSolid>()) { 1.696 + ProcessExtrudedAreaSolid(*solid,meshout,conv, !!conv.collect_openings); 1.697 + } 1.698 + else if(const IfcRevolvedAreaSolid* const rev = swept.ToPtr<IfcRevolvedAreaSolid>()) { 1.699 + ProcessRevolvedAreaSolid(*rev,meshout,conv); 1.700 + } 1.701 + else { 1.702 + IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is " + swept.GetClassName()); 1.703 + } 1.704 +} 1.705 + 1.706 +// ------------------------------------------------------------------------------------------------ 1.707 +bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned int>& mesh_indices, 1.708 + ConversionData& conv) 1.709 +{ 1.710 + bool fix_orientation = true; 1.711 + boost::shared_ptr< TempMesh > meshtmp = boost::make_shared<TempMesh>(); 1.712 + if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<IfcShellBasedSurfaceModel>()) { 1.713 + BOOST_FOREACH(boost::shared_ptr<const IfcShell> shell,shellmod->SbsmBoundary) { 1.714 + try { 1.715 + const EXPRESS::ENTITY& e = shell->To<ENTITY>(); 1.716 + const IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<IfcConnectedFaceSet>(); 1.717 + 1.718 + ProcessConnectedFaceSet(fs,*meshtmp.get(),conv); 1.719 + } 1.720 + catch(std::bad_cast&) { 1.721 + IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet"); 1.722 + } 1.723 + } 1.724 + } 1.725 + else if(const IfcConnectedFaceSet* fset = geo.ToPtr<IfcConnectedFaceSet>()) { 1.726 + ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv); 1.727 + } 1.728 + else if(const IfcSweptAreaSolid* swept = geo.ToPtr<IfcSweptAreaSolid>()) { 1.729 + ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv); 1.730 + } 1.731 + else if(const IfcSweptDiskSolid* disk = geo.ToPtr<IfcSweptDiskSolid>()) { 1.732 + ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv); 1.733 + fix_orientation = false; 1.734 + } 1.735 + else if(const IfcManifoldSolidBrep* brep = geo.ToPtr<IfcManifoldSolidBrep>()) { 1.736 + ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv); 1.737 + } 1.738 + else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr<IfcFaceBasedSurfaceModel>()) { 1.739 + BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) { 1.740 + ProcessConnectedFaceSet(fc,*meshtmp.get(),conv); 1.741 + } 1.742 + } 1.743 + else if(const IfcBooleanResult* boolean = geo.ToPtr<IfcBooleanResult>()) { 1.744 + ProcessBoolean(*boolean,*meshtmp.get(),conv); 1.745 + } 1.746 + else if(geo.ToPtr<IfcBoundingBox>()) { 1.747 + // silently skip over bounding boxes 1.748 + return false; 1.749 + } 1.750 + else { 1.751 + IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is " + geo.GetClassName()); 1.752 + return false; 1.753 + } 1.754 + 1.755 + // Do we just collect openings for a parent element (i.e. a wall)? 1.756 + // In such a case, we generate the polygonal mesh as usual, 1.757 + // but attach it to a TempOpening instance which will later be applied 1.758 + // to the wall it pertains to. 1.759 + 1.760 + // Note: swep area solids are added in ProcessExtrudedAreaSolid(), 1.761 + // which returns an empty mesh. 1.762 + if(conv.collect_openings) { 1.763 + if (!meshtmp->IsEmpty()) { 1.764 + conv.collect_openings->push_back(TempOpening(geo.ToPtr<IfcSolidModel>(), 1.765 + IfcVector3(0,0,0), 1.766 + meshtmp, 1.767 + boost::shared_ptr<TempMesh>())); 1.768 + } 1.769 + return true; 1.770 + } 1.771 + 1.772 + if (meshtmp->IsEmpty()) { 1.773 + return false; 1.774 + } 1.775 + 1.776 + meshtmp->RemoveAdjacentDuplicates(); 1.777 + meshtmp->RemoveDegenerates(); 1.778 + 1.779 + if(fix_orientation) { 1.780 + meshtmp->FixupFaceOrientation(); 1.781 + } 1.782 + 1.783 + aiMesh* const mesh = meshtmp->ToMesh(); 1.784 + if(mesh) { 1.785 + mesh->mMaterialIndex = ProcessMaterials(geo,conv); 1.786 + mesh_indices.push_back(conv.meshes.size()); 1.787 + conv.meshes.push_back(mesh); 1.788 + return true; 1.789 + } 1.790 + return false; 1.791 +} 1.792 + 1.793 +// ------------------------------------------------------------------------------------------------ 1.794 +void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd, 1.795 + ConversionData& /*conv*/) 1.796 +{ 1.797 + if (!mesh_indices.empty()) { 1.798 + 1.799 + // make unique 1.800 + std::sort(mesh_indices.begin(),mesh_indices.end()); 1.801 + std::vector<unsigned int>::iterator it_end = std::unique(mesh_indices.begin(),mesh_indices.end()); 1.802 + 1.803 + const size_t size = std::distance(mesh_indices.begin(),it_end); 1.804 + 1.805 + nd->mNumMeshes = size; 1.806 + nd->mMeshes = new unsigned int[nd->mNumMeshes]; 1.807 + for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { 1.808 + nd->mMeshes[i] = mesh_indices[i]; 1.809 + } 1.810 + } 1.811 +} 1.812 + 1.813 +// ------------------------------------------------------------------------------------------------ 1.814 +bool TryQueryMeshCache(const IfcRepresentationItem& item, 1.815 + std::vector<unsigned int>& mesh_indices, 1.816 + ConversionData& conv) 1.817 +{ 1.818 + ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(&item); 1.819 + if (it != conv.cached_meshes.end()) { 1.820 + std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices)); 1.821 + return true; 1.822 + } 1.823 + return false; 1.824 +} 1.825 + 1.826 +// ------------------------------------------------------------------------------------------------ 1.827 +void PopulateMeshCache(const IfcRepresentationItem& item, 1.828 + const std::vector<unsigned int>& mesh_indices, 1.829 + ConversionData& conv) 1.830 +{ 1.831 + conv.cached_meshes[&item] = mesh_indices; 1.832 +} 1.833 + 1.834 +// ------------------------------------------------------------------------------------------------ 1.835 +bool ProcessRepresentationItem(const IfcRepresentationItem& item, 1.836 + std::vector<unsigned int>& mesh_indices, 1.837 + ConversionData& conv) 1.838 +{ 1.839 + if (!TryQueryMeshCache(item,mesh_indices,conv)) { 1.840 + if(ProcessGeometricItem(item,mesh_indices,conv)) { 1.841 + if(mesh_indices.size()) { 1.842 + PopulateMeshCache(item,mesh_indices,conv); 1.843 + } 1.844 + } 1.845 + else return false; 1.846 + } 1.847 + return true; 1.848 +} 1.849 + 1.850 + 1.851 +} // ! IFC 1.852 +} // ! Assimp 1.853 + 1.854 +#endif