1 /*
2 * OpenSCAD (www.openscad.org)
3 * Copyright (C) 2009-2016 Clifford Wolf <clifford@clifford.at> and
4 * Marius Kintel <marius@kintel.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * As a special exception, you have permission to link this program
12 * with the CGAL library and distribute executables, as long as you
13 * follow the requirements of the GNU GPL in regard to all of the
14 * software in the executable aside from CGAL.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27 #include "importnode.h"
28
29 #include "polyset.h"
30 #include "Geometry.h"
31 #include "printutils.h"
32 #include "version_helper.h"
33 #include "AST.h"
34 #include "boost-utils.h"
35
36 #ifdef ENABLE_LIB3MF
37 #ifndef LIB3MF_API_2
38 #include <Model/COM/NMR_DLLInterfaces.h>
39 #undef BOOL
40 using namespace NMR;
41
42 /*
43 * Provided here for reference in LibraryInfo.cc which can't include
44 * both Qt and lib3mf headers due to some conflicting definitions of
45 * windows types when compiling with MinGW.
46 */
get_lib3mf_version()47 const std::string get_lib3mf_version() {
48 DWORD major, minor, micro;
49 NMR::lib3mf_getinterfaceversion(&major, &minor, µ);
50
51 const OpenSCAD::library_version_number header_version{NMR_APIVERSION_INTERFACE_MAJOR, NMR_APIVERSION_INTERFACE_MINOR, NMR_APIVERSION_INTERFACE_MICRO};
52 const OpenSCAD::library_version_number runtime_version{major, minor, micro};
53 return OpenSCAD::get_version_string(header_version, runtime_version);
54 }
55
56 #ifdef ENABLE_CGAL
57 #include "cgalutils.h"
58 #endif
59
60 typedef std::list<std::shared_ptr<PolySet>> polysets_t;
61
import_3mf_error(PLib3MFModel * model=nullptr,PLib3MFModelResourceIterator * object_it=nullptr,PolySet * mesh=nullptr,PolySet * mesh2=nullptr)62 static Geometry * import_3mf_error(PLib3MFModel *model = nullptr, PLib3MFModelResourceIterator *object_it = nullptr, PolySet *mesh = nullptr, PolySet *mesh2 = nullptr)
63 {
64 if (model) {
65 lib3mf_release(model);
66 }
67 if (object_it) {
68 lib3mf_release(object_it);
69 }
70 if (mesh) {
71 delete mesh;
72 }
73 if (mesh2) {
74 delete mesh2;
75 }
76
77 return new PolySet(3);
78 }
79
import_3mf(const std::string & filename,const Location & loc)80 Geometry * import_3mf(const std::string &filename, const Location &loc)
81 {
82 DWORD interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
83 HRESULT result = lib3mf_getinterfaceversion(&interfaceVersionMajor, &interfaceVersionMinor, &interfaceVersionMicro);
84 if (result != LIB3MF_OK) {
85 LOG(message_group::Error,Location::NONE,"","Error reading 3MF library version");
86 return new PolySet(3);
87 }
88
89 if ((interfaceVersionMajor != NMR_APIVERSION_INTERFACE_MAJOR)) {
90 LOG(message_group::Error,Location::NONE,"","Invalid 3MF library major version %1$d.%2$d.%3$d, expected %4$d.%5$d.%6$d",
91 interfaceVersionMajor,interfaceVersionMinor,interfaceVersionMicro,
92 NMR_APIVERSION_INTERFACE_MAJOR,NMR_APIVERSION_INTERFACE_MINOR,NMR_APIVERSION_INTERFACE_MICRO);
93 return new PolySet(3);
94 }
95
96 PLib3MFModel *model;
97 result = lib3mf_createmodel(&model);
98 if (result != LIB3MF_OK) {
99 LOG(message_group::Error,Location::NONE,"","Could not create model: %1$08lx",result);
100 return import_3mf_error();
101 }
102
103 PLib3MFModelReader *reader;
104 result = lib3mf_model_queryreader(model, "3mf", &reader);
105 if (result != LIB3MF_OK) {
106 LOG(message_group::Error,Location::NONE,"","Could not create 3MF reader: %1$08lx",result);
107 return import_3mf_error(model);
108 }
109
110 result = lib3mf_reader_readfromfileutf8(reader, filename.c_str());
111 lib3mf_release(reader);
112 if (result != LIB3MF_OK) {
113 LOG(message_group::Warning,Location::NONE,"","Could not read file '%1$s', import() at line %2$d",filename.c_str(),loc.firstLine());
114 return import_3mf_error(model);
115 }
116
117 PLib3MFModelResourceIterator *object_it;
118 result = lib3mf_model_getmeshobjects(model, &object_it);
119 if (result != LIB3MF_OK) {
120 return import_3mf_error(model);
121 }
122
123 PolySet *first_mesh = 0;
124 polysets_t meshes;
125 unsigned int mesh_idx = 0;
126 while (true) {
127 int has_next;
128 result = lib3mf_resourceiterator_movenext(object_it, &has_next);
129 if (result != LIB3MF_OK) {
130 return import_3mf_error(model, object_it, first_mesh);
131 }
132 if (!has_next) {
133 break;
134 }
135
136 PLib3MFModelResource *object;
137 result = lib3mf_resourceiterator_getcurrent(object_it, &object);
138 if (result != LIB3MF_OK) {
139 return import_3mf_error(model, object_it, first_mesh);
140 }
141
142 DWORD vertex_count;
143 result = lib3mf_meshobject_getvertexcount(object, &vertex_count);
144 if (result != LIB3MF_OK) {
145 return import_3mf_error(model, object_it, first_mesh);
146 }
147 DWORD triangle_count;
148 result = lib3mf_meshobject_gettrianglecount(object, &triangle_count);
149 if (result != LIB3MF_OK) {
150 return import_3mf_error(model, object_it, first_mesh);
151 }
152
153 PRINTDB("%s: mesh %d, vertex count: %lu, triangle count: %lu", filename.c_str() % mesh_idx % vertex_count % triangle_count);
154
155 PolySet *p = new PolySet(3);
156 for (DWORD idx = 0; idx < triangle_count; ++idx) {
157 MODELMESHTRIANGLE triangle;
158 if (lib3mf_meshobject_gettriangle(object, idx, &triangle) != LIB3MF_OK) {
159 return import_3mf_error(model, object_it, first_mesh, p);
160 }
161
162 MODELMESHVERTEX vertex1, vertex2, vertex3;
163 if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[0], &vertex1) != LIB3MF_OK) {
164 return import_3mf_error(model, object_it, first_mesh, p);
165 }
166 if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[1], &vertex2) != LIB3MF_OK) {
167 return import_3mf_error(model, object_it, first_mesh, p);
168 }
169 if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[2], &vertex3) != LIB3MF_OK) {
170 return import_3mf_error(model, object_it, first_mesh, p);
171 }
172
173 p->append_poly();
174 p->append_vertex(vertex1.m_fPosition[0], vertex1.m_fPosition[1], vertex1.m_fPosition[2]);
175 p->append_vertex(vertex2.m_fPosition[0], vertex2.m_fPosition[1], vertex2.m_fPosition[2]);
176 p->append_vertex(vertex3.m_fPosition[0], vertex3.m_fPosition[1], vertex3.m_fPosition[2]);
177 }
178
179 if (first_mesh) {
180 meshes.push_back(std::shared_ptr<PolySet>(p));
181 } else {
182 first_mesh = p;
183 }
184 mesh_idx++;
185 }
186
187 lib3mf_release(object_it);
188 lib3mf_release(model);
189
190 if (first_mesh == 0) {
191 return new PolySet(3);
192 } else if (meshes.empty()) {
193 return first_mesh;
194 } else {
195 PolySet *p = new PolySet(3);
196 #ifdef ENABLE_CGAL
197 Geometry::Geometries children;
198 children.push_back(std::make_pair((const AbstractNode*)NULL, shared_ptr<const Geometry>(first_mesh)));
199 for (polysets_t::iterator it = meshes.begin(); it != meshes.end(); ++it) {
200 children.push_back(std::make_pair((const AbstractNode*)NULL, shared_ptr<const Geometry>(*it)));
201 }
202 CGAL_Nef_polyhedron *N = CGALUtils::applyUnion3D(children.begin(), children.end());
203
204 CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *p);
205 delete N;
206 #endif
207 return p;
208 }
209 }
210
211 #else // LIB3MF_API_2
212
213 #include "lib3mf_implicit.hpp"
214
215 /*
216 * Provided here for reference in LibraryInfo.cc which can't include
217 * both Qt and lib3mf headers due to some conflicting definitions of
218 * windows types when compiling with MinGW.
219 */
get_lib3mf_version()220 const std::string get_lib3mf_version() {
221 Lib3MF_uint32 interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
222 Lib3MF::PWrapper wrapper;
223
224 try {
225 wrapper = Lib3MF::CWrapper::loadLibrary();
226 wrapper->GetLibraryVersion(interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro);
227 } catch (Lib3MF::ELib3MFException &e) {
228 LOG(message_group::Export_Error,Location::NONE,"",e.what());
229 }
230
231 const OpenSCAD::library_version_number header_version{LIB3MF_VERSION_MAJOR, LIB3MF_VERSION_MINOR, LIB3MF_VERSION_MICRO};
232 const OpenSCAD::library_version_number runtime_version{interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro};
233 return OpenSCAD::get_version_string(header_version, runtime_version);
234 }
235
236 #ifdef ENABLE_CGAL
237 #include "cgalutils.h"
238 #endif
239
240 typedef std::list<std::shared_ptr<PolySet>> polysets_t;
241
import_3mf_error(PolySet * mesh=nullptr,PolySet * mesh2=nullptr)242 static Geometry * import_3mf_error(PolySet *mesh = nullptr, PolySet *mesh2 = nullptr)
243 {
244 if (mesh) {
245 delete mesh;
246 }
247 if (mesh2) {
248 delete mesh2;
249 }
250 return new PolySet(3);
251 }
252
import_3mf(const std::string & filename,const Location & loc)253 Geometry *import_3mf(const std::string &filename, const Location &loc)
254 {
255 Lib3MF_uint32 interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
256 Lib3MF::PWrapper wrapper;
257
258 try {
259 wrapper = Lib3MF::CWrapper::loadLibrary();
260 wrapper->GetLibraryVersion(interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro);
261 if (interfaceVersionMajor != LIB3MF_VERSION_MAJOR) {
262 LOG(message_group::Error,Location::NONE,"","Invalid 3MF library major version %1$d.%2$d.%3$d, expected %4$d.%5$d.%6$d",
263 interfaceVersionMajor,interfaceVersionMinor,interfaceVersionMicro,
264 LIB3MF_VERSION_MAJOR,LIB3MF_VERSION_MINOR,LIB3MF_VERSION_MICRO);
265 return new PolySet(3);
266 }
267 } catch (Lib3MF::ELib3MFException &e) {
268 LOG(message_group::Export_Error,Location::NONE,"",e.what());
269 return new PolySet(3);
270 }
271
272 Lib3MF::PModel model;
273 try {
274 model = wrapper->CreateModel();
275 if (!model) {
276 LOG(message_group::Error,Location::NONE,"","Could not create model");
277 return new PolySet(3);
278 }
279 } catch (Lib3MF::ELib3MFException &e) {
280 LOG(message_group::Export_Error,Location::NONE,"",e.what());
281 return new PolySet(3);
282 }
283
284 Lib3MF::PReader reader;
285 try {
286 reader = model->QueryReader("3mf");
287 if (!reader) {
288 LOG(message_group::Error,Location::NONE,"","Could not create 3MF reader");
289 return new PolySet(3);
290 }
291 } catch (Lib3MF::ELib3MFException &e) {
292 LOG(message_group::Export_Error,Location::NONE,"",e.what());
293 return new PolySet(3);
294 }
295
296 bool read_error = false;
297 try {
298 reader->ReadFromFile(filename);
299 } catch (Lib3MF::ELib3MFException &e) {
300 read_error = true;
301 }
302 if (!reader || read_error) {
303 LOG(message_group::Warning,Location::NONE,"","Could not read file '%1$s', import() at line %2$d",filename.c_str(),loc.firstLine());
304 return new PolySet(3);
305 }
306
307 Lib3MF::PMeshObjectIterator object_it;
308 object_it = model->GetMeshObjects();
309 if (!object_it) {
310 return new PolySet(3);
311 }
312
313 PolySet *first_mesh = 0;
314 polysets_t meshes;
315 unsigned int mesh_idx = 0;
316 bool has_next = object_it->MoveNext();
317 while (has_next) {
318 Lib3MF::PMeshObject object;
319 try {
320 object = object_it->GetCurrentMeshObject();
321 if (!object) {
322 return import_3mf_error(first_mesh);
323 }
324 } catch (Lib3MF::ELib3MFException &e) {
325 LOG(message_group::Error,Location::NONE,"",e.what());
326 return import_3mf_error(first_mesh);
327 }
328
329 Lib3MF_uint64 vertex_count = object->GetVertexCount();
330 if (!vertex_count) {
331 return import_3mf_error(first_mesh);
332 }
333 Lib3MF_uint64 triangle_count = object->GetTriangleCount();
334 if (!triangle_count) {
335 return import_3mf_error(first_mesh);
336 }
337
338 PRINTDB("%s: mesh %d, vertex count: %lu, triangle count: %lu", filename.c_str() % mesh_idx % vertex_count % triangle_count);
339
340 PolySet *p = new PolySet(3);
341 for (Lib3MF_uint64 idx = 0; idx < triangle_count; ++idx) {
342 Lib3MF::sTriangle triangle = object->GetTriangle(idx);
343 Lib3MF::sPosition vertex1, vertex2, vertex3;
344
345 vertex1 = object->GetVertex(triangle.m_Indices[0]);
346 vertex2 = object->GetVertex(triangle.m_Indices[1]);
347 vertex3 = object->GetVertex(triangle.m_Indices[2]);
348
349 p->append_poly();
350 p->append_vertex(vertex1.m_Coordinates[0], vertex1.m_Coordinates[1], vertex1.m_Coordinates[2]);
351 p->append_vertex(vertex2.m_Coordinates[0], vertex2.m_Coordinates[1], vertex2.m_Coordinates[2]);
352 p->append_vertex(vertex3.m_Coordinates[0], vertex3.m_Coordinates[1], vertex3.m_Coordinates[2]);
353 }
354
355 if (first_mesh) {
356 meshes.push_back(std::shared_ptr<PolySet>(p));
357 } else {
358 first_mesh = p;
359 }
360 mesh_idx++;
361 has_next = object_it->MoveNext();
362 }
363
364 if (first_mesh == 0) {
365 return new PolySet(3);
366 } else if (meshes.empty()) {
367 return first_mesh;
368 } else {
369 PolySet *p = new PolySet(3);
370 #ifdef ENABLE_CGAL
371 Geometry::Geometries children;
372 children.push_back(std::make_pair((const AbstractNode*)NULL, shared_ptr<const Geometry>(first_mesh)));
373 for (polysets_t::iterator it = meshes.begin(); it != meshes.end(); ++it) {
374 children.push_back(std::make_pair((const AbstractNode*)NULL, shared_ptr<const Geometry>(*it)));
375 }
376 CGAL_Nef_polyhedron *N = CGALUtils::applyUnion3D(children.begin(), children.end());
377
378 CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *p);
379 delete N;
380 #endif
381 return p;
382 }
383 }
384
385 #endif // LIB3MF_API_2
386
387 #else // ENABLE_LIB3MF
388
get_lib3mf_version()389 const std::string get_lib3mf_version() {
390 const std::string lib3mf_version = "(not enabled)";
391 return lib3mf_version;
392 }
393
import_3mf(const std::string &,const Location & loc)394 Geometry * import_3mf(const std::string &, const Location &loc)
395 {
396 LOG(message_group::Warning,Location::NONE,"","Import from 3MF format was not enabled when building the application, import() at line %1$d",loc.firstLine());
397 return new PolySet(3);
398 }
399
400 #endif // ENABLE_LIB3MF
401