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