1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2019, assimp team
6 
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the
12 following conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 
40 ----------------------------------------------------------------------
41 */
42 
43 /** @file  IFC.cpp
44  *  @brief Implementation of the Industry Foundation Classes loader.
45  */
46 
47 #ifndef INCLUDED_IFCUTIL_H
48 #define INCLUDED_IFCUTIL_H
49 
50 #include "IFCReaderGen_2x3.h"
51 #include "IFCLoader.h"
52 #include "code/Step/STEPFile.h"
53 #include <assimp/mesh.h>
54 #include <assimp/material.h>
55 
56 struct aiNode;
57 
58 namespace Assimp {
59 namespace IFC {
60 
61     typedef double IfcFloat;
62 
63     // IfcFloat-precision math data types
64     typedef aiVector2t<IfcFloat> IfcVector2;
65     typedef aiVector3t<IfcFloat> IfcVector3;
66     typedef aiMatrix4x4t<IfcFloat> IfcMatrix4;
67     typedef aiMatrix3x3t<IfcFloat> IfcMatrix3;
68     typedef aiColor4t<IfcFloat> IfcColor4;
69 
70 
71 // ------------------------------------------------------------------------------------------------
72 // Helper for std::for_each to delete all heap-allocated items in a container
73 // ------------------------------------------------------------------------------------------------
74 template<typename T>
75 struct delete_fun {
operatordelete_fun76     void operator()(T* del) {
77         delete del;
78     }
79 };
80 
81 
82 
83 // ------------------------------------------------------------------------------------------------
84 // Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
85 // ------------------------------------------------------------------------------------------------
86 struct TempMesh {
87     std::vector<IfcVector3> mVerts;
88     std::vector<unsigned int> mVertcnt;
89 
90     // utilities
91     aiMesh* ToMesh();
92     void Clear();
93     void Transform(const IfcMatrix4& mat);
94     IfcVector3 Center() const;
95     void Append(const TempMesh& other);
96     bool IsEmpty() const;
97     void RemoveAdjacentDuplicates();
98     void RemoveDegenerates();
99     void FixupFaceOrientation();
100     static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
101     IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
102     void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
103     void Swap(TempMesh& other);
104 };
105 
106 inline
IsEmpty()107 bool TempMesh::IsEmpty() const {
108     return mVerts.empty() && mVertcnt.empty();
109 }
110 
111 
112 // ------------------------------------------------------------------------------------------------
113 // Temporary representation of an opening in a wall or a floor
114 // ------------------------------------------------------------------------------------------------
115 struct TempOpening
116 {
117     const IFC::Schema_2x3::IfcSolidModel *solid;
118     IfcVector3 extrusionDir;
119 
120     std::shared_ptr<TempMesh> profileMesh;
121     std::shared_ptr<TempMesh> profileMesh2D;
122 
123     // list of points generated for this opening. This is used to
124     // create connections between two opposing holes created
125     // from a single opening instance (two because walls tend to
126     // have two sides). If !empty(), the other side of the wall
127     // has already been processed.
128     std::vector<IfcVector3> wallPoints;
129 
130     // ------------------------------------------------------------------------------
TempOpeningTempOpening131     TempOpening()
132         : solid()
133         , extrusionDir()
134         , profileMesh()
135     {
136     }
137 
138     // ------------------------------------------------------------------------------
TempOpeningTempOpening139     TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir,
140         std::shared_ptr<TempMesh> profileMesh,
141         std::shared_ptr<TempMesh> profileMesh2D)
142         : solid(solid)
143         , extrusionDir(extrusionDir)
144         , profileMesh(profileMesh)
145         , profileMesh2D(profileMesh2D)
146     {
147     }
148 
149     // ------------------------------------------------------------------------------
150     void Transform(const IfcMatrix4& mat); // defined later since TempMesh is not complete yet
151 
152 
153 
154     // ------------------------------------------------------------------------------
155     // Helper to sort openings by distance from a given base point
156     struct DistanceSorter {
157 
DistanceSorterTempOpening::DistanceSorter158         DistanceSorter(const IfcVector3& base) : base(base) {}
159 
operatorTempOpening::DistanceSorter160         bool operator () (const TempOpening& a, const TempOpening& b) const {
161             return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength();
162         }
163 
164         IfcVector3 base;
165     };
166 };
167 
168 
169 // ------------------------------------------------------------------------------------------------
170 // Intermediate data storage during conversion. Keeps everything and a bit more.
171 // ------------------------------------------------------------------------------------------------
172 struct ConversionData
173 {
ConversionDataConversionData174     ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings)
175         : len_scale(1.0)
176         , angle_scale(-1.0)
177         , db(db)
178         , proj(proj)
179         , out(out)
180         , settings(settings)
181         , apply_openings()
182         , collect_openings()
183     {}
184 
~ConversionDataConversionData185     ~ConversionData() {
186         std::for_each(meshes.begin(),meshes.end(),delete_fun<aiMesh>());
187         std::for_each(materials.begin(),materials.end(),delete_fun<aiMaterial>());
188     }
189 
190     IfcFloat len_scale, angle_scale;
191     bool plane_angle_in_radians;
192 
193     const STEP::DB& db;
194     const IFC::Schema_2x3::IfcProject& proj;
195     aiScene* out;
196 
197     IfcMatrix4 wcs;
198     std::vector<aiMesh*> meshes;
199     std::vector<aiMaterial*> materials;
200 
201     struct MeshCacheIndex {
202         const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex;
MeshCacheIndexConversionData::MeshCacheIndex203         MeshCacheIndex() : item(NULL), matindex(0) { }
MeshCacheIndexConversionData::MeshCacheIndex204         MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { }
205         bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; }
206         bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
207     };
208     typedef std::map<MeshCacheIndex, std::set<unsigned int> > MeshCache;
209     MeshCache cached_meshes;
210 
211     typedef std::map<const IFC::Schema_2x3::IfcSurfaceStyle*, unsigned int> MaterialCache;
212     MaterialCache cached_materials;
213 
214     const IFCImporter::Settings& settings;
215 
216     // Intermediate arrays used to resolve openings in walls: only one of them
217     // can be given at a time. apply_openings if present if the current element
218     // is a wall and needs its openings to be poured into its geometry while
219     // collect_openings is present only if the current element is an
220     // IfcOpeningElement, for which all the geometry needs to be preserved
221     // for later processing by a parent, which is a wall.
222     std::vector<TempOpening>* apply_openings;
223     std::vector<TempOpening>* collect_openings;
224 
225     std::set<uint64_t> already_processed;
226 };
227 
228 
229 // ------------------------------------------------------------------------------------------------
230 // Binary predicate to compare vectors with a given, quadratic epsilon.
231 // ------------------------------------------------------------------------------------------------
232 struct FuzzyVectorCompare {
233 
FuzzyVectorCompareFuzzyVectorCompare234     FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
operatorFuzzyVectorCompare235     bool operator()(const IfcVector3& a, const IfcVector3& b) {
236         return std::abs((a-b).SquareLength()) < epsilon;
237     }
238 
239     const IfcFloat epsilon;
240 };
241 
242 
243 // ------------------------------------------------------------------------------------------------
244 // Ordering predicate to totally order R^2 vectors first by x and then by y
245 // ------------------------------------------------------------------------------------------------
246 struct XYSorter {
247 
248     // sort first by X coordinates, then by Y coordinates
operatorXYSorter249     bool operator () (const IfcVector2&a, const IfcVector2& b) const {
250         if (a.x == b.x) {
251             return a.y < b.y;
252         }
253         return a.x < b.x;
254     }
255 };
256 
257 
258 
259 // conversion routines for common IFC entities, implemented in IFCUtil.cpp
260 void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in);
261 void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base);
262 void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in);
263 void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in);
264 void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in);
265 void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z);
266 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in);
267 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in);
268 void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& in);
269 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv);
270 void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op);
271 bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in);
272 IfcFloat ConvertSIPrefix(const std::string& prefix);
273 
274 
275 // IFCProfile.cpp
276 bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
277 bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
278 
279 // IFCMaterial.cpp
280 unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
281 
282 // IFCGeometry.cpp
283 IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
284 bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set<unsigned int>& mesh_indices, ConversionData& conv);
285 void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
286 
287 void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout,
288                            ConversionData& conv);
289 
290 void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result,
291                               ConversionData& conv, bool collect_openings);
292 
293 // IFCBoolean.cpp
294 
295 void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv);
296 void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result,
297                                        const TempMesh& first_operand,
298                                        ConversionData& conv);
299 
300 void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace* hs, TempMesh& result,
301                                                        const TempMesh& first_operand,
302                                                        ConversionData& conv);
303 void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid* as, TempMesh& result,
304                                                const TempMesh& first_operand,
305                                                ConversionData& conv);
306 
307 
308 // IFCOpenings.cpp
309 
310 bool GenerateOpenings(std::vector<TempOpening>& openings,
311                       const std::vector<IfcVector3>& nors,
312                       TempMesh& curmesh,
313                       bool check_intersection,
314                       bool generate_connection_geometry,
315                       const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0));
316 
317 
318 
319 // IFCCurve.cpp
320 
321 // ------------------------------------------------------------------------------------------------
322 // Custom exception for use by members of the Curve class
323 // ------------------------------------------------------------------------------------------------
324 class CurveError {
325 public:
CurveError(const std::string & s)326     CurveError(const std::string& s)
327     : mStr(s) {
328         // empty
329     }
330 
331     std::string mStr;
332 };
333 
334 // ------------------------------------------------------------------------------------------------
335 // Temporary representation for an arbitrary sub-class of IfcCurve. Used to sample the curves
336 // to obtain a list of line segments.
337 // ------------------------------------------------------------------------------------------------
338 class Curve {
339 protected:
Curve(const Schema_2x3::IfcCurve & base_entity,ConversionData & conv)340     Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv)
341     : base_entity(base_entity)
342     , conv(conv) {
343         // empty
344     }
345 
346 public:
347     typedef std::pair<IfcFloat, IfcFloat> ParamRange;
348 
~Curve()349     virtual ~Curve() {}
350 
351 
352     // check if a curve is closed
353     virtual bool IsClosed() const = 0;
354 
355     // evaluate the curve at the given parametric position
356     virtual IfcVector3 Eval(IfcFloat p) const = 0;
357 
358     // try to match a point on the curve to a given parameter
359     // for self-intersecting curves, the result is not ambiguous and
360     // it is undefined which parameter is returned.
361     virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const;
362 
363     // get the range of the curve (both inclusive).
364     // +inf and -inf are valid return values, the curve is not bounded in such a case.
365     virtual std::pair<IfcFloat,IfcFloat> GetParametricRange() const = 0;
366     IfcFloat GetParametricRangeDelta() const;
367 
368     // estimate the number of sample points that this curve will require
369     virtual size_t EstimateSampleCount(IfcFloat start,IfcFloat end) const;
370 
371     // intelligently sample the curve based on the current settings
372     // and append the result to the mesh
373     virtual void SampleDiscrete(TempMesh& out,IfcFloat start,IfcFloat end) const;
374 
375 #ifdef ASSIMP_BUILD_DEBUG
376     // check if a particular parameter value lies within the well-defined range
377     bool InRange(IfcFloat) const;
378 #endif
379     static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv);
380 
381 protected:
382     const Schema_2x3::IfcCurve& base_entity;
383     ConversionData& conv;
384 };
385 
386 
387 // --------------------------------------------------------------------------------
388 // A BoundedCurve always holds the invariant that GetParametricRange()
389 // never returns infinite values.
390 // --------------------------------------------------------------------------------
391 class BoundedCurve : public Curve {
392 public:
BoundedCurve(const Schema_2x3::IfcBoundedCurve & entity,ConversionData & conv)393     BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv)
394         : Curve(entity,conv)
395     {}
396 
397 public:
398 
399     bool IsClosed() const;
400 
401 public:
402 
403     // sample the entire curve
404     void SampleDiscrete(TempMesh& out) const;
405     using Curve::SampleDiscrete;
406 };
407 
408 // IfcProfile.cpp
409 bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
410 }
411 }
412 
413 #endif
414