vrshoot

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