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